Misc cleanup

This commit is contained in:
Antonio SJ Musumeci 2026-04-13 18:55:56 -05:00
parent ff48ff6047
commit fa06b4b8bd
74 changed files with 809 additions and 619 deletions

View file

@ -34,7 +34,7 @@
#include <fnmatch.h>
Branches::Impl::Impl(const u64 &default_minfreespace_)
Branches::Impl::Impl(const u64 *default_minfreespace_)
: _default_minfreespace(default_minfreespace_)
{
}
@ -48,6 +48,12 @@ Branches::Impl::operator=(Branches::Impl &rval_)
*this_base = *rval_base;
for(auto &branch : *this)
{
if(std::holds_alternative<const u64*>(branch._minfreespace))
branch._minfreespace = _default_minfreespace;
}
return *this;
}
@ -60,6 +66,12 @@ Branches::Impl::operator=(Branches::Impl &&rval_)
*this_base = std::move(*rval_base);
for(auto &branch : *this)
{
if(std::holds_alternative<const u64*>(branch._minfreespace))
branch._minfreespace = _default_minfreespace;
}
return *this;
}
@ -67,7 +79,7 @@ const
u64&
Branches::Impl::minfreespace(void) const
{
return _default_minfreespace;
return *_default_minfreespace;
}
namespace l
@ -148,7 +160,7 @@ namespace l
case 2:
*glob_ = v[0];
options = v[1];
v = str::split(options,',');
v = str::split(options,',');
switch(v.size())
{
case 2:
@ -229,7 +241,7 @@ namespace l
{
int rv;
StrVec paths;
Branches::Impl tmp_branches(branches_->minfreespace());
Branches::Impl tmp_branches(&branches_->minfreespace());
paths = str::split(str_,':');
for(auto &path : paths)
@ -251,7 +263,7 @@ namespace l
{
int rv;
std::vector<std::string> paths;
Branches::Impl tmp_branches(branches_->minfreespace());
Branches::Impl tmp_branches(&branches_->minfreespace());
paths = str::split(str_,':');
for(auto &path : paths)
@ -275,7 +287,7 @@ namespace l
{
int rv;
StrVec paths;
Branches::Impl tmp_branches(branches_->minfreespace());
Branches::Impl tmp_branches(&branches_->minfreespace());
paths = str::split(str_,':');
for(auto &path : paths)
@ -419,7 +431,7 @@ Branches::from_string(const std::string_view str_)
impl = std::atomic_load(&_impl);
new_impl = std::make_shared<Branches::Impl>(impl->minfreespace());
new_impl = std::make_shared<Branches::Impl>(&minfreespace);
*new_impl = *impl;
rv = new_impl->from_string(str_);
@ -440,7 +452,16 @@ Branches::to_string(void) const
void
Branches::find_and_set_mode_ro()
{
for(auto &branch : *_impl)
Branches::Ptr impl;
Branches::Ptr new_impl;
impl = std::atomic_load(&_impl);
new_impl = std::make_shared<Branches::Impl>(&minfreespace);
*new_impl = *impl;
bool changed = false;
for(auto &branch : *new_impl)
{
if(branch.mode != Branch::Mode::RW)
continue;
@ -452,7 +473,13 @@ Branches::find_and_set_mode_ro()
branch.path.string());
branch.mode = Branch::Mode::RO;
changed = true;
}
if(not changed)
return;
std::atomic_store(&_impl,new_impl);
}
SrcMounts::SrcMounts(Branches &b_)

View file

@ -39,13 +39,13 @@ public:
public std::vector<Branch>
{
private:
const u64 &_default_minfreespace;
const u64 *_default_minfreespace;
public:
using Ptr = std::shared_ptr<Impl>;
public:
Impl(const u64 &default_minfreespace);
Impl(const u64 *default_minfreespace);
public:
int from_string(const std::string_view str) final;
@ -73,7 +73,7 @@ private:
public:
Branches()
: _impl(std::make_shared<Impl>(minfreespace))
: _impl(std::make_shared<Impl>(&minfreespace))
{
}

View file

@ -47,7 +47,6 @@
#include "fuse_cfg.hpp"
#include "fuse_readdir.hpp"
#include "policy.hpp"
#include "rwlock.hpp"
#include "tofrom_ref.hpp"
#include "tofrom_wrapper.hpp"
#include "syslog.hpp"

View file

@ -26,7 +26,8 @@
LogFile::LogFile(const std::string &s_)
{
fuse_debug_set_output(s_);
if(!s_.empty())
fuse_debug_set_output(s_);
}
std::string

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_);
}

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_);
}

View file

@ -82,6 +82,8 @@ _movefile_and_open(const Policy::Create &createFunc_,
rv = createFunc_(branches_,fusepath_,dst_branch);
if(rv < 0)
return rv;
if(dst_branch.empty())
return -ENOSPC;
origfd_flags = fs::getfl(origfd_);
if(origfd_flags < 0)

View file

@ -30,7 +30,7 @@
static
int
_access(const Policy::Search &searchFunc_,
const Branches &branches_,
const Branches::Ptr branches_,
const fs::path &fusepath_,
const int mask_)
{
@ -42,6 +42,8 @@ _access(const Policy::Search &searchFunc_,
rv = searchFunc_(branches_,fusepath_,branches);
if(rv < 0)
return rv;
if(branches.empty())
return -ENOENT;
fullpath = branches[0]->path / fusepath_;

View file

@ -64,7 +64,7 @@ static
int
_chmod(const Policy::Action &actionFunc_,
const Policy::Search &searchFunc_,
const Branches &branches_,
const Branches::Ptr branches_,
const fs::path &fusepath_,
const mode_t mode_)
{
@ -75,6 +75,8 @@ _chmod(const Policy::Action &actionFunc_,
rv = actionFunc_(branches_,fusepath_,branches);
if(rv < 0)
return rv;
if(branches.empty())
return -ENOENT;
::_chmod_loop(branches,fusepath_,mode_,&prv);
if(prv.errors.empty())
@ -86,6 +88,8 @@ _chmod(const Policy::Action &actionFunc_,
rv = searchFunc_(branches_,fusepath_,branches);
if(rv < 0)
return rv;
if(branches.empty())
return -ENOENT;
return prv.get_error(branches[0]->path);
}

View file

@ -66,7 +66,7 @@ static
int
_chown(const Policy::Action &actionFunc_,
const Policy::Search &searchFunc_,
const Branches &branches_,
const Branches::Ptr branches_,
const fs::path &fusepath_,
const uid_t uid_,
const gid_t gid_)
@ -78,6 +78,8 @@ _chown(const Policy::Action &actionFunc_,
rv = actionFunc_(branches_,fusepath_,branches);
if(rv < 0)
return rv;
if(branches.empty())
return -ENOENT;
::_chown_loop(branches,fusepath_,uid_,gid_,&prv);
@ -90,6 +92,8 @@ _chown(const Policy::Action &actionFunc_,
rv = searchFunc_(branches_,fusepath_,branches);
if(rv < 0)
return rv;
if(branches.empty())
return -ENOENT;
return prv.get_error(branches[0]->path);
}

View file

@ -207,7 +207,7 @@ int
_create(const ugid_t ugid_,
const Policy::Search &searchFunc_,
const Policy::Create &createFunc_,
const Branches &branches_,
const Branches::Ptr branches_,
const fs::path &fusepath_,
fuse_file_info_t *ffi_,
const mode_t mode_,
@ -224,10 +224,14 @@ _create(const ugid_t ugid_,
rv = searchFunc_(branches_,fusedirpath,existingpaths);
if(rv < 0)
return rv;
if(existingpaths.empty())
return -ENOENT;
rv = createFunc_(branches_,fusedirpath,createpaths);
if(rv < 0)
return rv;
if(createpaths.empty())
return -ENOENT;
rv = fs::clonepath(existingpaths[0]->path,
createpaths[0]->path,
@ -292,6 +296,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):

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_,

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_);
}

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_);
}

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) ?

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_);
}

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_);
}

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_);
}

View file

@ -73,7 +73,8 @@ _set_stat_if_leads_to_reg(const fs::path &path_,
static
int
_getattr_fake_root(struct stat *st_)
_getattr_fake_root(struct stat *st_,
fuse_timeouts_t *timeout_)
{
st_->st_dev = 0;
st_->st_ino = 0;
@ -89,12 +90,16 @@ _getattr_fake_root(struct stat *st_)
st_->st_mtime = 0;
st_->st_ctime = 0;
timeout_->entry = 0;
timeout_->attr = 0;
return 0;
}
static
int
_getattr_controlfile(struct stat *st_)
_getattr_controlfile(struct stat *st_,
fuse_timeouts_t *timeout_)
{
static const uid_t uid = ::getuid();
static const gid_t gid = ::getgid();
@ -114,13 +119,16 @@ _getattr_controlfile(struct stat *st_)
st_->st_mtime = now;
st_->st_ctime = now;
timeout_->entry = cfg.cache_entry;
timeout_->attr = cfg.cache_attr;
return 0;
}
static
int
_getattr(const Policy::Search &searchFunc_,
const Branches &branches_,
const Branches::Ptr branches_,
const fs::path &fusepath_,
struct stat *st_,
const bool symlinkify_,
@ -134,6 +142,8 @@ _getattr(const Policy::Search &searchFunc_,
rv = searchFunc_(branches_,fusepath_,branches);
if(rv < 0)
return rv;
if(branches.empty())
return -ENOENT;
fullpath = branches[0]->path / fusepath_;
@ -144,12 +154,12 @@ _getattr(const Policy::Search &searchFunc_,
break;
case FollowSymlinks::ENUM::DIRECTORY:
rv = fs::lstat(fullpath,st_);
if(S_ISLNK(st_->st_mode))
if((rv >= 0) && S_ISLNK(st_->st_mode))
::_set_stat_if_leads_to_dir(fullpath,st_);
break;
case FollowSymlinks::ENUM::REGULAR:
rv = fs::lstat(fullpath,st_);
if(S_ISLNK(st_->st_mode))
if((rv >= 0) && S_ISLNK(st_->st_mode))
::_set_stat_if_leads_to_reg(fullpath,st_);
break;
case FollowSymlinks::ENUM::ALL:
@ -187,7 +197,7 @@ _getattr(const fs::path &fusepath_,
cfg.symlinkify_timeout,
cfg.follow_symlinks);
if((rv < 0) && Config::is_rootdir(fusepath_))
return ::_getattr_fake_root(st_);
return ::_getattr_fake_root(st_,timeout_);
timeout_->entry = ((rv >= 0) ?
cfg.cache_entry :
@ -223,7 +233,7 @@ FUSE::getattr(const fs::path &fusepath_,
fuse_timeouts_t *timeout_)
{
if(Config::is_ctrl_file(fusepath_))
return ::_getattr_controlfile(st_);
return ::_getattr_controlfile(st_,timeout_);
return ::_getattr(fusepath_,st_,timeout_);
}

View file

@ -97,7 +97,7 @@ _getxattr_from_string(char *destbuf_,
static
int
_getxattr_user_mergerfs_allpaths(const Branches::Ptr &branches_,
_getxattr_user_mergerfs_allpaths(const Branches::Ptr branches_,
const fs::path &fusepath_,
char *buf_,
const size_t count_)
@ -120,7 +120,7 @@ int
_getxattr_user_mergerfs(const fs::path &basepath_,
const fs::path &fusepath_,
const fs::path &fullpath_,
const Branches &branches_,
const Branches::Ptr branches_,
const char *attrname_,
char *buf_,
const size_t count_)
@ -144,7 +144,7 @@ _getxattr_user_mergerfs(const fs::path &basepath_,
static
int
_getxattr(const Policy::Search &searchFunc_,
const Branches &branches_,
const Branches::Ptr branches_,
const fs::path &fusepath_,
const char *attrname_,
char *buf_,
@ -157,6 +157,8 @@ _getxattr(const Policy::Search &searchFunc_,
rv = searchFunc_(branches_,fusepath_,branches);
if(rv < 0)
return rv;
if(branches.empty())
return -ENOENT;
fullpath = branches[0]->path / fusepath_;

View file

@ -45,7 +45,7 @@ _want(fuse_conn_info_t *conn_,
static
bool
_capable(fuse_conn_info_t *conn_,
const int flag_)
const int flag_)
{
return !!(conn_->capable & flag_);
}
@ -53,7 +53,7 @@ _capable(fuse_conn_info_t *conn_,
static
void
_want_if_capable(fuse_conn_info_t *conn_,
const int flag_)
const int flag_)
{
if(::_capable(conn_,flag_))
::_want(conn_,flag_);
@ -62,8 +62,8 @@ _want_if_capable(fuse_conn_info_t *conn_,
static
void
_want_if_capable(fuse_conn_info_t *conn_,
const int flag_,
ConfigBOOL *want_)
const int flag_,
ConfigBOOL *want_)
{
if(*want_ && ::_capable(conn_,flag_))
{

View file

@ -32,6 +32,7 @@
#include "fs_open.hpp"
#include "fs_path.hpp"
#include "mergerfs_ioctl.hpp"
#include "state.hpp"
#include "str.hpp"
#include <string>
@ -115,6 +116,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_);
}
@ -125,7 +129,7 @@ _ioctl_file(const fuse_req_ctx_t *ctx_,
static
int
_ioctl_dir_base(const Policy::Search &searchFunc_,
const Branches &branches_,
const Branches::Ptr branches_,
const fs::path &fusepath_,
const u32 cmd_,
void *data_,
@ -139,6 +143,8 @@ _ioctl_dir_base(const Policy::Search &searchFunc_,
rv = searchFunc_(branches_,fusepath_,branches);
if(rv < 0)
return rv;
if(branches.empty())
return -ENOENT;
fullpath = branches[0]->path / fusepath_;
@ -163,6 +169,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(),

View file

@ -74,7 +74,7 @@ static
int
_link_create_path(const Policy::Search &searchFunc_,
const Policy::Action &actionFunc_,
const Branches &ibranches_,
const Branches::Ptr ibranches_,
const fs::path &oldfusepath_,
const fs::path &newfusepath_)
{
@ -86,12 +86,16 @@ _link_create_path(const Policy::Search &searchFunc_,
rv = actionFunc_(ibranches_,oldfusepath_,oldbranches);
if(rv < 0)
return rv;
if(oldbranches.empty())
return -ENOENT;
newfusedirpath = newfusepath_.parent_path();
rv = searchFunc_(ibranches_,newfusedirpath,newbranches);
if(rv < 0)
return rv;
if(newbranches.empty())
return -ENOENT;
return ::_link_create_path_loop(oldbranches,newbranches[0],
oldfusepath_,newfusepath_,
@ -148,7 +152,7 @@ _link_preserve_path_loop(const std::vector<Branch*> &oldbranches_,
static
int
_link_preserve_path(const Policy::Action &actionFunc_,
const Branches &branches_,
const Branches::Ptr branches_,
const fs::path &oldfusepath_,
const fs::path &newfusepath_,
struct stat *st_)
@ -159,6 +163,8 @@ _link_preserve_path(const Policy::Action &actionFunc_,
rv = actionFunc_(branches_,oldfusepath_,oldbranches);
if(rv < 0)
return rv;
if(oldbranches.empty())
return -ENOENT;
return ::_link_preserve_path_loop(oldbranches,
oldfusepath_,
@ -231,8 +237,8 @@ _link_exdev_rel_symlink(const fuse_req_ctx_t *ctx_,
static
int
_link_exdev_abs_base_symlink(const fuse_req_ctx_t *ctx_,
const Policy::Search &openPolicy_,
const Branches::Ptr &ibranches_,
const Policy::Search &openPolicy_,
const Branches::Ptr ibranches_,
const fs::path &oldpath_,
const fs::path &newpath_,
struct stat *st_,
@ -245,6 +251,8 @@ _link_exdev_abs_base_symlink(const fuse_req_ctx_t *ctx_,
rv = openPolicy_(ibranches_,oldpath_,obranches);
if(rv < 0)
return rv;
if(obranches.empty())
return -ENOENT;
target = obranches[0]->path / oldpath_;

View file

@ -121,7 +121,7 @@ _listxattr(const std::vector<Branch*> &branches_,
static
int
_listxattr(const Policy::Search &searchFunc_,
const Branches &ibranches_,
const Branches::Ptr ibranches_,
const fs::path &fusepath_,
char *list_,
const size_t size_)

View file

@ -103,7 +103,7 @@ int
_mkdir(const ugid_t ugid_,
const Policy::Search &getattrPolicy_,
const Policy::Create &mkdirPolicy_,
const Branches &branches_,
const Branches::Ptr branches_,
const fs::path &fusepath_,
const mode_t mode_,
const mode_t umask_)
@ -118,10 +118,14 @@ _mkdir(const ugid_t ugid_,
rv = getattrPolicy_(branches_,fusedirpath,existingbranches);
if(rv < 0)
return rv;
if(existingbranches.empty())
return -ENOENT;
rv = mkdirPolicy_(branches_,fusedirpath,createbranches);
if(rv < 0)
return rv;
if(createbranches.empty())
return -ENOENT;
return ::_mkdir_loop(ugid_,
existingbranches[0],

View file

@ -108,7 +108,7 @@ int
_mknod(const ugid_t ugid_,
const Policy::Search &searchFunc_,
const Policy::Create &createFunc_,
const Branches &branches_,
const Branches::Ptr branches_,
const fs::path &fusepath_,
const mode_t mode_,
const mode_t umask_,
@ -124,10 +124,14 @@ _mknod(const ugid_t ugid_,
rv = searchFunc_(branches_,fusedirpath,existingbranches);
if(rv < 0)
return rv;
if(existingbranches.empty())
return -ENOENT;
rv = createFunc_(branches_,fusedirpath,createbranches);
if(rv < 0)
return rv;
if(createbranches.empty())
return -ENOENT;
return ::_mknod_loop(ugid_,
existingbranches[0]->path,

View file

@ -25,9 +25,9 @@
simultaneously attempt to open the same file (same nodeid), and we
need to:
1. Share a single canonical FileInfo entry in the open_files map for all
opens of the same file while still giving each FUSE handle its own
FileInfo object and file descriptor
1. Share a single canonical FileInfo entry in the open_files map for
all opens of the same file while still giving each FUSE handle
its own FileInfo object and file descriptor
2. Properly reference count to know when to release resources
3. Handle race conditions without deadlocks or resource leaks
4. Support FUSE passthrough mode for performance
@ -45,8 +45,8 @@
FileInfo by re-opening the canonical file descriptor
3. If it doesn't exist, we create a new FileInfo and try to insert it
4. If insertion fails (another thread inserted first), we clean up
and retry (because to use passthrough it MUST be the same underlying
file as the backing id was created)
and retry (because to use passthrough it MUST be the same
underlying file as the backing id was created)
THE RETRY LOOP
--------------
@ -308,7 +308,7 @@ _open_fd(const int fd_,
static
int
_open(const Policy::Search &searchFunc_,
const Branches &ibranches_,
const Branches::Ptr ibranches_,
const fs::path &fusepath_,
fuse_file_info_t *ffi_,
const bool link_cow_,
@ -321,6 +321,8 @@ _open(const Policy::Search &searchFunc_,
rv = searchFunc_(ibranches_,fusepath_,obranches);
if(rv < 0)
return rv;
if(obranches.empty())
return -ENOENT;
filepath = obranches[0]->path / fusepath_;
@ -423,6 +425,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))
{

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_);

View file

@ -84,18 +84,24 @@ _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;
constexpr const char msg[] =
"error: no valid mergerfs branch found, check config";
de = {};
de.d_ino = 0;
de.d_off = 0;
de.d_type = DT_UNKNOWN;
strcpy(de.d_name,"error: no valid mergerfs branch found, check config");
strncpy(de.d_name,msg,(sizeof(msg) - 1));
de.d_reclen = sizeof(de);
fuse_dirents_add(buf_,&de,::strlen(de.d_name));
fuse_dirents_add(buf_,&de,(sizeof(msg) - 1));
return 0;
}

View file

@ -22,6 +22,7 @@
#include "tofrom_string.hpp"
#include "fuse_readdir_base.hpp"
#include <atomic>
#include <memory>
#include <mutex>
@ -58,7 +59,7 @@ namespace FUSE
void initialize();
private:
bool _initialized = false;
std::atomic<bool> _initialized = false;
std::shared_ptr<std::string> _str;
std::shared_ptr<FUSE::ReadDirBase> _impl;
};

View file

@ -47,7 +47,7 @@ static
inline
int
_concurrent_readdir(ThreadPool &tp_,
const Branches::Ptr &branches_,
const Branches::Ptr branches_,
const fs::path &rel_dirpath_,
fuse_dirents_t *dirents_)
{
@ -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,

View file

@ -53,6 +53,9 @@ _readdir(const fs::path &branch_path_,
DEFER{ fs::close(fd); };
buf = msgbuf_alloc();
if(not buf)
return -ENOMEM;
DEFER{ msgbuf_free(buf); };
fs::inode::ReaddirCalc inodecalc(branch_path_,rel_dirpath_);

View file

@ -45,7 +45,7 @@ static
inline
int
_readdir(ThreadPool &tp_,
const Branches::Ptr &branches_,
const Branches::Ptr branches_,
const fs::path &rel_dirpath_,
fuse_dirents_t *dirents_)
{
@ -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,

View file

@ -87,6 +87,8 @@ _readdir(std::vector<std::future<DirRV>> &dh_futures_,
fuse_msgbuf_t *buf;
buf = msgbuf_alloc();
if(not buf)
return -ENOMEM;
DEFER { msgbuf_free(buf); };
for(auto &dh_future : dh_futures_)

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_);

View file

@ -45,6 +45,8 @@ _readdir(const Branches::Ptr &branches_,
fuse_dirents_reset(dirents_);
buf = msgbuf_alloc();
if(not buf)
return -ENOMEM;
DEFER { msgbuf_free(buf); };
for(const auto &branch : *branches_)

View file

@ -40,12 +40,11 @@ _readlink_core_standard(const fs::path &fullpath_,
{
int rv;
rv = fs::readlink(fullpath_,buf_,bufsize_);
rv = fs::readlink(fullpath_,buf_,(bufsize_ - 1));
if(rv < 0)
return rv;
if((size_t)rv < bufsize_)
buf_[rv] = '\0';
buf_[rv] = '\0';
return 0;
}
@ -71,10 +70,9 @@ _readlink_core_symlinkify(const fs::path &fullpath_,
if(!symlinkify::can_be_symlink(st,symlinkify_timeout_))
return ::_readlink_core_standard(fullpath_,buf_,bufsize_);
n = std::min(fullpath_str.size(),bufsize_);
n = std::min(fullpath_str.size(),(bufsize_ - 1));
memcpy(buf_,fullpath_str.c_str(),n);
if(n < bufsize_)
buf_[n] = '\0';
buf_[n] = '\0';
return 0;
}
@ -101,7 +99,7 @@ _readlink_core(const fs::path &basepath_,
static
int
_readlink(const Policy::Search &searchFunc_,
const Branches &ibranches_,
const Branches::Ptr ibranches_,
const fs::path &fusepath_,
char *buf_,
const size_t bufsize_,

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);

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);
}

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);
}

View file

@ -70,8 +70,8 @@ _remove(const StrVec &toremove_)
static
int
_rename_create_path(const Policy::Search &searchPolicy_,
const Policy::Action &actionPolicy_,
const Branches::Ptr &branches_,
const Policy::Action &actionPolicy_,
const Branches::Ptr branches_,
const fs::path &oldfusepath_,
const fs::path &newfusepath_)
{
@ -86,10 +86,14 @@ _rename_create_path(const Policy::Search &searchPolicy_,
rv = actionPolicy_(branches_,oldfusepath_,oldbranches);
if(rv < 0)
return rv;
if(oldbranches.empty())
return -ENOENT;
rv = searchPolicy_(branches_,newfusepath_.parent_path(),newbranches);
if(rv < 0)
return rv;
if(newbranches.empty())
return -ENOENT;
for(auto &branch : *branches_)
{
@ -127,7 +131,7 @@ _rename_create_path(const Policy::Search &searchPolicy_,
static
int
_rename_preserve_path(const Policy::Action &actionPolicy_,
const Branches::Ptr &branches_,
const Branches::Ptr branches_,
const fs::path &oldfusepath_,
const fs::path &newfusepath_)
{
@ -198,7 +202,7 @@ _rename_exdev_rename_back(const std::vector<Branch*> &branches_,
static
int
_rename_exdev_rename_target(const Policy::Action &actionPolicy_,
const Branches::Ptr &ibranches_,
const Branches::Ptr ibranches_,
const fs::path &oldfusepath_,
std::vector<Branch*> &obranches_)
{
@ -245,8 +249,8 @@ _rename_exdev_rename_target(const Policy::Action &actionPolicy_,
static
int
_rename_exdev_rel_symlink(const fuse_req_ctx_t *ctx_,
const Policy::Action &actionPolicy_,
const Branches::Ptr &branches_,
const Policy::Action &actionPolicy_,
const Branches::Ptr branches_,
const fs::path &oldfusepath_,
const fs::path &newfusepath_)
{
@ -274,8 +278,8 @@ _rename_exdev_rel_symlink(const fuse_req_ctx_t *ctx_,
static
int
_rename_exdev_abs_symlink(const fuse_req_ctx_t *ctx_,
const Policy::Action &actionPolicy_,
const Branches::Ptr &branches_,
const Policy::Action &actionPolicy_,
const Branches::Ptr branches_,
const fs::path &mount_,
const fs::path &oldfusepath_,
const fs::path &newfusepath_)

View file

@ -78,7 +78,7 @@ _rmdir_loop(const std::vector<Branch*> &branches_,
static
int
_rmdir(const Policy::Action &actionFunc_,
const Branches &branches_,
const Branches::Ptr branches_,
const FollowSymlinks followsymlinks_,
const fs::path &fusepath_)
{

View file

@ -145,7 +145,7 @@ static
int
_setxattr(const Policy::Action &setxattrPolicy_,
const Policy::Search &getxattrPolicy_,
const Branches &branches_,
const Branches::Ptr branches_,
const fs::path &fusepath_,
const char *attrname_,
const char *attrval_,
@ -159,6 +159,8 @@ _setxattr(const Policy::Action &setxattrPolicy_,
rv = setxattrPolicy_(branches_,fusepath_,branches);
if(rv < 0)
return rv;
if(branches.empty())
return -ENOENT;
::_setxattr_loop(branches,fusepath_,attrname_,attrval_,attrvalsize_,flags_,&prv);
if(prv.errors.empty())
@ -170,6 +172,8 @@ _setxattr(const Policy::Action &setxattrPolicy_,
rv = getxattrPolicy_(branches_,fusepath_,branches);
if(rv < 0)
return rv;
if(branches.empty())
return -ENOENT;
return prv.get_error(branches[0]->path);
}

View file

@ -41,9 +41,13 @@ _normalize_statvfs(struct statvfs *fsstat_,
const unsigned long min_frsize_,
const unsigned long min_namemax_)
{
fsstat_->f_blocks = (fsblkcnt_t)((fsstat_->f_blocks * fsstat_->f_frsize) / min_frsize_);
fsstat_->f_bfree = (fsblkcnt_t)((fsstat_->f_bfree * fsstat_->f_frsize) / min_frsize_);
fsstat_->f_bavail = (fsblkcnt_t)((fsstat_->f_bavail * fsstat_->f_frsize) / min_frsize_);
long double scale;
scale = (((long double)fsstat_->f_frsize) / ((long double)min_frsize_));
fsstat_->f_blocks = (fsblkcnt_t)(fsstat_->f_blocks * scale);
fsstat_->f_bfree = (fsblkcnt_t)(fsstat_->f_bfree * scale);
fsstat_->f_bavail = (fsblkcnt_t)(fsstat_->f_bavail * scale);
fsstat_->f_bsize = min_bsize_;
fsstat_->f_frsize = min_frsize_;
fsstat_->f_namemax = min_namemax_;
@ -76,7 +80,7 @@ _should_ignore(const StatFSIgnore ignore_,
static
int
_statfs(const Branches::Ptr &branches_,
_statfs(const Branches::Ptr branches_,
const fs::path &fusepath_,
const StatFS mode_,
const StatFSIgnore ignore_,
@ -147,7 +151,7 @@ FUSE::statfs(const fuse_req_ctx_t *ctx_,
const char *fusepath_,
struct statvfs *st_)
{
const fs::path fusepath{fusepath_};
const fs::path fusepath{fusepath_};
return ::_statfs(cfg.branches,
fusepath,

View file

@ -75,7 +75,8 @@ _set_stat_if_leads_to_reg(const fs::path &path_,
static
int
_statx_fake_root(struct fuse_statx *st_)
_statx_fake_root(struct fuse_statx *st_,
fuse_timeouts_t *timeout_)
{
st_->ino = 0;
st_->mode = (S_IFDIR|S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
@ -88,12 +89,16 @@ _statx_fake_root(struct fuse_statx *st_)
st_->mtime.tv_sec = 0;
st_->ctime.tv_sec = 0;
timeout_->entry = cfg.cache_entry;
timeout_->attr = cfg.cache_attr;
return 0;
}
static
int
_statx_controlfile(struct fuse_statx *st_)
_statx_controlfile(struct fuse_statx *st_,
fuse_timeouts_t *timeout_)
{
static const uid_t uid = ::getuid();
static const gid_t gid = ::getgid();
@ -110,13 +115,16 @@ _statx_controlfile(struct fuse_statx *st_)
st_->mtime.tv_sec = now;
st_->ctime.tv_sec = now;
timeout_->entry = cfg.cache_entry;
timeout_->attr = cfg.cache_attr;
return 0;
}
static
int
_statx(const Policy::Search &searchFunc_,
const Branches &branches_,
const Branches::Ptr branches_,
const fs::path &fusepath_,
const uint32_t flags_,
const uint32_t mask_,
@ -132,6 +140,8 @@ _statx(const Policy::Search &searchFunc_,
rv = searchFunc_(branches_,fusepath_,branches);
if(rv < 0)
return rv;
if(branches.empty())
return -ENOENT;
fullpath = branches[0]->path / fusepath_;
@ -190,7 +200,7 @@ _statx(const fs::path &fusepath_,
cfg.symlinkify_timeout,
cfg.follow_symlinks);
if((rv < 0) && Config::is_rootdir(fusepath_))
return ::_statx_fake_root(st_);
return ::_statx_fake_root(st_,timeout_);
timeout_->entry = ((rv >= 0) ?
cfg.cache_entry :
@ -235,7 +245,7 @@ FUSE::statx(const fuse_req_ctx_t *ctx_,
const fs::path fusepath{fusepath_};
if(Config::is_ctrl_file(fusepath))
return ::_statx_controlfile(st_);
return ::_statx_controlfile(st_,timeout_);
return ::_statx(fusepath,
flags_|AT_STATX_DONT_SYNC,
@ -253,27 +263,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 :

View file

@ -97,7 +97,7 @@ int
_symlink(const ugid_t ugid_,
const Policy::Search &searchFunc_,
const Policy::Create &createFunc_,
const Branches &branches_,
const Branches::Ptr branches_,
const char *target_,
const fs::path &linkpath_,
struct stat *st_)
@ -112,10 +112,14 @@ _symlink(const ugid_t ugid_,
rv = searchFunc_(branches_,newdirpath,existingbranches);
if(rv < 0)
return rv;
if(existingbranches.empty())
return -ENOENT;
rv = createFunc_(branches_,newdirpath,newbranches);
if(rv < 0)
return rv;
if(newbranches.empty())
return -ENOENT;
return ::_symlink_loop(ugid_,
existingbranches[0]->path,

View file

@ -64,7 +64,7 @@ static
int
_truncate(const Policy::Action &actionFunc_,
const Policy::Search &searchFunc_,
const Branches &branches_,
const Branches::Ptr branches_,
const fs::path &fusepath_,
const off_t size_)
{
@ -75,6 +75,8 @@ _truncate(const Policy::Action &actionFunc_,
rv = actionFunc_(branches_,fusepath_,branches);
if(rv < 0)
return rv;
if(branches.empty())
return -ENOENT;
::_truncate_loop(branches,fusepath_,size_,&prv);
if(prv.errors.empty())
@ -86,6 +88,8 @@ _truncate(const Policy::Action &actionFunc_,
rv = searchFunc_(branches_,fusepath_,branches);
if(rv < 0)
return rv;
if(branches.empty())
return -ENOENT;
return prv.get_error(branches[0]->path);
}

View file

@ -52,7 +52,7 @@ _unlink_loop(const std::vector<Branch*> &branches_,
static
int
_unlink(const Policy::Action &unlinkPolicy_,
const Branches &branches_,
const Branches::Ptr branches_,
const fs::path &fusepath_)
{
int rv;

View file

@ -63,7 +63,7 @@ static
int
_utimens(const Policy::Action &utimensPolicy_,
const Policy::Search &getattrPolicy_,
const Branches &branches_,
const Branches::Ptr branches_,
const fs::path &fusepath_,
const timespec ts_[2])
{
@ -74,6 +74,8 @@ _utimens(const Policy::Action &utimensPolicy_,
rv = utimensPolicy_(branches_,fusepath_,branches);
if(rv < 0)
return rv;
if(branches.empty())
return -ENOENT;
::_utimens_loop(branches,fusepath_,ts_,&prv);
if(prv.errors.empty())
@ -85,6 +87,8 @@ _utimens(const Policy::Action &utimensPolicy_,
rv = getattrPolicy_(branches_,fusepath_,branches);
if(rv < 0)
return rv;
if(branches.empty())
return -ENOENT;
return prv.get_error(branches[0]->path);
}

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

View file

@ -26,7 +26,6 @@
#include "fs_statvfs_cache.hpp"
#include "policy.hpp"
#include "policy_error.hpp"
#include "rwlock.hpp"
#include <string>
#include <vector>

View file

@ -25,7 +25,6 @@
#include "fs_statvfs_cache.hpp"
#include "policy.hpp"
#include "policy_error.hpp"
#include "rwlock.hpp"
#include <limits>
#include <string>

View file

@ -26,7 +26,6 @@
#include "policy.hpp"
#include "policy_error.hpp"
#include "rnd.hpp"
#include "rwlock.hpp"
#include "strvec.hpp"
#include <string>

View file

@ -26,7 +26,6 @@
#include "fs_statvfs_cache.hpp"
#include "policy.hpp"
#include "policy_error.hpp"
#include "rwlock.hpp"
#include "base_types.h"

View file

@ -25,7 +25,6 @@
#include "fs_statvfs_cache.hpp"
#include "policy.hpp"
#include "policy_error.hpp"
#include "rwlock.hpp"
#include <string>
#include <limits>

View file

@ -1,66 +0,0 @@
/*
ISC License
Copyright (c) 2026, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
#include <pthread.h>
namespace rwlock
{
class ReadGuard
{
public:
ReadGuard(pthread_rwlock_t &lock_)
: _lock(lock_)
{
pthread_rwlock_rdlock(&_lock);
}
~ReadGuard()
{
pthread_rwlock_unlock(&_lock);
}
private:
ReadGuard();
private:
pthread_rwlock_t &_lock;
};
class WriteGuard
{
public:
WriteGuard(pthread_rwlock_t &lock_)
: _lock(lock_)
{
pthread_rwlock_wrlock(&_lock);
}
~WriteGuard()
{
pthread_rwlock_unlock(&_lock);
}
private:
WriteGuard();
private:
pthread_rwlock_t &_lock;
};
}

View file

@ -79,6 +79,7 @@
#include "fuse.h"
#include <atomic>
#include <functional>
#include <map>
#include <string>
@ -102,7 +103,14 @@ public:
{
}
int ref_count;
OpenFile(OpenFile &&o_) noexcept
: ref_count(o_.ref_count.load()),
backing_id(o_.backing_id),
fi(o_.fi)
{
}
std::atomic<int> ref_count;
int backing_id;
FileInfo *fi;
};

View file

@ -300,6 +300,9 @@ bool
str::eq(const char *s0_,
const char *s1_) noexcept
{
if((s0_ == nullptr) || (s1_ == nullptr))
return (s0_ == s1_);
return (std::strcmp(s0_, s1_) == 0);
}
@ -307,6 +310,9 @@ bool
str::startswith(const char *s_,
const char *p_) noexcept
{
if((s_ == nullptr) || (p_ == nullptr))
return false;
while(*p_)
{
if(*p_ != *s_)

View file

@ -102,7 +102,7 @@ struct fuse_lowlevel_ops
void (*bmap)(fuse_req_t *req, const struct fuse_in_header *hdr);
void (*copy_file_range)(fuse_req_t *req, const struct fuse_in_header *hdr);
void (*create)(fuse_req_t *req, struct fuse_in_header *hdr);
void (*destroy)(void *userdata);
void (*destroy)();
void (*fallocate)(fuse_req_t *req, const struct fuse_in_header *hdr);
void (*flock)(fuse_req_t *req, uint64_t ino, fuse_file_info_t *fi, int op);
void (*flush)(fuse_req_t *req, struct fuse_in_header *hdr);
@ -113,7 +113,7 @@ struct fuse_lowlevel_ops
void (*getattr)(fuse_req_t *req, struct fuse_in_header *hdr);
void (*getlk)(fuse_req_t *req, const struct fuse_in_header *hdr);
void (*getxattr)(fuse_req_t *req, struct fuse_in_header *hdr);
void (*init)(void *userdata, fuse_conn_info_t *conn);
void (*init)(fuse_conn_info_t *conn);
void (*ioctl)(fuse_req_t *req, const struct fuse_in_header *hdr);
void (*link)(fuse_req_t *req, struct fuse_in_header *hdr);
void (*listxattr)(fuse_req_t *req, struct fuse_in_header *hdr);
@ -383,20 +383,6 @@ int fuse_reply_ioctl_retry(fuse_req_t *req,
*/
int fuse_reply_ioctl(fuse_req_t *req, int result, const void *buf, uint32_t size);
/**
* Reply to finish ioctl with iov buffer
*
* Possible requests:
* ioctl
*
* @param req request handle
* @param result result to be passed to the caller
* @param iov the vector containing the data
* @param count the size of vector
*/
int fuse_reply_ioctl_iov(fuse_req_t *req, int result, const struct iovec *iov,
int count);
/**
* Reply with poll result event mask
*
@ -535,12 +521,11 @@ int fuse_lowlevel_is_lib_option(const char *opt);
* @param args argument vector
* @param op the low level filesystem operations
* @param op_size sizeof(struct fuse_lowlevel_ops)
* @param userdata user data
* @return the created session object, or NULL on failure
*/
struct fuse_session *fuse_lowlevel_new(struct fuse_args *args,
const struct fuse_lowlevel_ops *op,
size_t op_size, void *userdata);
size_t op_size);
/* ----------------------------------------------------------- *
* Session interface *

View file

@ -214,6 +214,9 @@ private:
static_cast<void*>(node) :
_allocator.allocate(size_, alignof(T)));
if(not mem)
return nullptr;
try
{
return new(mem) T(std::forward<Args>(args_)...);
@ -242,12 +245,13 @@ public:
if(not obj_)
return;
bool should_pool = _should_pool(obj_);
bool should_pool = _should_pool(obj_);
Node *node = to_node(obj_);
obj_->~T();
if(should_pool)
_push_node(to_node(obj_),size_);
_push_node(node,size_);
else
_allocator.deallocate(obj_,size_,alignof(T));
}

View file

@ -180,14 +180,21 @@ ThreadPool::ThreadPool(const unsigned thread_count_,
inline
ThreadPool::~ThreadPool()
{
std::vector<pthread_t> threads;
{
mutex_lockguard(_threads_mutex);
threads = _threads;
}
syslog(LOG_DEBUG,
"threadpool (%s): destroying %zu threads",
_name.c_str(),
_threads.size());
threads.size());
for(auto t : _threads)
for(auto t : threads)
pthread_cancel(t);
for(auto t : _threads)
for(auto t : threads)
pthread_join(t,NULL);
}

View file

@ -136,6 +136,12 @@ CPU::cpu2core()
ifs >> core_id;
if(!ifs)
{
ifs.close();
continue;
}
c2c[i] = core_id;
ifs.close();
@ -169,6 +175,12 @@ CPU::core2cpus()
ifs >> core_id;
if(!ifs)
{
ifs.close();
continue;
}
c2c[core_id].insert(i);
ifs.close();

View file

@ -63,7 +63,7 @@ _debug_set_output_stderr()
auto new_log_file = std::shared_ptr<FILE>(stderr,[](FILE*){});
fuse_cfg.log_file(new_log_file);
fuse_cfg.log_filepath("/dev/stderr");
fuse_cfg.log_filepath("");
return 0;
}

View file

@ -6,7 +6,6 @@
See the file COPYING.LIB
*/
/* For pthread_rwlock_t */
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
@ -15,9 +14,11 @@
#include "crc32b.h"
#include "debug.hpp"
#include "fatal.hpp"
#include "kvec.h"
#include "mutex.hpp"
#include "node.hpp"
#include "xalloc.hpp"
#include "fuse_cfg.hpp"
#include "fuse_req.hpp"
@ -188,7 +189,7 @@ id_hash(uint64_t ino)
static
node_t*
get_node_nocheck(uint64_t nodeid)
get_node(const uint64_t nodeid)
{
size_t hash = id_hash(nodeid);
node_t *node;
@ -200,22 +201,6 @@ get_node_nocheck(uint64_t nodeid)
return NULL;
}
static
node_t*
get_node(const uint64_t nodeid)
{
node_t *node = get_node_nocheck(nodeid);
if(!node)
{
fprintf(stderr,"fuse internal error: node %llu not found\n",
(unsigned long long)nodeid);
abort();
}
return node;
}
static
void
remove_remembered_node(node_t *node_)
@ -290,10 +275,11 @@ node_table_reduce(struct node_table *t)
return;
newarray = realloc(t->array,sizeof(node_t*) * newsize);
if(newarray != NULL)
t->array = (node_t**)newarray;
if(newarray == NULL)
return;
t->size = newsize;
t->array = (node_t**)newarray;
t->size = newsize;
t->split = t->size / 2;
}
@ -471,33 +457,32 @@ static
void
unhash_name(node_t *node)
{
if(node->name)
if(not (node && node->name && node->parent))
return;
size_t hash = name_hash(node->parent->nodeid,node->name);
node_t **nodep = &f.name_table.array[hash];
for(; *nodep != NULL; nodep = &(*nodep)->name_next)
{
size_t hash = name_hash(node->parent->nodeid,node->name);
node_t **nodep = &f.name_table.array[hash];
if(*nodep != node)
continue;
for(; *nodep != NULL; nodep = &(*nodep)->name_next)
if(*nodep == node)
{
*nodep = node->name_next;
node->name_next = NULL;
unref_node(node->parent);
free(node->name);
node->name = NULL;
node->parent = NULL;
f.name_table.use--;
*nodep = node->name_next;
node->name_next = NULL;
unref_node(node->parent);
free(node->name);
node->name = NULL;
node->parent = NULL;
f.name_table.use--;
if(f.name_table.use < f.name_table.size / 4)
remerge_name();
return;
}
fprintf(stderr,
"fuse internal error: unable to unhash node: %llu\n",
(unsigned long long)node->nodeid);
abort();
if(f.name_table.use < f.name_table.size / 4)
remerge_name();
return;
}
fatal::abort("mergerfs: unable to unhash node: {}",
node->nodeid);
}
static
@ -517,6 +502,12 @@ rehash_name()
for(nodep = &t->array[hash]; *nodep != NULL; nodep = next)
{
node_t *node = *nodep;
if(node->parent == NULL)
{
next = &node->name_next;
continue;
}
size_t newhash = name_hash(node->parent->nodeid,node->name);
if(newhash != hash)
@ -542,8 +533,15 @@ hash_name(node_t *node,
uint64_t parentid,
const char *name)
{
size_t hash = name_hash(parentid,name);
node_t *parent = get_node(parentid);
size_t hash;
node_t *parent;
hash = name_hash(parentid,name);
parent = get_node(parentid);
if(parent == NULL)
return -1;
node->name = strdup(name);
if(node->name == NULL)
return -1;
@ -634,12 +632,18 @@ find_node(uint64_t parent,
const char *name)
{
node_t *node;
mutex_lockguard(f.lock);
mutex_lock(f.lock);
if(!name)
node = get_node(parent);
{
node = get_node(parent);
if(node == NULL)
goto out_err;
}
else
node = lookup_node(parent,name);
{
node = lookup_node(parent,name);
}
if(node == NULL)
{
@ -664,8 +668,8 @@ find_node(uint64_t parent,
remove_remembered_node(node);
}
inc_nlookup(node);
out_err:
mutex_unlock(f.lock);
return node;
}
@ -723,8 +727,11 @@ unlock_path(uint64_t nodeid,
wnode->treelock = 0;
}
for(node = get_node(nodeid);
node != end && node->nodeid != FUSE_ROOT_ID;
node = get_node(nodeid);
if(node == NULL)
return;
for(;node != end && node->nodeid != FUSE_ROOT_ID;
node = node->parent)
{
assert(node->treelock != 0);
@ -794,7 +801,14 @@ try_get_path(uint64_t nodeid,
}
}
for(node = get_node(nodeid); node->nodeid != FUSE_ROOT_ID; node = node->parent)
node = get_node(nodeid);
if(node == NULL)
{
err = -ESTALE;
goto out_unlock;
}
for(; node->nodeid != FUSE_ROOT_ID; node = node->parent)
{
err = -ESTALE;
if(node->name == NULL || node->parent == NULL)
@ -874,7 +888,8 @@ queue_element_wakeup(struct lock_queue_element *qe)
if(!qe->path1)
{
/* Just waiting for it to be unlocked */
if(get_node(qe->nodeid1)->treelock == 0)
node_t *qnode = get_node(qe->nodeid1);
if(qnode && (qnode->treelock == 0))
pthread_cond_signal(&qe->cond);
return;
@ -1102,8 +1117,11 @@ forget_node(const uint64_t nodeid,
if(nodeid == FUSE_ROOT_ID)
return;
mutex_lock(f.lock);
mutex_lockguard(f.lock);
node = get_node(nodeid);
if(node == NULL)
return;
/*
* Node may still be locked due to interrupt idiocy in open,
@ -1142,8 +1160,6 @@ forget_node(const uint64_t nodeid,
kv_push(remembered_node_t,f.remembered_nodes,fn);
}
mutex_unlock(f.lock);
}
static
@ -1320,15 +1336,14 @@ reply_entry(fuse_req_t *req_,
static
void
fuse_lib_init(void *data,
fuse_conn_info_t *conn)
fuse_lib_init(fuse_conn_info_t *conn)
{
f.ops.init(conn);
}
static
void
fuse_lib_destroy(void *data)
fuse_lib_destroy()
{
f.ops.destroy();
}
@ -1353,29 +1368,34 @@ fuse_lib_lookup(fuse_req_t *req_,
if(name[1] == '\0')
{
name = NULL;
mutex_lock(f.lock);
dot = get_node_nocheck(nodeid);
mutex_lockguard(f.lock);
dot = get_node(nodeid);
if(dot == NULL)
{
mutex_unlock(f.lock);
reply_entry(req_,&e,-ESTALE);
return;
}
dot->refctr++;
mutex_unlock(f.lock);
}
else if((name[1] == '.') && (name[2] == '\0'))
{
if(nodeid == 1)
if(nodeid == FUSE_ROOT_ID)
{
reply_entry(req_,&e,-ENOENT);
return;
}
name = NULL;
mutex_lock(f.lock);
nodeid = get_node(nodeid)->parent->nodeid;
mutex_unlock(f.lock);
mutex_lockguard(f.lock);
{
node_t *pnode = get_node(nodeid);
if((pnode == NULL) || (pnode->parent == NULL))
{
reply_entry(req_,&e,-ESTALE);
return;
}
nodeid = pnode->parent->nodeid;
}
}
}
@ -1476,19 +1496,28 @@ fuse_lib_getattr(fuse_req_t *req_,
if(!err)
{
err = ((fusepath != NULL) ?
f.ops.getattr(&req_->ctx,&fusepath[1],&buf,&timeouts) :
f.ops.fgetattr(&req_->ctx,fh,&buf,&timeouts));
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;
free_path(hdr_->nodeid,fusepath);
}
if(!err)
{
mutex_lock(f.lock);
node = get_node(hdr_->nodeid);
update_stat(node,&buf);
mutex_unlock(f.lock);
{
mutex_lockguard(f.lock);
node = get_node(hdr_->nodeid);
if(node == NULL)
{
fuse_reply_err(req_,ENOMEM);
return;
}
update_stat(node,&buf);
}
set_stat(hdr_->nodeid,&buf);
fuse_reply_attr(req_,&buf,timeouts.attr);
}
@ -1654,9 +1683,15 @@ fuse_lib_setattr(fuse_req_t *req_,
if(!err)
{
mutex_lock(f.lock);
update_stat(get_node(hdr_->nodeid),&stbuf);
mutex_unlock(f.lock);
{
node_t *node;
mutex_lockguard(f.lock);
node = get_node(hdr_->nodeid);
if(node)
update_stat(node,&stbuf);
}
set_stat(hdr_->nodeid,&stbuf);
fuse_reply_attr(req_,&stbuf,timeouts.attr);
}
@ -1997,15 +2032,17 @@ fuse_do_release(fuse_req_ctx_t *req_ctx_,
f.ops.release(req_ctx_,
ffi_);
mutex_lock(f.lock);
mutex_lockguard(f.lock);
{
node_t *node;
node = get_node(ino_);
assert(node->open_count > 0);
node->open_count--;
if(node)
{
assert(node->open_count > 0);
node->open_count--;
}
}
mutex_unlock(f.lock);
}
static
@ -2021,8 +2058,8 @@ fuse_lib_create(fuse_req_t *req_,
struct fuse_entry_param e;
struct fuse_create_in *arg;
arg = (fuse_create_in*)fuse_hdr_arg(hdr_);
name = (const char*)PARAM(arg);
arg = (fuse_create_in*)fuse_hdr_arg(hdr_);
name = (const char*)PARAM(arg);
ffi.flags = arg->flags;
@ -2034,7 +2071,15 @@ fuse_lib_create(fuse_req_t *req_,
// opportunistically allocate a node so we have a new nodeid for
// later in the actual create. Added for use with passthrough.
{
node_t *node = find_node(hdr_->nodeid,name);
node_t *node;
node = find_node(hdr_->nodeid,name);
if(node == NULL)
{
fuse_reply_err(req_,ENOMEM);
return;
}
new_nodeid = node->nodeid;
req_->ctx.nodeid = new_nodeid;
}
@ -2065,7 +2110,9 @@ fuse_lib_create(fuse_req_t *req_,
if(!err)
{
mutex_lock(f.lock);
get_node(e.ino)->open_count++;
node_t *node = get_node(e.ino);
if(node)
node->open_count++;
mutex_unlock(f.lock);
if(fuse_reply_create(req_,&e,&ffi) == -ENOENT)
@ -2098,6 +2145,12 @@ open_auto_cache(fuse_req_ctx_t *req_ctx_,
mutex_lock(f.lock);
node = get_node(ino);
if(not node)
{
mutex_unlock(f.lock);
return;
}
if(node->is_stat_cache_valid)
{
int err;
@ -2134,7 +2187,7 @@ fuse_lib_open(fuse_req_t *req_,
fuse_file_info_t ffi = {};
struct fuse_open_in *arg;
arg = (fuse_open_in*)fuse_hdr_arg(hdr_);
arg = (fuse_open_in*)fuse_hdr_arg(hdr_);
ffi.flags = arg->flags;
@ -2154,7 +2207,9 @@ fuse_lib_open(fuse_req_t *req_,
if(!err)
{
mutex_lock(f.lock);
get_node(hdr_->nodeid)->open_count++;
node_t *node = get_node(hdr_->nodeid);
if(node)
node->open_count++;
mutex_unlock(f.lock);
/* The open syscall was interrupted,so it must be cancelled */
if(fuse_reply_open(req_,&ffi) == -ENOENT)
@ -2187,6 +2242,11 @@ fuse_lib_read(fuse_req_t *req_,
}
msgbuf = msgbuf_alloc_page_aligned();
if(msgbuf == NULL)
{
fuse_reply_err(req_,ENOMEM);
return;
}
res = f.ops.read(&req_->ctx,
&ffi,
@ -2260,11 +2320,17 @@ fuse_lib_fsync(fuse_req_t *req_,
static
struct fuse_dh*
get_dirhandle(const fuse_file_info_t *llfi,
fuse_file_info_t *fi)
fuse_file_info_t *ffi)
{
struct fuse_dh *dh = (struct fuse_dh *)(uintptr_t)llfi->fh;
memset(fi,0,sizeof(fuse_file_info_t));
fi->fh = dh->fh;
if(dh == NULL)
return NULL;
memset(ffi,0,sizeof(fuse_file_info_t));
ffi->fh = dh->fh;
return dh;
}
@ -2283,14 +2349,21 @@ fuse_lib_opendir(fuse_req_t *req_,
arg = (fuse_open_in*)fuse_hdr_arg(hdr_);
llffi.flags = arg->flags;
dh = (struct fuse_dh *)calloc(1,sizeof(struct fuse_dh));
dh = (fuse_dh*)calloc(1,sizeof(fuse_dh));
if(dh == NULL)
{
fuse_reply_err(req_,ENOMEM);
return;
}
fuse_dirents_init(&dh->d);
err = fuse_dirents_init(&dh->d);
if(err)
{
free(dh);
fuse_reply_err(req_,ENOMEM);
return;
}
mutex_init(dh->lock);
llffi.fh = (uintptr_t)dh;
@ -2313,8 +2386,9 @@ fuse_lib_opendir(fuse_req_t *req_,
{
/* The opendir syscall was interrupted,so it
must be cancelled */
f.ops.releasedir(&req_->ctx,
&ffi);
f.ops.releasedir(&req_->ctx,&ffi);
fuse_dirents_free(&dh->d);
mutex_destroy(dh->lock);
free(dh);
}
@ -2322,6 +2396,7 @@ fuse_lib_opendir(fuse_req_t *req_,
else
{
fuse_reply_err(req_,err);
fuse_dirents_free(&dh->d);
mutex_destroy(dh->lock);
free(dh);
}
@ -2363,7 +2438,7 @@ fuse_lib_readdir(fuse_req_t *req_,
size_t size;
fuse_dirents_t *d;
struct fuse_dh *dh;
fuse_file_info_t ffi = {};
fuse_file_info_t ffi;
fuse_file_info_t llffi = {};
struct fuse_read_in *arg;
@ -2372,9 +2447,15 @@ fuse_lib_readdir(fuse_req_t *req_,
llffi.fh = arg->fh;
dh = get_dirhandle(&llffi,&ffi);
d = &dh->d;
if(not dh)
{
fuse_reply_err(req_,EBADF);
return;
}
mutex_lock(dh->lock);
d = &dh->d;
mutex_lockguard(dh->lock);
rv = 0;
if((arg->offset == 0) || (kv_size(d->data) == 0))
@ -2385,17 +2466,17 @@ fuse_lib_readdir(fuse_req_t *req_,
if(rv)
{
fuse_reply_err(req_,rv);
goto out;
return;
}
size = readdir_buf_size(d,size,arg->offset);
fuse_reply_buf(req_,
readdir_buf(d,arg->offset),
size);
out:
mutex_unlock(dh->lock);
if(size == 0)
fuse_reply_buf(req_,NULL,0);
else
fuse_reply_buf(req_,
readdir_buf(d,arg->offset),
size);
}
static
@ -2407,7 +2488,7 @@ fuse_lib_readdir_plus(fuse_req_t *req_,
size_t size;
fuse_dirents_t *d;
struct fuse_dh *dh;
fuse_file_info_t ffi = {};
fuse_file_info_t ffi;
fuse_file_info_t llffi = {};
struct fuse_read_in *arg;
@ -2416,9 +2497,15 @@ fuse_lib_readdir_plus(fuse_req_t *req_,
llffi.fh = arg->fh;
dh = get_dirhandle(&llffi,&ffi);
d = &dh->d;
if(not dh)
{
fuse_reply_err(req_,EBADF);
return;
}
mutex_lock(dh->lock);
d = &dh->d;
mutex_lockguard(dh->lock);
rv = 0;
if((arg->offset == 0) || (kv_size(d->data) == 0))
@ -2429,17 +2516,17 @@ fuse_lib_readdir_plus(fuse_req_t *req_,
if(rv)
{
fuse_reply_err(req_,rv);
goto out;
return;
}
size = readdir_buf_size(d,size,arg->offset);
fuse_reply_buf(req_,
readdir_buf(d,arg->offset),
size);
out:
mutex_unlock(dh->lock);
if(size == 0)
fuse_reply_buf(req_,NULL,0);
else
fuse_reply_buf(req_,
readdir_buf(d,arg->offset),
size);
}
static
@ -2457,6 +2544,11 @@ fuse_lib_releasedir(fuse_req_t *req_,
llffi.flags = arg->flags;
dh = get_dirhandle(&llffi,&ffi);
if(not dh)
{
fuse_reply_err(req_,EBADF);
return;
}
f.ops.releasedir(&req_->ctx,
&ffi);
@ -2477,6 +2569,7 @@ fuse_lib_fsyncdir(fuse_req_t *req_,
{
int err;
int is_datasync;
fuse_dh *dh;
fuse_file_info_t ffi;
fuse_file_info_t llffi = {};
struct fuse_fsync_in *arg;
@ -2485,7 +2578,12 @@ fuse_lib_fsyncdir(fuse_req_t *req_,
is_datasync = !!(arg->fsync_flags & FUSE_FSYNC_FDATASYNC);
llffi.fh = arg->fh;
get_dirhandle(&llffi,&ffi);
dh = get_dirhandle(&llffi,&ffi);
if(not dh)
{
fuse_reply_err(req_,EBADF);
return;
}
err = f.ops.fsyncdir(&req_->ctx,
&ffi,
@ -2822,7 +2920,9 @@ fuse_lib_tmpfile(fuse_req_t *req_,
if(!err)
{
mutex_lock(f.lock);
get_node(e.ino)->open_count++;
node_t *node = get_node(e.ino);
if(node)
node->open_count++;
mutex_unlock(f.lock);
if(fuse_reply_create(req_,&e,&ffi) == -ENOENT)
@ -3008,9 +3108,13 @@ fuse_flush_common(fuse_req_t *req_,
{
flock_to_lock(&lock,&l);
l.owner = ffi_->lock_owner;
mutex_lock(f.lock);
locks_insert(get_node(ino_),&l);
mutex_unlock(f.lock);
{
mutex_lockguard(f.lock);
node_t *n = get_node(ino_);
if(n)
locks_insert(n,&l);
}
/* if op.lock() is defined FLUSH is needed regardless
of op.flush() */
@ -3133,11 +3237,15 @@ fuse_lib_getlk(fuse_req_t *req_,
flock_to_lock(&flk,&lk);
lk.owner = ffi.lock_owner;
mutex_lock(f.lock);
conflict = locks_conflict(get_node(hdr_->nodeid),&lk);
if(conflict)
lock_to_flock(conflict,&flk);
mutex_unlock(f.lock);
{
mutex_lockguard(f.lock);
node_t *n = get_node(hdr_->nodeid);
conflict = (n ? locks_conflict(n,&lk) : NULL);
if(conflict)
lock_to_flock(conflict,&flk);
}
if(!conflict)
err = fuse_lock_common(req_,hdr_->nodeid,&ffi,&flk,F_GETLK);
else
@ -3157,16 +3265,26 @@ fuse_lib_setlk(fuse_req_t *req_,
struct flock *lock,
int sleep)
{
int err = fuse_lock_common(req_,ino,fi,lock,
sleep ? F_SETLKW : F_SETLK);
int err;
err = fuse_lock_common(req_,
ino,
fi,
lock,
sleep ? F_SETLKW : F_SETLK);
if(!err)
{
lock_t l;
flock_to_lock(lock,&l);
l.owner = fi->lock_owner;
mutex_lock(f.lock);
locks_insert(get_node(ino),&l);
mutex_unlock(f.lock);
{
mutex_lockguard(f.lock);
node_t *n = get_node(ino);
if(n)
locks_insert(n,&l);
}
}
fuse_reply_err(req_,err);
@ -3224,20 +3342,20 @@ fuse_lib_ioctl(fuse_req_t *req_,
{
int err;
char *out_buf = NULL;
fuse_file_info_t ffi;
fuse_dh *dh;
fuse_file_info_t ffi = {};
fuse_file_info_t llffi = {};
const void *in_buf;
uint32_t out_size;
const struct fuse_ioctl_in *arg;
arg = (fuse_ioctl_in*)fuse_hdr_arg(hdr_);
if((arg->flags & FUSE_IOCTL_DIR) && !(req_->conn.want & FUSE_CAP_IOCTL_DIR))
{
fuse_reply_err(req_,ENOTTY);
return;
}
if((sizeof(void*) == 4) &&
err = -EPERM;
if(arg->flags & FUSE_IOCTL_UNRESTRICTED)
goto err;
if((sizeof(void*) == 4) &&
(req_->conn.proto_minor >= 16) &&
!(arg->flags & FUSE_IOCTL_32BIT))
{
@ -3248,14 +3366,27 @@ fuse_lib_ioctl(fuse_req_t *req_,
out_size = arg->out_size;
in_buf = (arg->in_size ? PARAM(arg) : NULL);
err = -EPERM;
if(arg->flags & FUSE_IOCTL_UNRESTRICTED)
goto err;
if(arg->flags & FUSE_IOCTL_DIR)
get_dirhandle(&llffi,&ffi);
{
dh = get_dirhandle(&llffi,&ffi);
if(not dh)
{
err = -EBADF;
goto err;
}
}
else
ffi = llffi;
{
ffi = llffi;
}
if((arg->in_size > 0) &&
(out_size > 0) &&
(arg->in_size != out_size))
{
err = -EINVAL;
goto err;
}
if(out_size)
{
@ -3263,28 +3394,27 @@ fuse_lib_ioctl(fuse_req_t *req_,
out_buf = (char*)malloc(out_size);
if(!out_buf)
goto err;
DEFER { free(out_buf); };
}
assert(!arg->in_size || !out_size || arg->in_size == out_size);
if(out_buf && in_buf)
memcpy(out_buf,in_buf,arg->in_size);
if(in_buf && out_buf)
memcpy(out_buf,in_buf,out_size);
err = f.ops.ioctl(&req_->ctx,
&ffi,
arg->cmd,
(void*)(uintptr_t)arg->arg,
arg->flags,
out_buf ?: (void *)in_buf,
out_buf ? out_buf : (void*)in_buf,
&out_size);
if(err < 0)
goto err;
fuse_reply_ioctl(req_,err,out_buf,out_size);
goto out;
return;
err:
fuse_reply_err(req_,err);
out:
free(out_buf);
}
static
@ -3352,7 +3482,11 @@ remembered_node_cmp(const void *a_,
const remembered_node_t *a = (const remembered_node_t*)a_;
const remembered_node_t *b = (const remembered_node_t*)b_;
return (a->time - b->time);
if(a->time < b->time)
return -1;
if(a->time > b->time)
return 1;
return 0;
}
static
@ -3606,7 +3740,10 @@ metrics_log_nodes_info_to_tmp_dir(struct fuse *f_)
struct stat st;
char const *mode = "a";
sprintf(filepath,"/tmp/mergerfs.%d.info",getpid());
snprintf(filepath,
sizeof(filepath),
"/tmp/mergerfs.%d.info",
getpid());
rv = lstat(filepath,&st);
if((rv == 0) && (st.st_size > INFO_FILE_MAX_SIZE))
@ -3725,7 +3862,7 @@ fuse_new(int fd_,
llop.setlk = NULL;
}
f.se = fuse_lowlevel_new_common(args,&llop,sizeof(llop),&f);
f.se = fuse_lowlevel_new_common(args,&llop,sizeof(llop));
if(f.se == NULL)
goto out_free_fs;
@ -3750,11 +3887,11 @@ fuse_new(int fd_,
root = node_alloc();
if(root == NULL)
{
fprintf(stderr,"fuse: memory allocation failed\n");
fprintf(stderr,"mergerfs: root node alloc failed\n");
goto out_free_id_table;
}
root->name = strdup("/");
root->name = xstrdup("/");
root->parent = NULL;
root->nodeid = FUSE_ROOT_ID;

View file

@ -56,6 +56,6 @@ EXTERN_C_BEGIN
struct fuse_session *fuse_lowlevel_new_common(struct fuse_args *args,
const struct fuse_lowlevel_ops *op,
size_t op_size, void *userdata);
size_t op_size);
EXTERN_C_END

View file

@ -57,6 +57,13 @@ _print_error(int rv_)
-rv_);
}
static
int
_nproc()
{
return std::max(1,(int)std::thread::hardware_concurrency());
}
struct AsyncWorker
{
fuse_session *_se;
@ -86,6 +93,11 @@ struct AsyncWorker
fuse_msgbuf_t *msgbuf;
msgbuf = msgbuf_alloc();
if(not msgbuf)
{
fmt::println(stderr,"mergerfs: failure to allocate msgbuf, exiting");
return;
}
while(true)
{
@ -141,6 +153,11 @@ struct SyncWorker
fuse_msgbuf_t *msgbuf;
msgbuf = msgbuf_alloc();
if(not msgbuf)
{
fmt::println(stderr,"mergerfs: failure to allocate msgbuf, exiting");
return;
}
while(true)
{
@ -166,31 +183,6 @@ struct SyncWorker
}
};
int
fuse_start_thread(pthread_t *thread_id,
void *(*func)(void *),
void *arg)
{
int res;
sigset_t oldset;
sigset_t newset;
sigfillset(&newset);
pthread_sigmask(SIG_BLOCK,&newset,&oldset);
res = pthread_create(thread_id,NULL,func,arg);
pthread_sigmask(SIG_SETMASK,&oldset,NULL);
if(res != 0)
{
fprintf(stderr,
"fuse: error creating thread: %s\n",
strerror(res));
return -1;
}
return 0;
}
static
int
_calculate_thread_count(const int raw_thread_count_)
@ -199,12 +191,12 @@ _calculate_thread_count(const int raw_thread_count_)
if(raw_thread_count_ == 0)
{
thread_count = std::thread::hardware_concurrency();
thread_count = ::_nproc();
thread_count = std::min(8,thread_count);
}
else if(raw_thread_count_ < 0)
{
thread_count = (std::thread::hardware_concurrency() / -raw_thread_count_);
thread_count = (::_nproc() / -raw_thread_count_);
thread_count = std::max(1,thread_count);
}
else if(raw_thread_count_ > 0)
@ -225,14 +217,14 @@ _calculate_thread_counts(int *read_thread_count_,
{
int nproc;
nproc = std::thread::hardware_concurrency();
nproc = ::_nproc();
*read_thread_count_ = std::min(8,nproc);
}
else if((*read_thread_count_ == 0) && (*process_thread_count_ == 0))
{
int nproc;
nproc = std::thread::hardware_concurrency();
nproc = ::_nproc();
*read_thread_count_ = 2;
*process_thread_count_ = std::max(2,(nproc - 2));
*process_thread_count_ = std::min(8,*process_thread_count_);

View file

@ -45,7 +45,6 @@ static size_t pagesize;
struct fuse_ll
{
struct fuse_lowlevel_ops op;
void *userdata;
uid_t owner;
fuse_conn_info_t conn;
mutex_t lock;
@ -361,6 +360,9 @@ int
fuse_reply_readlink(fuse_req_t *req,
const char *linkname)
{
if(not linkname)
return fuse_reply_err(req,EIO);
if(fuse_cfg.debug)
fuse_debug_readlink(req->ctx.unique,linkname);
@ -538,12 +540,12 @@ fuse_reply_ioctl_retry(fuse_req_t *req,
const struct iovec *out_iov,
size_t out_count)
{
int res;
struct fuse_ioctl_out arg = {};
struct fuse_ioctl_iovec *in_fiov = NULL;
struct fuse_ioctl_iovec *out_fiov = NULL;
struct iovec iov[4];
size_t count = 1;
int res;
arg.flags |= FUSE_IOCTL_RETRY;
arg.in_iovs = in_count;
@ -603,6 +605,7 @@ fuse_reply_ioctl_retry(fuse_req_t *req,
}
res = send_reply_iov(req, 0, iov, count);
out:
free(in_fiov);
free(out_fiov);
@ -647,36 +650,6 @@ fuse_reply_ioctl(fuse_req_t *req,
return send_reply_iov(req, 0, iov, count);
}
int
fuse_reply_ioctl_iov(fuse_req_t *req,
int result,
const struct iovec *iov,
int count)
{
struct iovec *padded_iov;
struct fuse_ioctl_out arg = {};
int res;
padded_iov = (iovec*)malloc((count + 2) * sizeof(struct iovec));
if(padded_iov == NULL)
return fuse_reply_err(req, ENOMEM);
arg.result = result;
if(fuse_cfg.debug)
fuse_debug_ioctl_out(req->ctx.unique,&arg);
padded_iov[1].iov_base = &arg;
padded_iov[1].iov_len = sizeof(arg);
memcpy(&padded_iov[2], iov, count * sizeof(struct iovec));
res = send_reply_iov(req, 0, padded_iov, count + 2);
free(padded_iov);
return res;
}
int
fuse_reply_poll(fuse_req_t *req,
unsigned revents)
@ -1195,8 +1168,7 @@ do_init(fuse_req_t *req,
max_write = bufsize;
f.got_init = 1;
if(f.op.init)
f.op.init(f.userdata, &f.conn);
f.op.init(&f.conn);
outargflags = outarg.flags;
if((inargflags & FUSE_MAX_PAGES) && (f.conn.want & FUSE_CAP_MAX_PAGES))
@ -1309,7 +1281,8 @@ do_destroy(fuse_req_t *req,
struct fuse_in_header *hdr_)
{
f.got_destroy = 1;
f.op.destroy(f.userdata);
f.op.destroy();
send_reply_ok(req,NULL,0);
}
@ -1697,10 +1670,7 @@ void
fuse_ll_destroy(void *data)
{
if(f.got_init && !f.got_destroy)
{
if(f.op.destroy)
f.op.destroy(f.userdata);
}
f.op.destroy();
mutex_destroy(f.lock);
msgbuf_gc();
@ -1854,8 +1824,7 @@ fuse_ll_buf_process_read_init(struct fuse_session *se_,
struct fuse_session *
fuse_lowlevel_new_common(struct fuse_args *args,
const struct fuse_lowlevel_ops *op,
size_t op_size,
void *userdata)
size_t op_size)
{
struct fuse_session *se;
@ -1871,7 +1840,6 @@ fuse_lowlevel_new_common(struct fuse_args *args,
memcpy(&f.op,op,op_size);
f.owner = getuid();
f.userdata = userdata;
se = fuse_session_new(&f,
(void*)fuse_ll_buf_receive_read,
@ -1892,8 +1860,7 @@ fuse_lowlevel_new_common(struct fuse_args *args,
struct fuse_session*
fuse_lowlevel_new(struct fuse_args *args,
const struct fuse_lowlevel_ops *op,
size_t op_size,
void *userdata)
size_t op_size)
{
return fuse_lowlevel_new_common(args, op, op_size, userdata);
return fuse_lowlevel_new_common(args, op, op_size);
}

View file

@ -214,23 +214,35 @@ int fuse_opt_match(const struct fuse_opt *opts, const char *opt)
return find_opt(opts, opt, &dummy) ? 1 : 0;
}
static int process_opt_param(void *var, const char *format, const char *param,
const char *arg)
static
int
process_opt_param(void *var,
const char *format,
const char *param,
const char *arg)
{
assert(format[0] == '%');
if (format[1] == 's')
if(format[1] == 's')
{
char *copy = strdup(param);
if (!copy)
if(!copy)
return alloc_failed();
*static_cast<char**>(var) = copy;
} else {
if (sscanf(param, format, static_cast<int*>(var)) != 1) {
fprintf(stderr, "fuse: invalid parameter in option `%s'\n", arg);
return -1;
}
}
else
{
int rv;
rv = sscanf(param, format, static_cast<int*>(var));
if(rv != 1)
{
fprintf(stderr,"mergerfs: invalid parameter in option `%s'\n",arg);
return -1;
}
}
return 0;
}

View file

@ -50,6 +50,9 @@ fuse_session_new(void *data,
void
fuse_session_destroy(struct fuse_session *se)
{
if(se == nullptr)
return;
if(se->destroy)
se->destroy(se->f);

View file

@ -8,19 +8,24 @@
#include "fuse_lowlevel.h"
#include <atomic>
#include <stdio.h>
#include <string.h>
#include <signal.h>
static struct fuse_session *fuse_instance;
static std::atomic<fuse_session*> g_fuse_instance{nullptr};
static
void
exit_handler(int sig)
{
(void)sig;
if (fuse_instance)
fuse_session_exit(fuse_instance);
struct fuse_session *instance;
instance = g_fuse_instance.load(std::memory_order_acquire);
if(instance)
fuse_session_exit(instance);
}
static int set_one_signal_handler(int sig, void (*handler)(int), int remove)
@ -54,20 +59,20 @@ int fuse_set_signal_handlers(struct fuse_session *se)
(set_one_signal_handler(SIGPIPE, SIG_IGN, 0) == -1))
return -1;
fuse_instance = se;
g_fuse_instance.store(se,std::memory_order_release);
return 0;
}
void fuse_remove_signal_handlers(struct fuse_session *se)
{
if (fuse_instance != se)
fprintf(stderr,
"fuse: fuse_remove_signal_handlers: unknown session\n");
else
fuse_instance = NULL;
set_one_signal_handler(SIGINT, exit_handler, 1);
set_one_signal_handler(SIGTERM, exit_handler, 1);
set_one_signal_handler(SIGQUIT, exit_handler, 1);
set_one_signal_handler(SIGPIPE, SIG_IGN, 1);
if(g_fuse_instance.load(std::memory_order_acquire) != se)
fprintf(stderr,"mergerfs: fuse_remove_signal_handlers: unknown session\n");
else
g_fuse_instance.store(nullptr,std::memory_order_release);
}

View file

@ -102,7 +102,7 @@ fuse_helper_opt_proc(void *data,
default:
return 1;
}
}
}
static int add_default_subtype(const char *progname, struct fuse_args *args)
@ -153,58 +153,66 @@ fuse_parse_cmdline(struct fuse_args *args_,
err:
free(hopts.mountpoint);
return -1;
}
int fuse_daemonize(int foreground)
int
fuse_daemonize(int foreground)
{
if (!foreground) {
int nullfd;
int waiter[2];
char completed;
if(!foreground)
{
int nullfd;
int waiter[2];
char completed;
if (pipe(waiter)) {
perror("fuse_daemonize: pipe");
return -1;
if(pipe(waiter))
{
perror("fuse_daemonize: pipe");
return -1;
}
/*
* demonize current process by forking it and killing the
* parent. This makes current process as a child of 'init'.
*/
switch(fork())
{
case -1:
perror("fuse_daemonize: fork");
return -1;
case 0:
break;
default:
read(waiter[0], &completed, sizeof(completed));
_exit(0);
}
if(setsid() == -1)
{
perror("fuse_daemonize: setsid");
return -1;
}
(void) chdir("/");
nullfd = open("/dev/null", O_RDWR, 0);
if(nullfd != -1)
{
(void) dup2(nullfd, 0);
(void) dup2(nullfd, 1);
(void) dup2(nullfd, 2);
if (nullfd > 2)
close(nullfd);
}
/* Propagate completion of daemon initializatation */
completed = 1;
write(waiter[1], &completed, sizeof(completed));
close(waiter[0]);
close(waiter[1]);
}
/*
* demonize current process by forking it and killing the
* parent. This makes current process as a child of 'init'.
*/
switch(fork()) {
case -1:
perror("fuse_daemonize: fork");
return -1;
case 0:
break;
default:
read(waiter[0], &completed, sizeof(completed));
_exit(0);
}
if (setsid() == -1) {
perror("fuse_daemonize: setsid");
return -1;
}
(void) chdir("/");
nullfd = open("/dev/null", O_RDWR, 0);
if (nullfd != -1) {
(void) dup2(nullfd, 0);
(void) dup2(nullfd, 1);
(void) dup2(nullfd, 2);
if (nullfd > 2)
close(nullfd);
}
/* Propagate completion of daemon initializatation */
completed = 1;
write(waiter[1], &completed, sizeof(completed));
close(waiter[0]);
close(waiter[1]);
}
return 0;
}

View file

@ -16,7 +16,7 @@ static
void*
_thread_loop(void *)
{
int count;
u64 count;
pthread_setname_np(pthread_self(),"fuse.maint");

View file

@ -0,0 +1,60 @@
#pragma once
#include <stdio.h>
#include <stdlib.h>
#define xstrdup(S) xstrdup_impl((S),__FILE__,__LINE__)
#define xmalloc(S) xmalloc_impl((S),__FILE__,__LINE__)
static
inline
char*
xstrdup_impl(const char *s_,
const char *file_,
const unsigned line_)
{
char *p;
if(s_ == NULL)
{
fprintf(stderr,
"mergerfs: xstrdup called with NULL - file: %s; line: %u\n",
file_,
line_);
exit(EXIT_FAILURE);
}
p = strdup(s_);
if(p == NULL)
{
fprintf(stderr,
"mergerfs: strdup failed - file: %s; line: %u\n",
file_,
line_);
exit(EXIT_FAILURE);
}
return p;
}
static
inline
void*
xmalloc_impl(const size_t size_,
const char *file_,
const unsigned line_)
{
void *p;
p = malloc(size_);
if(p == NULL)
{
fprintf(stderr,
"mergerfs: malloc failed - file: %s; line: %u\n",
file_,
line_);
exit(EXIT_FAILURE);
}
return p;
}

View file

@ -445,6 +445,7 @@ static int unmount_fuse_locked(const char *mnt, int quiet, int lazy)
}
out:
free(copy);
if (res == -1)
return -1;
@ -688,7 +689,7 @@ static int get_mnt_opts(int flags, char *opts, char **mnt_optsp)
if (add_option(mnt_optsp, "user=", strlen(user)) == -1)
return -1;
strcat(*mnt_optsp, user);
strncat(*mnt_optsp,user,strlen(user));
}
return 0;
}
@ -992,7 +993,7 @@ static int check_perm(const char **mntp, struct stat *stbuf, int *mountpoint_fd)
return -1;
}
sprintf(procfile, "/proc/self/fd/%i", *mountpoint_fd);
snprintf(procfile,sizeof(procfile),"/proc/self/fd/%i",*mountpoint_fd);
*mntp = procfile;
} else {
fprintf(stderr,