FUSE: pass Branches::Ptr by value with empty-branches guards

Previously most FUSE ops took a reference to the live cfg.branches
object. With atomic_load now producing a shared_ptr snapshot, pass
it by value: the op works against a single stable view of the
configuration for its whole lifetime, immune to concurrent
reconfiguration.

Also bail out with -ENOENT when the policy returns an empty branch
set rather than dereferencing branches[0]. This was reachable if a
policy rejected every branch (e.g. everything readonly while
looking for a writable target).

Covers access, chmod, chown, create, getattr, getxattr, ioctl,
link, listxattr, mkdir, mknod, open, readdir (cor/cosr),
readlink, removexattr, rename, rmdir, setxattr, statfs, statx,
symlink, truncate, unlink, utimens.

While here, in _getattr only dereference st_->st_mode when lstat
succeeded (it is otherwise uninitialized).
This commit is contained in:
Antonio S. Musumeci 2026-04-18 00:56:15 -04:00 • committed by trapexit
parent 98648ae44b
commit 0c17492c78
25 changed files with 110 additions and 45 deletions

View file

@ -63,7 +63,7 @@ static
int
_removexattr(const Policy::Action &actionFunc_,
const Policy::Search &searchFunc_,
const Branches &ibranches_,
const Branches::Ptr ibranches_,
const fs::path &fusepath_,
const char *attrname_)
{
@ -74,6 +74,8 @@ _removexattr(const Policy::Action &actionFunc_,
rv = actionFunc_(ibranches_,fusepath_,obranches);
if(rv < 0)
return rv;
if(obranches.empty())
return -ENOENT;
::_removexattr_loop(obranches,fusepath_,attrname_,&prv);
if(prv.errors.empty())
@ -85,6 +87,8 @@ _removexattr(const Policy::Action &actionFunc_,
rv = searchFunc_(ibranches_,fusepath_,obranches);
if(rv < 0)
return rv;
if(obranches.empty())
return -ENOENT;
return prv.get_error(obranches[0]->path);
}