When file unlinked check open_files map

Inadvertantly removed. Didn't realize that the kernel would not always
send file handle when unlinked.
This commit is contained in:
Antonio SJ Musumeci 2026-04-28 09:07:27 -05:00 • committed by trapexit
parent 7616d8b1ac
commit 9308a3729c
16 changed files with 140 additions and 36 deletions

View file

@ -21,6 +21,7 @@
#include "errno.hpp"
#include "fileinfo.hpp"
#include "fs_copy_file_range.hpp"
#include "state.hpp"
#include "fuse.h"
@ -57,11 +58,14 @@ FUSE::copy_file_range(const fuse_req_ctx_t *ctx_,
const size_t size_,
const unsigned int flags_)
{
FileInfo *src_fi = FileInfo::from_fh(src_ffi_->fh);
FileInfo *dst_fi = FileInfo::from_fh(dst_ffi_->fh);
FileInfo *src_fi;
FileInfo *dst_fi;
src_fi = state.get_fi(ctx_,src_ffi_->fh);
if(not src_fi)
return -EBADF;
dst_fi = state.get_fi(ctx_,dst_ffi_->fh);
if(not dst_fi)
return -EBADF;

View file

@ -41,6 +41,13 @@ _fallocate(const int fd_,
return rv;
}
// In cases where the node was opened but unlinked there will be no
// path and no guarentee of a `fh`. It could always do the lookup but
// why bother if the kernel has provided it to us? The reason the
// function doesn't need to run within the visit lambda is because
// since the request is outstanding the kernel won't be releasing the
// node and therefore the entry will be valid over the lifetime of
// this function.
int
FUSE::fallocate(const fuse_req_ctx_t *ctx_,
cu64 fh_,
@ -48,8 +55,9 @@ FUSE::fallocate(const fuse_req_ctx_t *ctx_,
off_t offset_,
off_t len_)
{
FileInfo *fi = FileInfo::from_fh(fh_);
FileInfo *fi;
fi = state.get_fi(ctx_,fh_);
if(not fi)
return -EBADF;

View file

@ -38,13 +38,21 @@ _fchmod(const int fd_,
return rv;
}
// In cases where the node was opened but unlinked there will be no
// path and no guarentee of a `fh`. It could always do the lookup but
// why bother if the kernel has provided it to us? The reason the
// function doesn't need to run within the visit lambda is because
// since the request is outstanding the kernel won't be releasing the
// node and therefore the entry will be valid over the lifetime of
// this function.
int
FUSE::fchmod(const fuse_req_ctx_t *ctx_,
cu64 fh_,
const mode_t mode_)
{
FileInfo *fi = FileInfo::from_fh(fh_);
FileInfo *fi;
fi = state.get_fi(ctx_,fh_);
if(not fi)
return -EBADF;

View file

@ -41,14 +41,22 @@ _fchown(const int fd_,
return rv;
}
// In cases where the node was opened but unlinked there will be no
// path and no guarentee of a `fh`. It could always do the lookup but
// why bother if the kernel has provided it to us? The reason the
// function doesn't need to run within the visit lambda is because
// since the request is outstanding the kernel won't be releasing the
// node and therefore the entry will be valid over the lifetime of
// this function.
int
FUSE::fchown(const fuse_req_ctx_t *ctx_,
cu64 fh_,
const uid_t uid_,
const gid_t gid_)
{
FileInfo *fi = FileInfo::from_fh(fh_);
FileInfo *fi;
fi = state.get_fi(ctx_,fh_);
if(not fi)
return -EBADF;

View file

@ -46,7 +46,13 @@ _fgetattr(const FileInfo *fi_,
return rv;
}
// In cases where the node was opened but unlinked there will be no
// path and no guarentee of a `fh`. It could always do the lookup but
// why bother if the kernel has provided it to us? The reason the
// function doesn't need to run within the visit lambda is because
// since the request is outstanding the kernel won't be releasing the
// node and therefore the entry will be valid over the lifetime of
// this function.
int
FUSE::fgetattr(const fuse_req_ctx_t *ctx_,
cu64 fh_,
@ -54,13 +60,11 @@ FUSE::fgetattr(const fuse_req_ctx_t *ctx_,
fuse_timeouts_t *timeout_)
{
int rv;
FileInfo *fi = FileInfo::from_fh(fh_);
FileInfo *fi;
fi = state.get_fi(ctx_,fh_);
if(not fi)
{
timeout_->entry = cfg.cache_negative_entry;
return -EBADF;
}
return -EBADF;
rv = ::_fgetattr(fi,st_);

View file

@ -38,13 +38,21 @@ _flock(const int fd_,
return rv;
}
// In cases where the node was opened but unlinked there will be no
// path and no guarentee of a `fh`. It could always do the lookup but
// why bother if the kernel has provided it to us? The reason the
// function doesn't need to run within the visit lambda is because
// since the request is outstanding the kernel won't be releasing the
// node and therefore the entry will be valid over the lifetime of
// this function.
int
FUSE::flock(const fuse_req_ctx_t *ctx_,
const fuse_file_info_t *ffi_,
int op_)
{
FileInfo* fi = FileInfo::from_fh(ffi_->fh);
FileInfo *fi;
fi = state.get_fi(ctx_,ffi_->fh);
if(not fi)
return -EBADF;

View file

@ -22,6 +22,7 @@
#include "fileinfo.hpp"
#include "fs_close.hpp"
#include "fs_dup.hpp"
#include "state.hpp"
#include "fuse.h"
@ -39,12 +40,20 @@ _flush(const int fd_)
return fs::close(rv);
}
// In cases where the node was opened but unlinked there will be no
// path and no guarentee of a `fh`. It could always do the lookup but
// why bother if the kernel has provided it to us? The reason the
// function doesn't need to run within the visit lambda is because
// since the request is outstanding the kernel won't be releasing the
// node and therefore the entry will be valid over the lifetime of
// this function.
int
FUSE::flush(const fuse_req_ctx_t *ctx_,
const fuse_file_info_t *ffi_)
{
FileInfo *fi = FileInfo::from_fh(ffi_->fh);
FileInfo *fi;
fi = state.get_fi(ctx_,ffi_->fh);
if(not fi)
return -EBADF;

View file

@ -22,6 +22,7 @@
#include "fileinfo.hpp"
#include "fs_fdatasync.hpp"
#include "fs_fsync.hpp"
#include "state.hpp"
#include "to_neg_errno.hpp"
#include "fuse.h"
@ -44,13 +45,21 @@ _fsync(const int fd_,
return ::to_neg_errno(rv);
}
// In cases where the node was opened but unlinked there will be no
// path and no guarentee of a `fh`. It could always do the lookup but
// why bother if the kernel has provided it to us? The reason the
// function doesn't need to run within the visit lambda is because
// since the request is outstanding the kernel won't be releasing the
// node and therefore the entry will be valid over the lifetime of
// this function.
int
FUSE::fsync(const fuse_req_ctx_t *ctx_,
cu64 fh_,
int isdatasync_)
cu64 fh_,
int isdatasync_)
{
FileInfo *fi = FileInfo::from_fh(fh_);
FileInfo *fi;
fi = state.get_fi(ctx_,fh_);
if(not fi)
return -EBADF;

View file

@ -38,13 +38,21 @@ _ftruncate(const int fd_,
return rv;
}
// In cases where the node was opened but unlinked there will be no
// path and no guarentee of a `fh`. It could always do the lookup but
// why bother if the kernel has provided it to us? The reason the
// function doesn't need to run within the visit lambda is because
// since the request is outstanding the kernel won't be releasing the
// node and therefore the entry will be valid over the lifetime of
// this function.
int
FUSE::ftruncate(const fuse_req_ctx_t *ctx_,
cu64 fh_,
off_t size_)
{
FileInfo *fi = FileInfo::from_fh(fh_);
FileInfo *fi;
fi = state.get_fi(ctx_,fh_);
if(not fi)
return -EBADF;

View file

@ -40,13 +40,21 @@ _futimens(const int fd_,
return rv;
}
// In cases where the node was opened but unlinked there will be no
// path and no guarentee of a `fh`. It could always do the lookup but
// why bother if the kernel has provided it to us? The reason the
// function doesn't need to run within the visit lambda is because
// since the request is outstanding the kernel won't be releasing the
// node and therefore the entry will be valid over the lifetime of
// this function.
int
FUSE::futimens(const fuse_req_ctx_t *ctx_,
cu64 fh_,
const struct timespec ts_[2])
{
FileInfo *fi = FileInfo::from_fh(fh_);
FileInfo *fi;
fi = state.get_fi(ctx_,fh_);
if(not fi)
return -EBADF;

View file

@ -114,8 +114,9 @@ _ioctl_file(const fuse_req_ctx_t *ctx_,
void *data_,
u32 *out_bufsz_)
{
FileInfo *fi = FileInfo::from_fh(ffi_->fh);
FileInfo *fi;
fi = state.get_fi(ctx_,ffi_->fh);
if(not fi)
return -EBADF;

View file

@ -22,6 +22,7 @@
#include "fileinfo.hpp"
#include "fs_pread.hpp"
#include "ioprio.hpp"
#include "state.hpp"
#include "fuse.h"
@ -65,8 +66,9 @@ FUSE::read(const fuse_req_ctx_t *ctx_,
off_t offset_)
{
ioprio::SetFrom iop(ctx_->pid);
FileInfo *fi = FileInfo::from_fh(ffi_->fh);
FileInfo *fi;
fi = state.get_fi(ctx_,ffi_->fh);
if(not fi)
return -EBADF;

View file

@ -263,8 +263,9 @@ FUSE::statx_fh(const fuse_req_ctx_t *ctx_,
fuse_timeouts_t *timeout_)
{
int rv;
FileInfo *fi = FileInfo::from_fh(fh_);
FileInfo *fi;
fi = state.get_fi(ctx_,fh_);
if(not fi)
return -EBADF;

View file

@ -27,6 +27,7 @@
#include "fs_pwrite.hpp"
#include "fs_pwriten.hpp"
#include "ioprio.hpp"
#include "state.hpp"
#include "scope_guard/scope_guard.hpp"
@ -160,14 +161,15 @@ _write_cached(const char *buf_,
static
int
_write(const fuse_file_info_t *ffi_,
_write(const fuse_req_ctx_t *ctx_,
const fuse_file_info_t *ffi_,
const char *buf_,
const size_t count_,
const off_t offset_)
{
FileInfo *fi;
fi = FileInfo::from_fh(ffi_->fh);
fi = state.get_fi(ctx_,ffi_->fh);
if(not fi)
return -EBADF;
@ -180,8 +182,7 @@ _write(const fuse_file_info_t *ffi_,
// could change the move file behavior to use a known target file
// and have threads use O_EXCL and back off and wait for the
// transfer to complete before retrying.
mutex_lock(fi->mutex);
DEFER { mutex_unlock(fi->mutex); };
mutex_lockguard(fi->mutex);
if(fi->direct_io)
return ::_write_direct_io(buf_,count_,offset_,fi);
@ -198,7 +199,7 @@ FUSE::write(const fuse_req_ctx_t *ctx_,
{
ioprio::SetFrom iop(ctx_->pid);
return ::_write(ffi_,buf_,count_,offset_);
return ::_write(ctx_,ffi_,buf_,count_,offset_);
}
int

View file

@ -115,6 +115,24 @@ public:
FileInfo *fi;
};
public:
FileInfo*
get_fi(const fuse_req_ctx_t *ctx_,
cu64 fh_) const
{
FileInfo *fi;
fi = FileInfo::from_fh(fh_);
if(not fi)
open_files.cvisit(ctx_->nodeid,
[&](auto &v_)
{
fi = v_.second.fi;
});
return fi;
}
public:
struct GetSet
{

View file

@ -1498,10 +1498,8 @@ fuse_lib_getattr(fuse_req_t *req_,
{
if(fusepath != NULL)
err = f.ops.getattr(&req_->ctx,&fusepath[1],&buf,&timeouts);
else if(fh != 0)
err = f.ops.fgetattr(&req_->ctx,fh,&buf,&timeouts);
else
err = -ENOENT;
err = f.ops.fgetattr(&req_->ctx,fh,&buf,&timeouts);
free_path(hdr_->nodeid,fusepath);
}
@ -1538,20 +1536,29 @@ fuse_lib_statx_path(fuse_req_t *req_,
struct fuse_statx st{};
fuse_timeouts_t timeouts{};
// TODO: if node unlinked... but FUSE may not send it
err = get_path(hdr_->nodeid,&fusepath);
if(err == -ESTALE)
err = 0;
if(err)
{
fuse_reply_err(req_,err);
return;
}
err = f.ops.statx(&req_->ctx,
&fusepath[1],
inarg_->sx_flags,
inarg_->sx_mask,
&st,
&timeouts);
if(fusepath != NULL)
err = f.ops.statx(&req_->ctx,
&fusepath[1],
inarg_->sx_flags,
inarg_->sx_mask,
&st,
&timeouts);
else
err = f.ops.statx_fh(&req_->ctx,
0,
inarg_->sx_flags,
inarg_->sx_mask,
&st,
&timeouts);
free_path(hdr_->nodeid,fusepath);