Browse Source

option_parser.cpp

Antonio SJ Musumeci 5 days ago
parent
commit
6d77b2ec97
  1. 30
      mkdocs/docs/config/deprecated_options.md
  2. 345
      src/config.cpp
  3. 40
      src/config.hpp
  4. 6
      src/config_cachefiles.cpp
  5. 1
      src/config_cachefiles.hpp
  6. 24
      src/config_dummy.hpp
  7. 40
      src/config_noforget.hpp
  8. 5
      src/fuse_create.cpp
  9. 2
      src/fuse_init.cpp
  10. 5
      src/fuse_open.cpp
  11. 482
      src/mergerfs.cpp
  12. 139
      src/option_parser.cpp
  13. 5
      src/option_parser.hpp

30
mkdocs/docs/config/deprecated_options.md

@ -3,19 +3,21 @@
These are old, deprecated options which may no longer have any
function or have been replaced. **They should not be used.**
* **direct_io**: Bypass page cache. Use `cache.files=off`
instead.
* **kernel_cache**: Do not invalidate data cache on file open. Use
`cache.files=full` instead.
* **auto_cache**: Invalidate data cache if file mtime or
size change. Use `cache.files=auto-full` instead. (default: false)
* **async_read**: Perform reads asynchronously. Use
`async_read=true` instead.
* **sync_read**: Perform reads synchronously. Use
`async_read=false` instead.
* **splice_read**: Does nothing.
* **splice_write**: Does nothing.
* **splice_move**: Does nothing.
* **allow_other**: mergerfs v2.35.0 and above sets this FUSE option
automatically if running as root.
* **use_ino**: Effectively replaced with `inodecalc`.
* **async_read**: Use `async_read=true`.
* **atomic_o_trunc**: Does nothing.
* **big_writes**: Does nothing.
* **attr_timeout**: Use `cache.attr`.
* **auto_cache**: Use `cache.files=auto-full`.
* **direct_io**: Use `cache.files=off`.
* **entry_timeout**: Use `cache.entry`.
* **hard_remove**: Does nothing.
* **kernel_cache**: Use `cache.files=full`.
* **negative_entry**: Use `cache.negative_entry`.
* **nonempty**: Does nothing.
* **splice_move**: Does nothing.
* **splice_read**: Does nothing.
* **splice_write**: Does nothing.
* **sync_read**: Use `async_read=false`.
* **use_ino**: Use `inodecalc`.

345
src/config.cpp

@ -45,11 +45,42 @@ constexpr static const char CACHE_FILES_PROCESS_NAMES_DEFAULT[] =
Config cfg;
Config::CfgConfigFile::CfgConfigFile()
{
}
int
Config::CfgConfigFile::from_string(const std::string_view s_)
{
int rv;
fs::path cfg_file;
if(_depth > 5)
return -ELOOP;
_depth++;
cfg_file = (s_.empty() ? _cfg_file : s_);
rv = cfg.from_file(cfg_file);
if(rv == 0)
_cfg_file = cfg_file;
_depth--;
return rv;
}
std::string
Config::CfgConfigFile::to_string() const
{
return _cfg_file;
}
Config::Config()
:
allow_idmap(false),
async_read(true),
auto_cache(false),
branches(minfreespace),
branches_mount_timeout(0),
branches_mount_timeout_fail(false),
@ -63,9 +94,9 @@ Config::Config()
cache_symlinks(false),
cache_writeback(false),
category(func),
config_file(),
congestion_threshold(fuse_cfg.congestion_threshold,0),
debug(fuse_cfg.debug,false),
direct_io(false),
direct_io_allow_mmap(true),
dropcacheonclose(false),
export_support(true),
@ -81,7 +112,6 @@ Config::Config()
handle_killpriv_v2(true),
ignorepponrename(false),
inodecalc("hybrid-hash"),
kernel_cache(false),
kernel_permissions_check(true),
lazy_umount_mountpoint(false),
link_cow(false),
@ -92,6 +122,7 @@ Config::Config()
mountpoint(),
moveonenospc(true),
nfsopenhack(NFSOpenHack::ENUM::OFF),
noforget(),
nullrw(false),
parallel_direct_writes(true),
passthrough(Passthrough::ENUM::OFF),
@ -104,7 +135,6 @@ Config::Config()
read_thread_count(fuse_cfg.read_thread_count,0),
readahead(0),
readdir("seq"),
readdirplus(false),
remember_nodes(fuse_cfg.remember_nodes,0),
rename_exdev(RenameEXDEV::ENUM::PASSTHROUGH),
scheduling_priority(-10),
@ -147,128 +177,119 @@ Config::Config()
srcmounts.ro =
version.ro =
true;
auto_cache.display =
congestion_threshold.ro =
direct_io.display =
congestion_threshold.ro =
gid.display =
kernel_cache.display =
max_background.ro =
readdirplus.display =
threads.display =
uid.display =
umask.display =
false;
_map["allow-idmap"] = &allow_idmap;
_map["async_read"] = &async_read;
_map["auto_cache"] = &auto_cache;
_map["branches"] = &branches;
_map["branches-mount-timeout"] = &branches_mount_timeout;
_map["allow-idmap"] = &allow_idmap;
_map["async_read"] = &async_read;
_map["atomic_o_trunc"] = &_dummy;
_map["auto_cache"] = &_dummy;
_map["big_writes"] = &_dummy;
_map["branches"] = &branches;
_map["branches-mount-timeout"] = &branches_mount_timeout;
_map["branches-mount-timeout-fail"] = &branches_mount_timeout_fail;
_map["cache.attr"] = &cache_attr;
_map["cache.entry"] = &cache_entry;
_map["cache.files"] = &cache_files;
_map["cache.files.process-names"] = &cache_files_process_names;
_map["cache.negative_entry"] = &cache_negative_entry;
_map["cache.readdir"] = &cache_readdir;
_map["cache.statfs"] = &cache_statfs;
_map["cache.symlinks"] = &cache_symlinks;
_map["cache.writeback"] = &cache_writeback;
_map["category.action"] = &category.action;
_map["category.create"] = &category.create;
_map["category.search"] = &category.search;
_map["debug"] = &debug;
_map["direct-io-allow-mmap"] = &direct_io_allow_mmap;
_map["direct_io"] = &direct_io;
_map["dropcacheonclose"] = &dropcacheonclose;
_map["export-support"] = &export_support;
_map["flush-on-close"] = &flushonclose;
_map["follow-symlinks"] = &follow_symlinks;
_map["fsname"] = &fsname;
_map["func.access"] = &func.access;
_map["func.chmod"] = &func.chmod;
_map["func.chown"] = &func.chown;
_map["func.create"] = &func.create;
_map["func.getattr"] = &func.getattr;
_map["func.getxattr"] = &func.getxattr;
_map["func.link"] = &func.link;
_map["func.listxattr"] = &func.listxattr;
_map["func.mkdir"] = &func.mkdir;
_map["func.mknod"] = &func.mknod;
_map["func.open"] = &func.open;
_map["func.readdir"] = &readdir;
_map["func.readlink"] = &func.readlink;
_map["func.removexattr"] = &func.removexattr;
_map["func.rename"] = &func.rename;
_map["func.rmdir"] = &func.rmdir;
_map["func.setxattr"] = &func.setxattr;
_map["func.symlink"] = &func.symlink;
_map["func.truncate"] = &func.truncate;
_map["func.unlink"] = &func.unlink;
_map["func.utimens"] = &func.utimens;
_map["fuse_msg_size"] = &fuse_msg_size;
_map["gid"] = &gid;
_map["gid-cache-expire-timeout"] = &gid_cache_expire_timeout;
_map["gid-cache-remove-timeout"] = &gid_cache_remove_timeout;
_map["handle-killpriv"] = &handle_killpriv;
_map["handle-killpriv-v2"] = &handle_killpriv_v2;
_map["ignorepponrename"] = &ignorepponrename;
_map["inodecalc"] = &inodecalc;
_map["kernel-permissions-check"] = &kernel_permissions_check;
_map["kernel_cache"] = &kernel_cache;
_map["lazy-umount-mountpoint"] = &lazy_umount_mountpoint;
_map["link-exdev"] = &link_exdev;
_map["link_cow"] = &link_cow;
_map["log.metrics"] = &log_metrics;
_map["minfreespace"] = &minfreespace;
_map["mount"] = &mountpoint;
_map["moveonenospc"] = &moveonenospc;
_map["nfsopenhack"] = &nfsopenhack;
_map["nullrw"] = &nullrw;
_map["parallel-direct-writes"] = &parallel_direct_writes;
_map["passthrough"] = &passthrough;
_map["cache.attr"] = &cache_attr;
_map["cache.entry"] = &cache_entry;
_map["cache.files"] = &cache_files;
_map["cache.files.process-names"] = &cache_files_process_names;
_map["cache.negative_entry"] = &cache_negative_entry;
_map["cache.open"] = &_dummy;
_map["cache.readdir"] = &cache_readdir;
_map["cache.statfs"] = &cache_statfs;
_map["cache.symlinks"] = &cache_symlinks;
_map["cache.writeback"] = &cache_writeback;
_map["category.action"] = &category.action;
_map["category.create"] = &category.create;
_map["category.search"] = &category.search;
_map["config"] = &config_file;
_map["debug"] = &debug;
_map["direct-io-allow-mmap"] = &direct_io_allow_mmap;
_map["direct_io"] = &_dummy;
_map["dropcacheonclose"] = &dropcacheonclose;
_map["export-support"] = &export_support;
_map["flush-on-close"] = &flushonclose;
_map["follow-symlinks"] = &follow_symlinks;
_map["fsname"] = &fsname;
_map["func.access"] = &func.access;
_map["func.chmod"] = &func.chmod;
_map["func.chown"] = &func.chown;
_map["func.create"] = &func.create;
_map["func.getattr"] = &func.getattr;
_map["func.getxattr"] = &func.getxattr;
_map["func.link"] = &func.link;
_map["func.listxattr"] = &func.listxattr;
_map["func.mkdir"] = &func.mkdir;
_map["func.mknod"] = &func.mknod;
_map["func.open"] = &func.open;
_map["func.readdir"] = &readdir;
_map["func.readlink"] = &func.readlink;
_map["func.removexattr"] = &func.removexattr;
_map["func.rename"] = &func.rename;
_map["func.rmdir"] = &func.rmdir;
_map["func.setxattr"] = &func.setxattr;
_map["func.symlink"] = &func.symlink;
_map["func.truncate"] = &func.truncate;
_map["func.unlink"] = &func.unlink;
_map["func.utimens"] = &func.utimens;
_map["fuse_msg_size"] = &fuse_msg_size;
_map["gid"] = &gid;
_map["gid-cache-expire-timeout"] = &gid_cache_expire_timeout;
_map["gid-cache-remove-timeout"] = &gid_cache_remove_timeout;
_map["handle-killpriv"] = &handle_killpriv;
_map["handle-killpriv-v2"] = &handle_killpriv_v2;
_map["hard_remove"] = &_dummy;
_map["ignorepponrename"] = &ignorepponrename;
_map["inodecalc"] = &inodecalc;
_map["kernel-permissions-check"] = &kernel_permissions_check;
_map["kernel_cache"] = &_dummy;
_map["lazy-umount-mountpoint"] = &lazy_umount_mountpoint;
_map["link-exdev"] = &link_exdev;
_map["link_cow"] = &link_cow;
_map["log.metrics"] = &log_metrics;
_map["minfreespace"] = &minfreespace;
_map["mount"] = &mountpoint;
_map["moveonenospc"] = &moveonenospc;
_map["negative_entry"] = &_dummy;
_map["nfsopenhack"] = &nfsopenhack;
_map["no_splice_move"] = &_dummy;
_map["no_split_write"] = &_dummy;
_map["noforget"] = &noforget;
_map["nonempty"] = &_dummy;
_map["nullrw"] = &nullrw;
_map["parallel-direct-writes"] = &parallel_direct_writes;
_map["passthrough"] = &passthrough;
_map["passthrough-max-stack-depth"] = &passthrough_max_stack_depth;
_map["pid"] = &pid;
_map["pin-threads"] = &pin_threads;
_map["posix_acl"] = &posix_acl;
_map["process-thread-count"] = &process_thread_count;
_map["process-thread-queue-depth"] = &process_thread_queue_depth;
_map["proxy-ioprio"] = &proxy_ioprio;
_map["read-thread-count"] = &read_thread_count;
_map["readahead"] = &readahead;
_map["readdirplus"] = &readdirplus;
_map["remember-nodes"] = &remember_nodes;
_map["rename-exdev"] = &rename_exdev;
_map["scheduling-priority"] = &scheduling_priority;
_map["security_capability"] = &security_capability;
_map["srcmounts"] = &srcmounts;
_map["statfs"] = &statfs;
_map["statfs_ignore"] = &statfs_ignore;
_map["symlinkify"] = &symlinkify;
_map["symlinkify_timeout"] = &symlinkify_timeout;
_map["threads"] = &threads;
_map["uid"] = &uid;
_map["umask"] = &umask;
_map["version"] = &version;
_map["xattr"] = &xattr;
}
Config&
Config::operator=(const Config &cfg_)
{
int rv;
std::string val;
for(auto &kv : _map)
{
rv = cfg_.get(kv.first,&val);
if(rv)
continue;
kv.second->from_string(val);
}
return *this;
_map["pid"] = &pid;
_map["pin-threads"] = &pin_threads;
_map["posix_acl"] = &posix_acl;
_map["process-thread-count"] = &process_thread_count;
_map["process-thread-queue-depth"] = &process_thread_queue_depth;
_map["proxy-ioprio"] = &proxy_ioprio;
_map["read-thread-count"] = &read_thread_count;
_map["readahead"] = &readahead;
_map["remember-nodes"] = &remember_nodes;
_map["rename-exdev"] = &rename_exdev;
_map["scheduling-priority"] = &scheduling_priority;
_map["security_capability"] = &security_capability;
_map["splice_move"] = &_dummy;
_map["splice_read"] = &_dummy;
_map["splice_write"] = &_dummy;
_map["srcmounts"] = &srcmounts;
_map["statfs"] = &statfs;
_map["statfs_ignore"] = &statfs_ignore;
_map["symlinkify"] = &symlinkify;
_map["symlinkify_timeout"] = &symlinkify_timeout;
_map["threads"] = &threads;
_map["uid"] = &uid;
_map["umask"] = &umask;
_map["use_ino"] = &_dummy;
_map["version"] = &version;
_map["xattr"] = &xattr;
}
bool
@ -317,6 +338,7 @@ Config::keys_listxattr_size() const
{
if(val->display == false)
continue;
rv += sizeof("user.mergerfs.");
rv += key.size();
}
@ -395,16 +417,13 @@ Config::set(const std::string &kv_)
}
int
Config::from_stream(std::istream &istrm_,
ErrVec *errs_)
Config::from_stream(std::istream &istrm_)
{
int rv;
std::string line;
std::string key;
std::string val;
Config newcfg;
newcfg = *this;
Config::ErrVec new_errs;
while(std::getline(istrm_,line,'\n'))
{
@ -416,22 +435,21 @@ Config::from_stream(std::istream &istrm_,
key = str::trim(key);
val = str::trim(val);
rv = newcfg.set(key,val);
rv = set(key,val);
if(rv < 0)
errs_->push_back({rv,key});
new_errs.push_back({-rv,key+'='+val});
}
if(!errs_->empty())
return -EINVAL;
*this = newcfg;
rv = (new_errs.empty() ? 0 : -EINVAL);
errs.insert(errs.end(),
new_errs.begin(),
new_errs.end());
return 0;
return rv;
}
int
Config::from_file(const std::string &filepath_,
ErrVec *errs_)
Config::from_file(const std::string &filepath_)
{
int rv;
std::ifstream ifstrm;
@ -439,11 +457,11 @@ Config::from_file(const std::string &filepath_,
ifstrm.open(filepath_);
if(!ifstrm.good())
{
errs_->push_back({-errno,filepath_});
errs.push_back({errno,filepath_});
return -errno;
}
rv = from_stream(ifstrm,errs_);
rv = from_stream(ifstrm);
ifstrm.close();
@ -500,52 +518,31 @@ Config::prune_cmd_xattr(const std::string_view &s_)
return {};
}
std::ostream&
operator<<(std::ostream &os_,
const Config &c_)
{
for(const auto &[key,val] : c_._map)
os_ << key << '=' << val->to_string() << std::endl;
return os_;
}
static
std::string
err2str(const int err_)
Config::Err::to_string() const
{
switch(err_)
std::string s;
switch(err)
{
case 0:
return std::string();
case -EINVAL:
return "invalid value";
case -ENOATTR:
return "unknown option";
case -EROFS:
return "read-only option";
break;
case EINVAL:
s = "invalid value";
break;
case ENOATTR:
s = "unknown option";
break;
case EROFS:
s = "read-only option";
break;
case ELOOP:
s = "too many levels of config";
break;
default:
return strerror(-err_);
}
return std::string();
}
std::ostream&
operator<<(std::ostream &os_,
const Config::ErrVec &ev_)
{
std::string errstr;
for(auto &err : ev_)
{
os_ << "* ERROR: ";
errstr = err2str(err.err);
if(!errstr.empty())
os_ << errstr << " - ";
os_ << err.str << std::endl;
s = strerror(err);
break;
}
return os_;
return fmt::format("{} - {}",s,str);
}

40
src/config.hpp

@ -16,11 +16,10 @@
#pragma once
#include "fuse_cfg.hpp"
#include "tofrom_ref.hpp"
#include "branches.hpp"
#include "category.hpp"
#include "config_cachefiles.hpp"
#include "config_dummy.hpp"
#include "config_flushonclose.hpp"
#include "config_follow_symlinks.hpp"
#include "config_gidcache.hpp"
@ -29,6 +28,7 @@
#include "config_log_metrics.hpp"
#include "config_moveonenospc.hpp"
#include "config_nfsopenhack.hpp"
#include "config_noforget.hpp"
#include "config_pagesize.hpp"
#include "config_passthrough.hpp"
#include "config_pid.hpp"
@ -42,10 +42,13 @@
#include "errno.hpp"
#include "fs_path.hpp"
#include "funcs.hpp"
#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"
#include "fuse.h"
@ -75,10 +78,30 @@ public:
{
int err;
std::string str;
std::string to_string() const;
};
typedef std::vector<Err> ErrVec;
public:
ErrVec errs;
public:
class CfgConfigFile : public ToFromString
{
private:
fs::path _cfg_file;
int _depth = 0;
public:
CfgConfigFile();
public:
int from_string(const std::string_view s_) final;
std::string to_string(void) const final;
};
public:
Config();
@ -88,7 +111,6 @@ public:
public:
ConfigBOOL allow_idmap;
ConfigBOOL async_read;
ConfigBOOL auto_cache;
Branches branches;
ConfigUINT64 branches_mount_timeout;
ConfigBOOL branches_mount_timeout_fail;
@ -102,9 +124,9 @@ public:
ConfigBOOL cache_symlinks;
ConfigBOOL cache_writeback;
Categories category;
CfgConfigFile config_file;
TFSRef<int> congestion_threshold;
TFSRef<bool> debug;
ConfigBOOL direct_io;
ConfigBOOL direct_io_allow_mmap;
ConfigBOOL dropcacheonclose;
ConfigBOOL export_support;
@ -120,7 +142,6 @@ public:
ConfigBOOL handle_killpriv_v2;
ConfigBOOL ignorepponrename;
InodeCalc inodecalc;
ConfigBOOL kernel_cache;
ConfigBOOL kernel_permissions_check;
ConfigBOOL lazy_umount_mountpoint;
ConfigBOOL link_cow;
@ -131,6 +152,7 @@ public:
ConfigPath mountpoint;
MoveOnENOSPC moveonenospc;
NFSOpenHack nfsopenhack;
CfgNoforget noforget;
ConfigBOOL nullrw;
ConfigBOOL parallel_direct_writes;
Passthrough passthrough;
@ -144,7 +166,6 @@ public:
TFSRef<int> read_thread_count;
ConfigUINT64 readahead;
FUSE::ReadDir readdir;
ConfigBOOL readdirplus;
TFSRef<s64> remember_nodes;
RenameEXDEV rename_exdev;
ConfigINT scheduling_priority;
@ -160,6 +181,9 @@ public:
ConfigROSTR version;
XAttr xattr;
private:
CfgDummy _dummy;
private:
bool _initialized;
@ -183,8 +207,8 @@ public:
int set(const std::string &kv);
public:
int from_stream(std::istream &istrm, ErrVec *errs);
int from_file(const std::string &filepath, ErrVec *errs);
int from_stream(std::istream &istrm);
int from_file(const std::string &filepath);
public:
static bool is_rootdir(const fs::path &fusepath);

6
src/config_cachefiles.cpp

@ -26,8 +26,6 @@ CacheFiles::to_string() const
{
switch(_data)
{
case CacheFiles::ENUM::LIBFUSE:
return "libfuse";
case CacheFiles::ENUM::OFF:
return "off";
case CacheFiles::ENUM::PARTIAL:
@ -47,9 +45,7 @@ template<>
int
CacheFiles::from_string(const std::string_view s_)
{
if(s_ == "libfuse")
_data = CacheFiles::ENUM::LIBFUSE;
ef(s_ == "off")
if(s_ == "off")
_data = CacheFiles::ENUM::OFF;
ef(s_ == "partial")
_data = CacheFiles::ENUM::PARTIAL;

1
src/config_cachefiles.hpp

@ -23,7 +23,6 @@
enum class CacheFilesEnum
{
LIBFUSE,
OFF,
PARTIAL,
FULL,

24
src/config_dummy.hpp

@ -0,0 +1,24 @@
#pragma once
#include "tofrom_string.hpp"
class CfgDummy : public ToFromString
{
public:
CfgDummy()
{
display = false;
}
std::string
to_string() const
{
return {};
}
int
from_string(const std::string_view)
{
return 0;
}
};

40
src/config_noforget.hpp

@ -0,0 +1,40 @@
#pragma once
#include "tofrom_string.hpp"
#include "from_string.hpp"
#include "fuse_cfg.hpp"
class CfgNoforget : public ToFromString
{
public:
int
from_string(const std::string_view s_)
{
int rv;
bool b = false;
rv = str::from(s_,&b);
if((b == true) || s_.empty())
{
fuse_cfg.remember_nodes = -1;
return 0;
}
if(rv)
return rv;
fuse_cfg.remember_nodes = 0;
return 0;
}
std::string
to_string() const
{
if(fuse_cfg.remember_nodes == -1)
return "true";
return "false";
}
};

5
src/fuse_create.cpp

@ -87,11 +87,6 @@ _config_to_ffi_flags(Config &cfg_,
{
switch(cfg_.cache_files)
{
case CacheFiles::ENUM::LIBFUSE:
ffi_->direct_io = cfg_.direct_io;
ffi_->keep_cache = cfg_.kernel_cache;
ffi_->auto_cache = cfg_.auto_cache;
break;
case CacheFiles::ENUM::OFF:
ffi_->direct_io = 1;
ffi_->keep_cache = 0;

2
src/fuse_init.cpp

@ -204,7 +204,7 @@ FUSE::init(fuse_conn_info *conn_)
::_want_if_capable(conn_,FUSE_CAP_PARALLEL_DIROPS);
::_want_if_capable(conn_,FUSE_CAP_PASSTHROUGH);
::_want_if_capable(conn_,FUSE_CAP_POSIX_ACL,&cfg.posix_acl);
::_want_if_capable(conn_,FUSE_CAP_READDIR_PLUS,&cfg.readdirplus);
// ::_want_if_capable(conn_,FUSE_CAP_READDIR_PLUS,&cfg.readdirplus);
::_want_if_capable(conn_,FUSE_CAP_WRITEBACK_CACHE,&cfg.cache_writeback);
::_want_if_capable(conn_,FUSE_CAP_ALLOW_IDMAP,&cfg.allow_idmap);
// ::_want_if_capable(conn_,FUSE_CAP_READDIR_PLUS_AUTO);

5
src/fuse_open.cpp

@ -145,11 +145,6 @@ _config_to_ffi_flags(Config &cfg_,
{
switch(cfg.cache_files)
{
case CacheFiles::ENUM::LIBFUSE:
ffi_->direct_io = cfg.direct_io;
ffi_->keep_cache = cfg.kernel_cache;
ffi_->auto_cache = cfg.auto_cache;
break;
case CacheFiles::ENUM::OFF:
ffi_->direct_io = 1;
ffi_->keep_cache = 0;

482
src/mergerfs.cpp

@ -18,6 +18,7 @@
#include "mergerfs_fsck.hpp"
#include "mergerfs_collect_info.hpp"
#include "config.hpp"
#include "fs_path.hpp"
#include "fs_readahead.hpp"
#include "fs_umount2.hpp"
@ -89,272 +90,279 @@
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <unordered_set>
namespace l
static
void
_get_fuse_operations(struct fuse_operations &ops_,
const bool nullrw_)
{
static
void
get_fuse_operations(struct fuse_operations &ops_,
const bool nullrw_)
{
ops_.access = FUSE::access;
ops_.bmap = FUSE::bmap;
ops_.chmod = FUSE::chmod;
ops_.chown = FUSE::chown;
ops_.copy_file_range = FUSE::copy_file_range;
ops_.create = FUSE::create;
ops_.destroy = FUSE::destroy;
ops_.fallocate = FUSE::fallocate;
ops_.fchmod = FUSE::fchmod;
ops_.fchown = FUSE::fchown;
ops_.fgetattr = FUSE::fgetattr;
ops_.flock = FUSE::flock;
ops_.flush = FUSE::flush;
ops_.fsync = FUSE::fsync;
ops_.fsyncdir = FUSE::fsyncdir;
ops_.ftruncate = FUSE::ftruncate;
ops_.futimens = FUSE::futimens;
ops_.getattr = FUSE::getattr;
ops_.getxattr = FUSE::getxattr;
ops_.init = FUSE::init;
ops_.ioctl = FUSE::ioctl;
ops_.link = FUSE::link;
ops_.listxattr = FUSE::listxattr;
ops_.lock = FUSE::lock;
ops_.mkdir = FUSE::mkdir;
ops_.mknod = FUSE::mknod;
ops_.open = FUSE::open;
ops_.opendir = FUSE::opendir;
ops_.poll = FUSE::poll;;
ops_.read = (nullrw_ ? FUSE::read_null : FUSE::read);
ops_.readdir = FUSE::readdir;
ops_.readdir_plus = FUSE::readdir_plus;
ops_.readlink = FUSE::readlink;
ops_.release = FUSE::release;
ops_.releasedir = FUSE::releasedir;
ops_.removemapping = FUSE::removemapping;
ops_.removexattr = FUSE::removexattr;
ops_.rename = FUSE::rename;
ops_.rmdir = FUSE::rmdir;
ops_.setupmapping = FUSE::setupmapping;
ops_.setxattr = FUSE::setxattr;
ops_.statfs = FUSE::statfs;
ops_.statx = FUSE::statx;
ops_.statx_fh = FUSE::statx_fh;
ops_.symlink = FUSE::symlink;
ops_.syncfs = FUSE::syncfs;
ops_.tmpfile = FUSE::tmpfile;
ops_.truncate = FUSE::truncate;
ops_.unlink = FUSE::unlink;
ops_.utimens = FUSE::utimens;
ops_.write = (nullrw_ ? FUSE::write_null : FUSE::write);
ops_.access = FUSE::access;
ops_.bmap = FUSE::bmap;
ops_.chmod = FUSE::chmod;
ops_.chown = FUSE::chown;
ops_.copy_file_range = FUSE::copy_file_range;
ops_.create = FUSE::create;
ops_.destroy = FUSE::destroy;
ops_.fallocate = FUSE::fallocate;
ops_.fchmod = FUSE::fchmod;
ops_.fchown = FUSE::fchown;
ops_.fgetattr = FUSE::fgetattr;
ops_.flock = FUSE::flock;
ops_.flush = FUSE::flush;
ops_.fsync = FUSE::fsync;
ops_.fsyncdir = FUSE::fsyncdir;
ops_.ftruncate = FUSE::ftruncate;
ops_.futimens = FUSE::futimens;
ops_.getattr = FUSE::getattr;
ops_.getxattr = FUSE::getxattr;
ops_.init = FUSE::init;
ops_.ioctl = FUSE::ioctl;
ops_.link = FUSE::link;
ops_.listxattr = FUSE::listxattr;
ops_.lock = FUSE::lock;
ops_.mkdir = FUSE::mkdir;
ops_.mknod = FUSE::mknod;
ops_.open = FUSE::open;
ops_.opendir = FUSE::opendir;
ops_.poll = FUSE::poll;;
ops_.read = (nullrw_ ? FUSE::read_null : FUSE::read);
ops_.readdir = FUSE::readdir;
ops_.readdir_plus = FUSE::readdir_plus;
ops_.readlink = FUSE::readlink;
ops_.release = FUSE::release;
ops_.releasedir = FUSE::releasedir;
ops_.removemapping = FUSE::removemapping;
ops_.removexattr = FUSE::removexattr;
ops_.rename = FUSE::rename;
ops_.rmdir = FUSE::rmdir;
ops_.setupmapping = FUSE::setupmapping;
ops_.setxattr = FUSE::setxattr;
ops_.statfs = FUSE::statfs;
ops_.statx = FUSE::statx;
ops_.statx_fh = FUSE::statx_fh;
ops_.symlink = FUSE::symlink;
ops_.syncfs = FUSE::syncfs;
ops_.tmpfile = FUSE::tmpfile;
ops_.truncate = FUSE::truncate;
ops_.unlink = FUSE::unlink;
ops_.utimens = FUSE::utimens;
ops_.write = (nullrw_ ? FUSE::write_null : FUSE::write);
return;
}
static
void
_setup_resources(const int scheduling_priority_)
{
std::srand(time(NULL));
resources::reset_umask();
resources::maxout_rlimit_nofile();
resources::maxout_rlimit_fsize();
resources::setpriority(scheduling_priority_);
}
static
void
_set_oom_score_adj()
{
int rv;
int orig;
int score;
if(!oom::has_oom_score_adj())
return;
}
static
void
setup_resources(const int scheduling_priority_)
{
std::srand(time(NULL));
resources::reset_umask();
resources::maxout_rlimit_nofile();
resources::maxout_rlimit_fsize();
resources::setpriority(scheduling_priority_);
}
static
void
set_oom_score_adj()
{
int rv;
int orig;
int score;
score = -990;
if(!oom::has_oom_score_adj())
return;
orig = oom::get_oom_score_adj();
rv = oom::set_oom_score_adj(score);
score = -990;
(void)rv;
SysLog::info("set oom_score_adj to {}, originally {}",
score,
orig);
}
orig = oom::get_oom_score_adj();
rv = oom::set_oom_score_adj(score);
static
bool
_wait_for_mount()
{
int failures;
std::vector<fs::path> paths;
std::chrono::milliseconds timeout;
(void)rv;
SysLog::info("set oom_score_adj to {}, originally {}",
score,
orig);
}
paths = cfg.branches->to_paths();
static
bool
wait_for_mount(const Config &cfg_)
{
int failures;
std::vector<fs::path> paths;
std::chrono::milliseconds timeout;
paths = cfg_.branches->to_paths();
SysLog::info("Waiting {} seconds for {} branches to mount",
(uint64_t)cfg_.branches_mount_timeout,
paths.size());
timeout = std::chrono::milliseconds(cfg_.branches_mount_timeout * 1000);
failures = fs::wait_for_mount(cfg_.mountpoint,
paths,
timeout);
if(failures)
{
if(cfg_.branches_mount_timeout_fail)
{
SysLog::error("{} of {} branches were not mounted"
" within the timeout of {}s. Exiting",
failures,
paths.size(),
(uint64_t)cfg_.branches_mount_timeout);
return true;
}
SysLog::warning("Continuing to mount mergerfs despite {} branches not "
"being different from the mountpoint filesystem",
failures);
}
else
{
SysLog::info("All {} branches are mounted",
paths.size());
}
return false;
}
static
void
lazy_umount(const fs::path &target_)
{
int rv;
rv = fs::umount_lazy(target_);
switch(rv)
{
case 0:
SysLog::notice("{} has been successfully lazily unmounted",
target_.string());
break;
case -EINVAL:
SysLog::notice("{} was not a mount point needing to be unmounted",
target_.string());
break;
default:
SysLog::error("Error unmounting {}: {} - {}",
target_.string(),
-rv,
strerror(-rv));
break;
}
}
static
void
usr1_signal_handler(int signal_)
{
// SysLog::info("Received SIGUSR1 - invalidating all nodes");
// fuse_invalidate_all_nodes();
}
SysLog::info("Waiting {} seconds for {} branches to mount",
(uint64_t)cfg.branches_mount_timeout,
paths.size());
static
void
usr2_signal_handler(int signal_)
{
// SysLog::info("Received SIGUSR2 - triggering thorough gc");
// fuse_gc();
// GIDCache::clear_all();
}
static
void
setup_signal_handlers()
{
std::signal(SIGUSR1,l::usr1_signal_handler);
std::signal(SIGUSR2,l::usr2_signal_handler);
}
timeout = std::chrono::milliseconds(cfg.branches_mount_timeout * 1000);
failures = fs::wait_for_mount(cfg.mountpoint,
paths,
timeout);
if(failures)
{
if(cfg.branches_mount_timeout_fail)
{
SysLog::error("{} of {} branches were not mounted"
" within the timeout of {}s. Exiting",
failures,
paths.size(),
(uint64_t)cfg.branches_mount_timeout);
return true;
}
SysLog::warning("Continuing to mount mergerfs despite {} branches not "
"being different from the mountpoint filesystem",
failures);
}
else
{
SysLog::info("All {} branches are mounted",
paths.size());
}
static
void
warn_if_not_root()
{
uid_t uid;
return false;
}
uid = geteuid();
if(uid == 0)
return;
static
void
_lazy_umount(const fs::path &target_)
{
int rv;
constexpr const char s[] = "mergerfs is not running as root and may not work correctly\n";
fmt::print(stderr,"warning: {}",s);
SysLog::warning(s);
}
rv = fs::umount_lazy(target_);
switch(rv)
{
case 0:
SysLog::notice("{} has been successfully lazily unmounted",
target_.string());
break;
case -EINVAL:
SysLog::notice("{} was not a mount point needing to be unmounted",
target_.string());
break;
default:
SysLog::error("Error unmounting {}: {} - {}",
target_.string(),
-rv,
strerror(-rv));
break;
}
}
int
main(int argc_,
char **argv_)
{
int rv;
Config::ErrVec errs;
fuse_args args;
fuse_operations ops;
static
void
_usr1_signal_handler(int signal_)
{
// SysLog::info("Received SIGUSR1 - invalidating all nodes");
// fuse_invalidate_all_nodes();
}
static
void
_usr2_signal_handler(int signal_)
{
// SysLog::info("Received SIGUSR2 - triggering thorough gc");
// fuse_gc();
// GIDCache::clear_all();
}
SysLog::open();
static
void
_setup_signal_handlers()
{
std::signal(SIGUSR1,::_usr1_signal_handler);
std::signal(SIGUSR2,::_usr2_signal_handler);
}
memset(&ops,0,sizeof(fuse_operations));
static
void
_warn_if_not_root()
{
uid_t uid;
args.argc = argc_;
args.argv = argv_;
args.allocated = 0;
uid = geteuid();
if(uid == 0)
return;
options::parse(&args,&errs);
if(errs.size())
{
std::cerr << errs << std::endl;
return 1;
}
constexpr const char s[] =
"mergerfs is not running as root"
" and may not work correctly";
fmt::println(stderr,"* WARNING: {}",s);
SysLog::warning(s);
}
int
_main(int argc_,
char **argv_)
{
int rv;
fuse_args args;
fuse_operations ops;
SysLog::open();
memset(&ops,0,sizeof(fuse_operations));
args.argc = argc_;
args.argv = argv_;
args.allocated = false;
if(cfg.branches_mount_timeout > 0)
{
bool failure;
SysLog::info("mergerfs v{} started",MERGERFS_VERSION);
SysLog::info("Go to https://trapexit.github.io/mergerfs/support for support");
failure = l::wait_for_mount(cfg);
if(failure)
return 1;
}
options::parse(&args);
if(!cfg.errs.empty())
{
for(auto &err : cfg.errs)
{
std::string s = err.to_string();
SysLog::error("error: {}",s);
fmt::println(stderr,"* ERROR: {}",s);
}
SysLog::info("mergerfs v{} started",MERGERFS_VERSION);
SysLog::info("Go to https://trapexit.github.io/mergerfs/support for support");
l::warn_if_not_root();
return 1;
}
MaintenanceThread::push_job([](int count_)
if(cfg.branches_mount_timeout > 0)
{
if((count_ % 60) == 0)
GIDCache::clear_unused();
});
l::setup_resources(cfg.scheduling_priority);
l::setup_signal_handlers();
l::set_oom_score_adj();
l::get_fuse_operations(ops,cfg.nullrw);
bool failure;
failure = ::_wait_for_mount();
if(failure)
return 1;
}
::_warn_if_not_root();
MaintenanceThread::push_job([](int count_)
{
if((count_ % 60) == 0)
GIDCache::clear_unused();
});
::_setup_resources(cfg.scheduling_priority);
::_setup_signal_handlers();
::_set_oom_score_adj();
::_get_fuse_operations(ops,cfg.nullrw);
if(cfg.lazy_umount_mountpoint)
l::lazy_umount(cfg.mountpoint);
if(cfg.lazy_umount_mountpoint)
::_lazy_umount(cfg.mountpoint);
rv = fuse_main(args.argc,
args.argv,
&ops);
rv = fuse_main(args.argc,
args.argv,
&ops);
SysLog::info("exiting main loop with return code {}",rv);
SysLog::info("exiting main loop with return code {}",rv);
SysLog::close();
SysLog::close();
return rv;
}
return rv;
}
static
@ -372,7 +380,7 @@ _pick_app_and_run(int argc_,
if(appname == "mergerfs.collect-info")
return mergerfs::collect_info::main(argc_,argv_);
return l::main(argc_,argv_);
return ::_main(argc_,argv_);
}
int

139
src/option_parser.cpp

@ -52,7 +52,6 @@ enum
MERGERFS_OPT_VERSION
};
static
void
_set_option(const std::string &option_,
@ -113,84 +112,28 @@ _set_default_options(fuse_args *args_)
SysLog::notice("not auto setting allow_other since not running as root");
}
static
bool
_should_ignore(const std::string &key_)
{
constexpr const std::array<std::string_view,13> ignored_keys =
{
"atomic_o_trunc",
"big_writes",
"cache.open",
"defaults",
"hard_remove",
"no_splice_move",
"no_splice_read",
"no_splice_write",
"nonempty",
"splice_move",
"splice_read",
"splice_write",
"use_ino",
};
for(const auto &key : ignored_keys)
{
if(key == key_)
return true;
}
return false;
}
static
int
_parse_and_process_kv_arg(Config::ErrVec *errs_,
const std::string &key_,
_parse_and_process_kv_arg(const std::string &key_,
const std::string &val_)
{
int rv;
std::string key(key_);
std::string val(val_);
rv = 0;
if(key == "config")
return ((cfg.from_file(val_,errs_) < 0) ? 1 : 0);
ef(key == "attr_timeout")
key = "cache.attr";
ef(key == "entry_timeout")
key = "cache.entry";
ef(key == "negative_entry")
key = "cache.negative_entry";
ef(key == "direct_io" && val.empty())
val = "true";
ef(key == "kernel_cache" && val.empty())
val = "true";
ef(key == "auto_cache" && val.empty())
val = "true";
ef(key == "async_read" && val.empty())
val = "true";
ef(key == "sync_read" && val.empty())
{key = "async_read", val = "false";}
ef(key == "noforget" && val.empty())
{key = "remember-nodes", val = "-1";}
ef(::_should_ignore(key_))
return 0;
if(cfg.has_key(key) == false)
return 1;
rv = cfg.set(key,val);
if(rv)
errs_->push_back({rv,key+'='+val});
if(rv < 0)
cfg.errs.push_back({-rv,key+'='+val});
return 0;
}
static
int
_process_opt(Config::ErrVec *errs_,
const std::string &arg_)
_process_opt(const std::string &arg_)
{
std::string key;
std::string val;
@ -199,68 +142,39 @@ _process_opt(Config::ErrVec *errs_,
key = str::trim(key);
val = str::trim(val);
return ::_parse_and_process_kv_arg(errs_,key,val);
return ::_parse_and_process_kv_arg(key,val);
}
static
int
_process_branches(Config::ErrVec *errs_,
const char *arg_)
_process_branches(const char *arg_)
{
int rv;
std::string arg;
arg = arg_;
rv = cfg.set("branches",arg);
if(rv)
errs_->push_back({rv,"branches="+arg});
if(rv < 0)
cfg.errs.push_back({-rv,"branches="+arg});
return 0;
}
static
int
_process_mount(Config::ErrVec *errs_,
const char *arg_)
_process_mount(const char *arg_)
{
int rv;
std::string arg;
arg = arg_;
rv = cfg.set("mount",arg);
if(rv)
errs_->push_back({rv,"mount="+arg});
if(rv < 0)
cfg.errs.push_back({-rv,"mount="+arg});
return 1;
}
static
void
_postprocess_passthrough()
{
if(cfg.passthrough == Passthrough::ENUM::OFF)
return;
if(cfg.cache_files == CacheFiles::ENUM::OFF)
{
SysLog::warning("'cache.files' can not be 'off' when using 'passthrough'."
" Setting 'cache.files=full'");
cfg.cache_files = CacheFiles::ENUM::FULL;
}
if(cfg.cache_writeback == true)
{
SysLog::warning("'cache.writeback' can not be enabled when using 'passthrough'."
" Setting 'cache.writeback=false'");
cfg.cache_writeback = false;
}
if(cfg.moveonenospc.enabled == true)
{
SysLog::warning("`moveonenospc` will not function when `passthrough` is enabled");
}
}
static
void
_usage(void)
@ -277,18 +191,18 @@ _option_processor(void *data_,
int key_,
fuse_args *outargs_)
{
Config::ErrVec *errs = (Config::ErrVec*)data_;
(void)data_;
switch(key_)
{
case FUSE_OPT_KEY_OPT:
return ::_process_opt(errs,arg_);
return ::_process_opt(arg_);
case FUSE_OPT_KEY_NONOPT:
if(cfg.branches->empty())
return ::_process_branches(errs,arg_);
return ::_process_branches(arg_);
else
return ::_process_mount(errs,arg_);
return ::_process_mount(arg_);
case MERGERFS_OPT_HELP:
::_usage();
@ -326,7 +240,7 @@ _option_processor(void *data_,
static
void
_check_for_mount_loop(Config::ErrVec *errs_)
_check_for_mount_loop()
{
fs::path mount;
std::vector<fs::path> branches;
@ -342,14 +256,14 @@ _check_for_mount_loop(Config::ErrVec *errs_)
errstr = fmt::format("branches can not include the mountpoint: {}",
branch.string());
errs_->push_back({0,errstr});
cfg.errs.push_back({0,errstr});
}
}
}
static
void
_print_warnings()
_postprocess_and_print_warnings()
{
if(fuse_cfg.uid != FUSE_CFG_INVALID_ID)
SysLog::warning("overwriting 'uid' is untested and unsupported,"
@ -379,7 +293,7 @@ _print_warnings()
if(cfg.moveonenospc.enabled == true)
{
SysLog::warning("`moveonenospc` will not function when `passthrough` is enabled");
SysLog::warning("'moveonenospc' will not function when 'passthrough' is enabled");
}
}
}
@ -395,8 +309,8 @@ _cleanup_options()
namespace options
{
void
parse(fuse_args *args_,
Config::ErrVec *errs_)
parse(fuse_args *args_)
{
const struct fuse_opt opts[] =
{
@ -409,21 +323,20 @@ namespace options
};
fuse_opt_parse(args_,
errs_,
NULL,
opts,
::_option_processor);
if(cfg.branches->empty())
errs_->push_back({0,"branches not set"});
cfg.errs.push_back({0,"branches not set"});
if(cfg.mountpoint->empty())
errs_->push_back({0,"mountpoint not set"});
cfg.errs.push_back({0,"mountpoint not set"});
::_postprocess_passthrough();
::_check_for_mount_loop(errs_);
::_postprocess_and_print_warnings();
::_check_for_mount_loop();
::_set_default_options(args_);
::_set_fsname(args_);
::_set_subtype(args_);
::_print_warnings();
::_cleanup_options();
cfg.finish_initializing();

5
src/option_parser.hpp

@ -16,14 +16,11 @@
#pragma once
#include "config.hpp"
#include "fuse.h"
namespace options
{
void
parse(fuse_args *args,
Config::ErrVec *errs);
parse(fuse_args *args);
}
Loading…
Cancel
Save