From 6d77b2ec97fcd4ce3f2b1d7b366a0148da751fb9 Mon Sep 17 00:00:00 2001 From: Antonio SJ Musumeci Date: Sun, 19 Oct 2025 16:48:19 -0500 Subject: [PATCH] option_parser.cpp --- mkdocs/docs/config/deprecated_options.md | 30 +- src/config.cpp | 345 ++++++++-------- src/config.hpp | 40 +- src/config_cachefiles.cpp | 6 +- src/config_cachefiles.hpp | 1 - src/config_dummy.hpp | 24 ++ src/config_noforget.hpp | 40 ++ src/fuse_create.cpp | 5 - src/fuse_init.cpp | 2 +- src/fuse_open.cpp | 5 - src/mergerfs.cpp | 482 ++++++++++++----------- src/option_parser.cpp | 139 ++----- src/option_parser.hpp | 5 +- 13 files changed, 557 insertions(+), 567 deletions(-) create mode 100644 src/config_dummy.hpp create mode 100644 src/config_noforget.hpp diff --git a/mkdocs/docs/config/deprecated_options.md b/mkdocs/docs/config/deprecated_options.md index 69b6b872..24759672 100644 --- a/mkdocs/docs/config/deprecated_options.md +++ b/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`. diff --git a/src/config.cpp b/src/config.cpp index ae9039e3..dd808a9d 100644 --- a/src/config.cpp +++ b/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"] = ¶llel_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"] = ¶llel_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); } diff --git a/src/config.hpp b/src/config.hpp index ee171bb6..31ea239d 100644 --- a/src/config.hpp +++ b/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 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 congestion_threshold; TFSRef 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 read_thread_count; ConfigUINT64 readahead; FUSE::ReadDir readdir; - ConfigBOOL readdirplus; TFSRef 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); diff --git a/src/config_cachefiles.cpp b/src/config_cachefiles.cpp index dd574287..ce660c95 100644 --- a/src/config_cachefiles.cpp +++ b/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; diff --git a/src/config_cachefiles.hpp b/src/config_cachefiles.hpp index 5bb23ea4..848ebdff 100644 --- a/src/config_cachefiles.hpp +++ b/src/config_cachefiles.hpp @@ -23,7 +23,6 @@ enum class CacheFilesEnum { - LIBFUSE, OFF, PARTIAL, FULL, diff --git a/src/config_dummy.hpp b/src/config_dummy.hpp new file mode 100644 index 00000000..67bdafe2 --- /dev/null +++ b/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; + } +}; diff --git a/src/config_noforget.hpp b/src/config_noforget.hpp new file mode 100644 index 00000000..9e10fe46 --- /dev/null +++ b/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"; + } +}; diff --git a/src/fuse_create.cpp b/src/fuse_create.cpp index 998c1746..7a9d63f3 100644 --- a/src/fuse_create.cpp +++ b/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; diff --git a/src/fuse_init.cpp b/src/fuse_init.cpp index 3c7bedb1..071631ab 100644 --- a/src/fuse_init.cpp +++ b/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); diff --git a/src/fuse_open.cpp b/src/fuse_open.cpp index 61345b90..b4c8988b 100644 --- a/src/fuse_open.cpp +++ b/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; diff --git a/src/mergerfs.cpp b/src/mergerfs.cpp index fbf4a32b..63d6c146 100644 --- a/src/mergerfs.cpp +++ b/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 #include #include +#include -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 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 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 diff --git a/src/option_parser.cpp b/src/option_parser.cpp index ee21fd6c..5a0776b7 100644 --- a/src/option_parser.cpp +++ b/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 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 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(); diff --git a/src/option_parser.hpp b/src/option_parser.hpp index 1e9c4122..4646250d 100644 --- a/src/option_parser.hpp +++ b/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); }