FUSE: require valid file/dir handle; drop open_files fallback

Several FUSE ops fell back to looking up a FileInfo via
state.open_files when the kernel-provided fh was zero, returning
-ENOENT if the lookup failed. This path papered over handle-tracking
bugs and added cost on every call. Use the handle directly and
return -EBADF when it is invalid - the kernel should always hand us
a valid fh for an open resource.

Also simplify FileInfo::from_fh / DirInfo::from_fh and their to_fh
helpers: callers now handle the null-pointer case explicitly so the
ASSERT_NOT_NULL checks are redundant.

Covers fallocate, fchmod, fchown, fgetattr, flock, ftruncate,
futimens, read, release, releasedir, write, statx, ioctl (file +
dir), readdir (sequential, concurrent-on-read,
concurrent-on-search), and the passthrough fast paths in
create/open.
This commit is contained in:
Antonio S. Musumeci
2026-04-18 13:47:23 -05:00
committed by trapexit
parent 0dcca0f1a3
commit 98648ae44b
21 changed files with 74 additions and 143 deletions
+10 -9
View File
@@ -27,6 +27,7 @@
class DirInfo : public FH
{
public:
static u64 to_fh(const DirInfo*);
static DirInfo *from_fh(const u64 fh);
public:
@@ -39,23 +40,23 @@ public:
}
};
inline
u64
DirInfo::to_fh(const DirInfo *di_)
{
return reinterpret_cast<u64>(di_);
}
inline
u64
DirInfo::to_fh() const
{
ASSERT_NOT_NULL(this);
return reinterpret_cast<u64>(this);
return to_fh(this);
}
inline
DirInfo*
DirInfo::from_fh(const u64 fh_)
{
DirInfo *rv;
rv = reinterpret_cast<DirInfo*>(fh_);
ASSERT_NOT_NULL(rv);
return rv;
return reinterpret_cast<DirInfo*>(fh_);
}
+2 -10
View File
@@ -104,7 +104,6 @@ inline
u64
FileInfo::to_fh(const FileInfo *fi_)
{
ASSERT_NOT_NULL(fi_);
return reinterpret_cast<u64>(fi_);
}
@@ -112,19 +111,12 @@ inline
u64
FileInfo::to_fh() const
{
ASSERT_NOT_NULL(this);
return reinterpret_cast<u64>(this);
return to_fh(this);
}
inline
FileInfo*
FileInfo::from_fh(const u64 fh_)
{
FileInfo *rv;
rv = reinterpret_cast<FileInfo*>(fh_);
ASSERT_NOT_NULL(rv);
return rv;
return reinterpret_cast<FileInfo*>(fh_);
}
+3
View File
@@ -292,6 +292,9 @@ _create(const fuse_req_ctx_t *ctx_,
FileInfo *fi = FileInfo::from_fh(ffi_->fh);
int backing_id = INVALID_BACKING_ID;
if(not fi)
return -EBADF;
switch(_(cfg.passthrough_io,ffi_->flags))
{
case _(PassthroughIO::ENUM::RO,O_RDONLY):
+3 -15
View File
@@ -48,22 +48,10 @@ FUSE::fallocate(const fuse_req_ctx_t *ctx_,
off_t offset_,
off_t len_)
{
u64 fh;
FileInfo *fi = FileInfo::from_fh(fh_);
fh = fh_;
if(fh == 0)
{
state.open_files.cvisit(ctx_->nodeid,
[&](auto &val_)
{
fh = val_.second.fi->to_fh();
});
}
if(fh == 0)
return -ENOENT;
FileInfo *fi = FileInfo::from_fh(fh);
if(not fi)
return -EBADF;
return ::_fallocate(fi->fd,
mode_,
+3 -15
View File
@@ -43,22 +43,10 @@ FUSE::fchmod(const fuse_req_ctx_t *ctx_,
cu64 fh_,
const mode_t mode_)
{
u64 fh;
FileInfo *fi = FileInfo::from_fh(fh_);
fh = fh_;
if(fh == 0)
{
state.open_files.cvisit(ctx_->nodeid,
[&](auto &val_)
{
fh = val_.second.fi->to_fh();
});
}
if(fh == 0)
return -ENOENT;
FileInfo *fi = FileInfo::from_fh(fh);
if(not fi)
return -EBADF;
return ::_fchmod(fi->fd,mode_);
}
+3 -15
View File
@@ -47,22 +47,10 @@ FUSE::fchown(const fuse_req_ctx_t *ctx_,
const uid_t uid_,
const gid_t gid_)
{
u64 fh;
FileInfo *fi = FileInfo::from_fh(fh_);
fh = fh_;
if(fh == 0)
{
state.open_files.cvisit(ctx_->nodeid,
[&](auto &val_)
{
fh = val_.second.fi->to_fh();
});
}
if(fh == 0)
return -ENOENT;
FileInfo *fi = FileInfo::from_fh(fh);
if(not fi)
return -EBADF;
return ::_fchown(fi->fd,uid_,gid_);
}
+3 -15
View File
@@ -54,26 +54,14 @@ FUSE::fgetattr(const fuse_req_ctx_t *ctx_,
fuse_timeouts_t *timeout_)
{
int rv;
u64 fh;
FileInfo *fi = FileInfo::from_fh(fh_);
fh = fh_;
if(fh == 0)
{
state.open_files.cvisit(ctx_->nodeid,
[&](const auto &val_)
{
fh = val_.second.fi->to_fh();
});
}
if(fh == 0)
if(not fi)
{
timeout_->entry = cfg.cache_negative_entry;
return -ENOENT;
return -EBADF;
}
FileInfo *fi = FileInfo::from_fh(fh);
rv = ::_fgetattr(fi,st_);
timeout_->entry = ((rv >= 0) ?
+3 -15
View File
@@ -43,22 +43,10 @@ FUSE::flock(const fuse_req_ctx_t *ctx_,
const fuse_file_info_t *ffi_,
int op_)
{
u64 fh;
FileInfo* fi = FileInfo::from_fh(ffi_->fh);
fh = ffi_->fh;
if(fh == 0)
{
state.open_files.cvisit(ctx_->nodeid,
[&](const auto &val_)
{
fh = val_.second.fi->to_fh();
});
}
if(fh == 0)
return -ENOENT;
FileInfo* fi = FileInfo::from_fh(fh);
if(not fi)
return -EBADF;
return ::_flock(fi->fd,op_);
}
+3 -15
View File
@@ -43,22 +43,10 @@ FUSE::ftruncate(const fuse_req_ctx_t *ctx_,
cu64 fh_,
off_t size_)
{
u64 fh;
FileInfo *fi = FileInfo::from_fh(fh_);
fh = fh_;
if(fh == 0)
{
state.open_files.cvisit(ctx_->nodeid,
[&](auto &val_)
{
fh = val_.second.fi->to_fh();
});
}
if(fh == 0)
return -ENOENT;
FileInfo *fi = FileInfo::from_fh(fh);
if(not fi)
return -EBADF;
return ::_ftruncate(fi->fd,size_);
}
+3 -15
View File
@@ -45,22 +45,10 @@ FUSE::futimens(const fuse_req_ctx_t *ctx_,
cu64 fh_,
const struct timespec ts_[2])
{
u64 fh;
FileInfo *fi = FileInfo::from_fh(fh_);
fh = fh_;
if(fh == 0)
{
state.open_files.cvisit(ctx_->nodeid,
[&](const auto &val_)
{
fh = val_.second.fi->to_fh();
});
}
if(fh == 0)
return -ENOENT;
FileInfo *fi = FileInfo::from_fh(fh);
if(not fi)
return -EBADF;
return ::_futimens(fi->fd,ts_);
}
+6
View File
@@ -115,6 +115,9 @@ _ioctl_file(const fuse_req_ctx_t *ctx_,
{
FileInfo *fi = FileInfo::from_fh(ffi_->fh);
if(not fi)
return -EBADF;
return ::_ioctl(fi->fd,cmd_,data_,out_bufsz_);
}
@@ -163,6 +166,9 @@ _ioctl_dir(const fuse_req_ctx_t *ctx_,
{
DirInfo *di = DirInfo::from_fh(ffi_->fh);
if(not di)
return -EBADF;
return ::_ioctl_dir_base(cfg.func.open.policy,
cfg.branches,
di->fusepath.c_str(),
+2
View File
@@ -423,6 +423,8 @@ _open(const fuse_req_ctx_t *ctx_,
return rv;
fi = FileInfo::from_fh(ffi_->fh);
if(not fi)
return -EBADF;
switch(_(cfg.passthrough_io,ffi_->flags))
{
+3
View File
@@ -67,6 +67,9 @@ FUSE::read(const fuse_req_ctx_t *ctx_,
ioprio::SetFrom iop(ctx_->pid);
FileInfo *fi = FileInfo::from_fh(ffi_->fh);
if(not fi)
return -EBADF;
if(fi->direct_io)
return ::_read_direct_io(fi->fd,buf_,size_,offset_);
+3
View File
@@ -84,6 +84,9 @@ _handle_ENOENT(const fuse_file_info_t *ffi_,
dirent de;
DirInfo *di = DirInfo::from_fh(ffi_->fh);
if(not di)
return -EBADF;
if(!di->fusepath.empty())
return -ENOENT;
+3
View File
@@ -89,6 +89,9 @@ FUSE::ReadDirCOR::operator()(const fuse_req_ctx_t *ctx_,
{
DirInfo *di = DirInfo::from_fh(ffi_->fh);
if(not di)
return -EBADF;
return ::_concurrent_readdir(_tp,
cfg.branches,
di->fusepath,
+3
View File
@@ -67,6 +67,9 @@ FUSE::ReadDirCOSR::operator()(const fuse_req_ctx_t *ctx_,
{
DirInfo *di = DirInfo::from_fh(ffi_->fh);
if(not di)
return -EBADF;
return ::_readdir(_tp,
cfg.branches,
di->fusepath,
+3
View File
@@ -36,6 +36,9 @@ FUSE::ReadDirSeq::operator()(const fuse_req_ctx_t *ctx_,
{
DirInfo *di = DirInfo::from_fh(ffi_->fh);
if(not di)
return -EBADF;
return ::_readdir(cfg.branches,
di->fusepath,
dirents_);
+3
View File
@@ -119,6 +119,9 @@ FUSE::release(const fuse_req_ctx_t *ctx_,
{
FileInfo *fi = FileInfo::from_fh(ffi_->fh);
if(not fi)
return -EBADF;
return ::_release(ctx_->nodeid,
fi,
cfg.dropcacheonclose);
+3
View File
@@ -39,5 +39,8 @@ FUSE::releasedir(const fuse_req_ctx_t *ctx_,
{
DirInfo *di = DirInfo::from_fh(ffi_->fh);
if(not di)
return -EBADF;
return ::_releasedir(di);
}
+7 -19
View File
@@ -253,27 +253,15 @@ FUSE::statx_fh(const fuse_req_ctx_t *ctx_,
fuse_timeouts_t *timeout_)
{
int rv;
uint64_t fh;
FileInfo *fi = FileInfo::from_fh(fh_);
fh = fh_;
if(fh == 0)
{
state.open_files.cvisit(ctx_->nodeid,
[&](auto &val_)
{
fh = val_.second.fi->to_fh();
});
}
if(not fi)
return -EBADF;
if(fh == 0)
return -ENOENT;
FileInfo *fi = FileInfo::from_fh(fh);
rv = ::_statx(fi,
flags_|AT_STATX_DONT_SYNC,
mask_,
st_);
rv = ::_statx(fi,
flags_|AT_STATX_DONT_SYNC,
mask_,
st_);
timeout_->entry = ((rv >= 0) ?
cfg.cache_entry :
+2
View File
@@ -168,6 +168,8 @@ _write(const fuse_file_info_t *ffi_,
FileInfo *fi;
fi = FileInfo::from_fh(ffi_->fh);
if(not fi)
return -EBADF;
// Concurrent writes can only happen if:
// 1) writeback-cache is enabled and using page caching