diff --git a/.gitignore b/.gitignore index 7590ea28..17348aa0 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,6 @@ rpmbuild/ # mkdocs files mkdocs/venv __pycache__/ + +# Claude Code session state +.claude/ diff --git a/src/branch.hpp b/src/branch.hpp index 522c45d7..e9ad4776 100644 --- a/src/branch.hpp +++ b/src/branch.hpp @@ -20,7 +20,6 @@ #include "base_types.h" #include "strvec.hpp" -#include "fs_path.hpp" #include #include @@ -43,7 +42,7 @@ public: public: std::variant _minfreespace; Mode mode; - fs::path path; + std::string path; public: Branch(); diff --git a/src/branches.cpp b/src/branches.cpp index bc942be0..c067d5ee 100644 --- a/src/branches.cpp +++ b/src/branches.cpp @@ -33,6 +33,9 @@ #include #include +#include + +#include #include @@ -225,13 +228,24 @@ namespace l fs::realpathize(&paths); for(auto &path : paths) { - std::error_code ec; + struct stat st; + int rv; - if(!std::filesystem::exists(path,ec)) + rv = ::stat(path.c_str(),&st); + if(rv < 0) { - SysLog::notice("branch `{}` does not currently exist",path); + // Distinguish "doesn't exist" (the common case where the + // backing storage isn't mounted yet) from harder errors + // like permission, name-too-long, ELOOP, etc., which + // suggest a config problem rather than transient absence. + if(errno == ENOENT) + SysLog::notice("branch `{}` does not currently exist",path); + else + SysLog::warning("branch `{}` could not be stat'd: {}", + path, + strerror(errno)); } - else if(!std::filesystem::is_directory(path,ec)) + else if(!S_ISDIR(st.st_mode)) { SysLog::warning("branch `{}` is not a directory, skipping",path); continue; @@ -417,14 +431,14 @@ Branches::Impl::to_paths(StrVec &paths_) const { for(auto &branch : *this) { - paths_.push_back(branch.path); + paths_.emplace_back(branch.path); } } -std::vector +std::vector Branches::Impl::to_paths() const { - std::vector vp; + std::vector vp; for(const auto &branch : *this) vp.emplace_back(branch.path); @@ -484,7 +498,7 @@ Branches::find_and_set_mode_ro() { Branches::Ptr impl; Branches::Ptr new_impl; - std::vector ro_paths; + std::vector ro_paths; new_impl = std::make_shared(&minfreespace); @@ -524,7 +538,7 @@ Branches::find_and_set_mode_ro() for(const auto &path : ro_paths) SysLog::warning("branch `{}` found to be readonly - setting its mode=RO", - path.string()); + path); return; } diff --git a/src/branches.hpp b/src/branches.hpp index dae01822..84aa7663 100644 --- a/src/branches.hpp +++ b/src/branches.hpp @@ -19,7 +19,6 @@ #pragma once #include "branch.hpp" -#include "fs_path.hpp" #include "strvec.hpp" #include "tofrom_string.hpp" @@ -54,7 +53,7 @@ public: public: const u64 &minfreespace(void) const; void to_paths(StrVec &strvec) const; - std::vector to_paths() const; + std::vector to_paths() const; public: Impl& operator=(Impl &impl_); diff --git a/src/config.cpp b/src/config.cpp index 2194c1dc..1291bdcd 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -46,16 +46,16 @@ int Config::CfgConfigFile::from_string(const std::string_view s_) { int rv; - fs::path cfg_file; + std::string cfg_file; if(_depth > 5) return -ELOOP; _depth++; struct DepthGuard { int &d; ~DepthGuard() { --d; } } guard{_depth}; - cfg_file = (s_.empty() ? _cfg_file : s_); + cfg_file = (s_.empty() ? std::string_view(_cfg_file) : s_); - rv = cfg.from_file(cfg_file); + rv = cfg.from_file(cfg_file.c_str()); if(rv == 0) _cfg_file = cfg_file; @@ -426,7 +426,7 @@ Config::from_stream(std::istream &istrm_) } int -Config::from_file(const std::string &filepath_) +Config::from_file(const char *filepath_) { std::ifstream ifstrm; @@ -449,13 +449,13 @@ Config::finish_initializing() } bool -Config::is_rootdir(const fs::path &fusepath_) +Config::is_rootdir(const fs::relpath &fusepath_) { return fusepath_.empty(); } bool -Config::is_ctrl_file(const fs::path &fusepath_) +Config::is_ctrl_file(const fs::relpath &fusepath_) { return (fusepath_ == ".mergerfs"); } diff --git a/src/config.hpp b/src/config.hpp index 0005a2e3..70e10ed8 100644 --- a/src/config.hpp +++ b/src/config.hpp @@ -67,7 +67,7 @@ typedef ToFromWrapper ConfigU64; typedef ToFromWrapper ConfigS64; typedef ToFromWrapper ConfigINT; typedef ToFromWrapper ConfigSTR; -typedef ToFromWrapper ConfigPath; +typedef ToFromWrapper ConfigPath; typedef std::map Str2TFStrMap; typedef ROToFromWrapper ConfigROSTR; @@ -92,7 +92,7 @@ public: class CfgConfigFile : public ToFromString { private: - fs::path _cfg_file; + std::string _cfg_file; static thread_local int _depth; public: @@ -141,7 +141,7 @@ public: LinkEXDEV link_exdev; LogFile log_file; TFSRef minfreespace; - fs::path mountpoint; + std::string mountpoint; MoveOnENOSPC moveonenospc; NFSOpenHack nfsopenhack; ConfigBOOL nullrw; @@ -172,8 +172,8 @@ private: CfgDummy _dummy; TFSRef _gid; TFSRef _max_background; - TFSRef _mount; - TFSRef _mountpoint; + TFSRef _mount; + TFSRef _mountpoint; CfgNoforget _never_forget_nodes; CfgNoforget _noforget; CfgNoforget _remember; @@ -207,11 +207,11 @@ public: public: int from_stream(std::istream &istrm); - int from_file(const std::string &filepath); + int from_file(const char *filepath); public: - static bool is_rootdir(const fs::path &fusepath); - static bool is_ctrl_file(const fs::path &fusepath); + static bool is_rootdir(const fs::relpath &fusepath); + static bool is_ctrl_file(const fs::relpath &fusepath); static bool is_mergerfs_xattr(const char *attrname); static bool is_cmd_xattr(const std::string_view &attrname); static std::string_view prune_ctrl_xattr(const std::string_view s); diff --git a/src/dirinfo.hpp b/src/dirinfo.hpp index 2355b4a5..23676f07 100644 --- a/src/dirinfo.hpp +++ b/src/dirinfo.hpp @@ -34,7 +34,7 @@ public: u64 to_fh() const; public: - DirInfo(const fs::path &fusepath_) + DirInfo(const fs::relpath &fusepath_) : FH(fusepath_) { } diff --git a/src/fh.hpp b/src/fh.hpp index 5d7fa486..66224cc9 100644 --- a/src/fh.hpp +++ b/src/fh.hpp @@ -24,12 +24,12 @@ class FH { public: - FH(const fs::path &fusepath_) + FH(const fs::relpath &fusepath_) : fusepath(fusepath_) { } public: - fs::path fusepath; + fs::relpath fusepath; }; diff --git a/src/fileinfo.hpp b/src/fileinfo.hpp index f3524205..a8369856 100644 --- a/src/fileinfo.hpp +++ b/src/fileinfo.hpp @@ -62,7 +62,7 @@ public: public: FileInfo(const int fd_, const Branch *branch_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, const bool direct_io_) : FH(fusepath_), fd(fd_), @@ -73,7 +73,7 @@ public: FileInfo(const int fd_, const Branch &branch_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, const bool direct_io_) : FH(fusepath_), fd(fd_), diff --git a/src/from_string.cpp b/src/from_string.cpp index bab0d4d6..7df38959 100644 --- a/src/from_string.cpp +++ b/src/from_string.cpp @@ -206,12 +206,3 @@ str::from(const std::string_view value_, { return -EINVAL; } - -int -str::from(const std::string_view value_, - fs::path *path_) -{ - *path_ = value_; - - return 0; -} diff --git a/src/from_string.hpp b/src/from_string.hpp index fc39db98..57cc487c 100644 --- a/src/from_string.hpp +++ b/src/from_string.hpp @@ -19,7 +19,6 @@ #pragma once #include "base_types.h" -#include "fs_path.hpp" #include #include @@ -33,5 +32,4 @@ namespace str int from(const std::string_view, s64 *); int from(const std::string_view, std::string *); int from(const std::string_view, const std::string *); - int from(const std::string_view, fs::path *); } diff --git a/src/fs_acl.cpp b/src/fs_acl.cpp index 5f7ef8aa..6cddccf0 100644 --- a/src/fs_acl.cpp +++ b/src/fs_acl.cpp @@ -21,16 +21,15 @@ #include "fs_lgetxattr.hpp" #include "fs_path.hpp" -#include constexpr const char POSIX_ACL_DEFAULT_XATTR[] = "system.posix_acl_default"; bool -fs::acl::dir_has_defaults(const fs::path &fullpath_) +fs::acl::dir_has_defaults(const fs::relpath &fullpath_) { int rv; - fs::path dirpath; + fs::relpath dirpath; dirpath = fullpath_.parent_path(); diff --git a/src/fs_acl.hpp b/src/fs_acl.hpp index ff204287..9eb4148a 100644 --- a/src/fs_acl.hpp +++ b/src/fs_acl.hpp @@ -26,6 +26,6 @@ namespace fs namespace acl { bool - dir_has_defaults(const fs::path &fullpath); + dir_has_defaults(const fs::relpath &fullpath); } } diff --git a/src/fs_attr_linux.icpp b/src/fs_attr_linux.icpp index baa06b72..266abf7e 100644 --- a/src/fs_attr_linux.icpp +++ b/src/fs_attr_linux.icpp @@ -44,8 +44,8 @@ _get_fs_ioc_flags(const int fd, static int -_get_fs_ioc_flags(const string &file, - int &flags) +_get_fs_ioc_flags(const char *file, + int &flags) { int fd; int rv; @@ -81,8 +81,8 @@ _set_fs_ioc_flags(const int fd, static int -_set_fs_ioc_flags(const string &file, - const int flags) +_set_fs_ioc_flags(const char *file, + const int flags) { int fd; int rv; @@ -121,15 +121,15 @@ fs::attr::copy(const int fdin, } int -fs::attr::copy(const string &from, - const string &to) +fs::attr::copy(const std::string &from_, + const std::string &to_) { int rv; int flags; - rv = ::_get_fs_ioc_flags(from,flags); + rv = ::_get_fs_ioc_flags(from_.c_str(),flags); if(rv < 0) return rv; - return ::_set_fs_ioc_flags(to,flags); + return ::_set_fs_ioc_flags(to_.c_str(),flags); } diff --git a/src/fs_path.cpp b/src/fs_cleanpath.cpp similarity index 59% rename from src/fs_path.cpp rename to src/fs_cleanpath.cpp index 8c395119..6d17ccfd 100644 --- a/src/fs_path.cpp +++ b/src/fs_cleanpath.cpp @@ -16,4 +16,37 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include "fs_path.hpp" +#include "fs_cleanpath.hpp" + +#include + + +void +fs::cleanpath(std::string *path_) +{ + std::string &s = *path_; + std::size_t w = 0; + bool prev_slash = false; + + for(std::size_t r = 0; r < s.size(); r++) + { + const char c = s[r]; + if(c == '/') + { + if(prev_slash) + continue; + prev_slash = true; + } + else + { + prev_slash = false; + } + s[w++] = c; + } + + // Strip trailing '/' but leave a bare "/" intact. + if(w > 1 && s[w - 1] == '/') + w--; + + s.resize(w); +} diff --git a/src/fs_path.hpp b/src/fs_cleanpath.hpp similarity index 93% rename from src/fs_path.hpp rename to src/fs_cleanpath.hpp index e840911e..f6da8304 100644 --- a/src/fs_path.hpp +++ b/src/fs_cleanpath.hpp @@ -18,10 +18,10 @@ #pragma once -#include +#include namespace fs { - using path = std::filesystem::path; + void cleanpath(std::string *path); } diff --git a/src/fs_clonepath.cpp b/src/fs_clonepath.cpp index 1b7b4793..2e06ebc5 100644 --- a/src/fs_clonepath.cpp +++ b/src/fs_clonepath.cpp @@ -61,60 +61,84 @@ _ignorable_error(const int err_) The directories which already exist are left alone. The new directories have metadata set to match the original if possible. Optionally ignore errors on metadata copies. + + Iterative root-to-leaf walk. Stack usage is O(1) in component count. */ int -fs::clonepath(const fs::path &srcpath_, - const fs::path &dstpath_, - const fs::path &relpath_, - const bool return_metadata_errors_) +fs::clonepath(const std::string_view srcpath_, + const std::string_view dstpath_, + const fs::relpath &relpath_, + const bool return_metadata_errors_) { - int rv; - struct stat st; - fs::path dstpath; - fs::path srcpath; - fs::path dirname; - if(relpath_.empty()) return 0; - dirname = relpath_.parent_path(); - if(!dirname.empty()) + std::string srcpath; + std::string dstpath; + srcpath.reserve(srcpath_.size() + relpath_.size() + 1); + dstpath.reserve(dstpath_.size() + relpath_.size() + 1); + srcpath = srcpath_; + dstpath = dstpath_; + + std::string_view rel = relpath_.native(); + std::size_t pos = 0; + while(pos < rel.size()) { - rv = fs::clonepath(srcpath_,dstpath_,dirname,return_metadata_errors_); + std::size_t next = rel.find('/',pos); + if(next == std::string_view::npos) + next = rel.size(); + + std::string_view component = rel.substr(pos,next - pos); + pos = next + 1; + // Defensive: canonical rel form should not produce empty + // segments, but skip them if the input is malformed (e.g. a + // double slash) rather than appending nothing and re-stat'ing + // the same path. + if(component.empty()) + continue; + + srcpath += '/'; + srcpath.append(component); + dstpath += '/'; + dstpath.append(component); + + int rv; + struct stat st; + + rv = fs::lstat(srcpath,&st); if(rv < 0) return rv; + if(!S_ISDIR(st.st_mode)) + return -ENOTDIR; + + rv = fs::mkdir(dstpath,st.st_mode); + if(rv < 0 && rv != -EEXIST) + return rv; + // EEXIST: directory already there (could be from a prior clone + // or a race). Leave it alone, do NOT re-copy metadata, matching + // the recursive version's behavior when its mkdir hit EEXIST. + if(rv == -EEXIST) + continue; + + // it may not support it... it's fine... + rv = fs::attr::copy(srcpath,dstpath); + if(return_metadata_errors_ && (rv < 0) && !::_ignorable_error(-rv)) + return rv; + + // it may not support it... it's fine... + rv = fs::xattr::copy(srcpath,dstpath); + if(return_metadata_errors_ && (rv < 0) && !::_ignorable_error(-rv)) + return rv; + + rv = fs::lchown_check_on_error(dstpath,st); + if(return_metadata_errors_ && (rv < 0)) + return rv; + + rv = fs::lutimens(dstpath,st); + if(return_metadata_errors_ && (rv < 0)) + return rv; } - srcpath = srcpath_ / relpath_; - rv = fs::lstat(srcpath,&st); - if(rv < 0) - return rv; - else if(!S_ISDIR(st.st_mode)) - return -ENOTDIR; - - dstpath = dstpath_ / relpath_; - rv = fs::mkdir(dstpath,st.st_mode); - if(rv < 0) - return ((rv == -EEXIST) ? 0 : rv); - - // it may not support it... it's fine... - rv = fs::attr::copy(srcpath,dstpath); - if(return_metadata_errors_ && (rv < 0) && !::_ignorable_error(-rv)) - return rv; - - // it may not support it... it's fine... - rv = fs::xattr::copy(srcpath,dstpath); - if(return_metadata_errors_ && (rv < 0) && !::_ignorable_error(-rv)) - return rv; - - rv = fs::lchown_check_on_error(dstpath,st); - if(return_metadata_errors_ && (rv < 0)) - return rv; - - rv = fs::lutimens(dstpath,st); - if(return_metadata_errors_ && (rv < 0)) - return rv; - return 0; } @@ -122,10 +146,10 @@ fs::clonepath(const fs::path &srcpath_, // WORK IN PROGRESS static int -_clonepath2(const int srcfd_, - const int dstfd_, - const fs::path &dirname_, - const bool return_metadata_errors_) +_clonepath2(const int srcfd_, + const int dstfd_, + const fs::relpath &dirname_, + const bool return_metadata_errors_) { int rv; int srcdirfd; diff --git a/src/fs_clonepath.hpp b/src/fs_clonepath.hpp index feff3dce..e1accd20 100644 --- a/src/fs_clonepath.hpp +++ b/src/fs_clonepath.hpp @@ -20,11 +20,16 @@ #include "fs_path.hpp" +#include + namespace fs { - int clonepath(const fs::path &srcpath, - const fs::path &dstpath, - const fs::path &relpath, - const bool return_metadata_errors = false); + // srcpath / dstpath are absolute base paths; relpath is the rel-form + // chain to walk and create. Accepts string_view so an fs::relpath + // holding an absolute fullpath also works without conversion. + int clonepath(const std::string_view srcpath, + const std::string_view dstpath, + const fs::relpath &relpath, + const bool return_metadata_errors = false); } diff --git a/src/fs_copyfile.cpp b/src/fs_copyfile.cpp index c8ea3dd8..7ae7daf9 100644 --- a/src/fs_copyfile.cpp +++ b/src/fs_copyfile.cpp @@ -98,7 +98,7 @@ fs::copyfile(const int src_fd_, s64 fs::copyfile(const int src_fd_, - const fs::path &dst_filepath_, + const char *dst_filepath_, const fs::CopyFileFlags &flags_) { s64 rv; @@ -155,7 +155,7 @@ fs::copyfile(const int src_fd_, continue; } - rv = fs::rename(dst_tmppath,dst_filepath_); + rv = fs::rename(dst_tmppath.c_str(),dst_filepath_); if((rv < 0) && (flags_.cleanup_failure)) fs::unlink(dst_tmppath); break; @@ -170,8 +170,8 @@ fs::copyfile(const int src_fd_, #endif s64 -fs::copyfile(const fs::path &src_filepath_, - const fs::path &dst_filepath_, +fs::copyfile(const char *src_filepath_, + const char *dst_filepath_, const fs::CopyFileFlags &flags_) { int src_fd; diff --git a/src/fs_copyfile.hpp b/src/fs_copyfile.hpp index 5cf25b6a..30f05014 100644 --- a/src/fs_copyfile.hpp +++ b/src/fs_copyfile.hpp @@ -19,7 +19,8 @@ #pragma once #include "base_types.h" -#include "fs_path.hpp" + +#include #include @@ -40,11 +41,21 @@ namespace fs s64 copyfile(const int src_fd, - const fs::path &dst_filepath, + const char *dst_filepath, const CopyFileFlags &flags); s64 - copyfile(const fs::path &src_filepath, - const fs::path &dst_filepath, + copyfile(const char *src_filepath, + const char *dst_filepath, const CopyFileFlags &flags); + + static + inline + s64 + copyfile(const std::string &src_filepath, + const std::string &dst_filepath, + const CopyFileFlags &flags) + { + return fs::copyfile(src_filepath.c_str(),dst_filepath.c_str(),flags); + } } diff --git a/src/fs_cow.cpp b/src/fs_cow.cpp index 18aafea8..d106e28b 100644 --- a/src/fs_cow.cpp +++ b/src/fs_cow.cpp @@ -53,8 +53,8 @@ fs::cow::is_eligible(const int flags_, } bool -fs::cow::is_eligible(const fs::path &fullpath_, - const int flags_) +fs::cow::is_eligible(const std::string &fullpath_, + const int flags_) { int rv; struct stat st; @@ -70,7 +70,7 @@ fs::cow::is_eligible(const fs::path &fullpath_, } int -fs::cow::break_link(const fs::path &src_filepath_) +fs::cow::break_link(const std::string &src_filepath_) { return fs::copyfile(src_filepath_, src_filepath_, diff --git a/src/fs_cow.hpp b/src/fs_cow.hpp index e0e1a92f..b4832a9b 100644 --- a/src/fs_cow.hpp +++ b/src/fs_cow.hpp @@ -18,8 +18,7 @@ #pragma once -#include "fs_path.hpp" - +#include #include #include @@ -32,8 +31,8 @@ namespace fs bool is_eligible(const struct stat &st); bool is_eligible(const int flags, const struct stat &st); - bool is_eligible(const fs::path &fullpath, const int flags); + bool is_eligible(const std::string &fullpath, const int flags); - int break_link(const fs::path &fullpath); + int break_link(const std::string &fullpath); } } diff --git a/src/fs_eaccess.hpp b/src/fs_eaccess.hpp index 60da834e..e268fbb2 100644 --- a/src/fs_eaccess.hpp +++ b/src/fs_eaccess.hpp @@ -44,7 +44,7 @@ namespace fs static inline int - eaccess(const fs::path &path_, + eaccess(const fs::relpath &path_, const int mode_) { return fs::eaccess(path_.c_str(),mode_); diff --git a/src/fs_exists.hpp b/src/fs_exists.hpp index 25553a99..76c46145 100644 --- a/src/fs_exists.hpp +++ b/src/fs_exists.hpp @@ -27,8 +27,8 @@ namespace fs static inline bool - exists(const fs::path &path_, - struct stat *st_) + exists(const char *path_, + struct stat *st_) { int rv; @@ -40,7 +40,7 @@ namespace fs static inline bool - exists(const fs::path &path_) + exists(const char *path_) { struct stat st; @@ -50,13 +50,49 @@ namespace fs static inline bool - exists(const fs::path &basepath_, - const char *relpath_, - struct stat *st_) + exists(const std::string &path_, + struct stat *st_) { - fs::path fullpath; + return fs::exists(path_.c_str(),st_); + } - fullpath = basepath_ / relpath_; + static + inline + bool + exists(const std::string &path_) + { + return fs::exists(path_.c_str()); + } + + static + inline + bool + exists(const fs::relpath &path_, + struct stat *st_) + { + return fs::exists(path_.c_str(),st_); + } + + static + inline + bool + exists(const fs::relpath &path_) + { + return fs::exists(path_.c_str()); + } + + // Two-arg helpers for the per-branch loop pattern: build + // basepath/'/'/relpath into an fs::relpath and stat it. + static + inline + bool + exists(const std::string &basepath_, + const fs::relpath &relpath_, + struct stat *st_) + { + fs::relpath fullpath; + + fullpath.assign_concat(basepath_,relpath_); return fs::exists(fullpath,st_); } @@ -64,36 +100,37 @@ namespace fs static inline bool - exists(const fs::path &basepath_, - const fs::path &relpath_, - struct stat *st_) - { - fs::path fullpath; - - fullpath = basepath_ / relpath_; - - return fs::exists(fullpath,st_); - } - - static - inline - bool - exists(const fs::path &basepath_, - const char *relpath_) + exists(const std::string &basepath_, + const fs::relpath &relpath_) { struct stat st; return fs::exists(basepath_,relpath_,&st); } + // Hot-path variant for per-branch fan-out loops. The caller builds + // the fullpath once (with the rel portion set) outside the loop, and + // each iteration only rewrites the prefix area via set_prefix -- + // skipping the per-iteration rel memmove that the (basepath, + // relpath) overload incurs. static inline bool - exists(const fs::path &basepath_, - const fs::path &relpath_) + exists(fs::relpath &fullpath_, + const std::string &basepath_, + struct stat *st_) { - struct stat st; + fullpath_.set_prefix(basepath_); + return fs::exists(fullpath_.c_str(),st_); + } - return fs::exists(basepath_,relpath_,&st); + static + inline + bool + exists(fs::relpath &fullpath_, + const std::string &basepath_) + { + fullpath_.set_prefix(basepath_); + return fs::exists(fullpath_.c_str()); } } diff --git a/src/fs_faccessat.hpp b/src/fs_faccessat.hpp index a57716ae..b3282164 100644 --- a/src/fs_faccessat.hpp +++ b/src/fs_faccessat.hpp @@ -65,7 +65,7 @@ namespace fs inline int faccessat(const int dirfd_, - const fs::path &path_, + const fs::relpath &path_, const int mode_, const int flags_) { diff --git a/src/fs_fchmodat.hpp b/src/fs_fchmodat.hpp index 9be6b371..a0e5a7e5 100644 --- a/src/fs_fchmodat.hpp +++ b/src/fs_fchmodat.hpp @@ -67,7 +67,7 @@ namespace fs inline int fchmodat(const int dirfd_, - const fs::path &pathname_, + const fs::relpath &pathname_, const mode_t mode_, const int flags_) { diff --git a/src/fs_fgetxattr.hpp b/src/fs_fgetxattr.hpp index 7bd82717..7f3219f7 100644 --- a/src/fs_fgetxattr.hpp +++ b/src/fs_fgetxattr.hpp @@ -21,7 +21,6 @@ #include "to_neg_errno.hpp" #include "xattr.hpp" -#include #include #include diff --git a/src/fs_findallfiles.cpp b/src/fs_findallfiles.cpp index 3a9587f5..099a3fce 100644 --- a/src/fs_findallfiles.cpp +++ b/src/fs_findallfiles.cpp @@ -27,18 +27,18 @@ void fs::findallfiles(const std::vector &basepaths_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector *paths_) { - fs::path fullpath; + fs::relpath fullpath = fusepath_; for(const auto &basepath : basepaths_) { - fullpath = basepath / fusepath_; + fullpath.set_prefix(basepath); if(!fs::exists(fullpath)) continue; - paths_->push_back(fullpath); + paths_->emplace_back(fullpath.native()); } } diff --git a/src/fs_findallfiles.hpp b/src/fs_findallfiles.hpp index 40a83433..1c99d6d5 100644 --- a/src/fs_findallfiles.hpp +++ b/src/fs_findallfiles.hpp @@ -28,6 +28,6 @@ namespace fs { void findallfiles(const std::vector &basepaths, - const fs::path &fusepath, + const fs::relpath &fusepath, std::vector *paths); } diff --git a/src/fs_findonfs.cpp b/src/fs_findonfs.cpp index 6f2b0538..3e25c7d1 100644 --- a/src/fs_findonfs.cpp +++ b/src/fs_findonfs.cpp @@ -30,14 +30,14 @@ static int _findonfs(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, const int fd_, std::string *basepath_) { int rv; dev_t dev; struct stat st; - fs::path fullpath; + fs::relpath fullpath = fusepath_; rv = fs::fstat(fd_,&st); if(rv < 0) @@ -46,7 +46,7 @@ _findonfs(const Branches::Ptr &branches_, dev = st.st_dev; for(const auto &branch : *branches_) { - fullpath = branch.path / fusepath_; + fullpath.set_prefix(branch.path); rv = fs::lstat(fullpath,&st); if(rv < 0) @@ -70,5 +70,15 @@ fs::findonfs(const Branches::Ptr &branches_, const int fd_, std::string *basepath_) { - return ::_findonfs(branches_,fusepath_,fd_,basepath_); + // The public API accepts std::string for caller convenience. + // Internally we need a canonical-rel fusepath (no leading '/') so + // the per-branch loop's set_prefix() produces a single separator. + // Strip the leading '/' by hand if present. + std::string_view sv(fusepath_); + if(!sv.empty() && sv.front() == '/') + sv.remove_prefix(1); + + fs::relpath fusepath(sv); + + return ::_findonfs(branches_,fusepath,fd_,basepath_); } diff --git a/src/fs_findonfs.hpp b/src/fs_findonfs.hpp index 56f513da..58fd30e1 100644 --- a/src/fs_findonfs.hpp +++ b/src/fs_findonfs.hpp @@ -25,6 +25,10 @@ namespace fs { + // Public API takes std::string for caller convenience. Internally + // we strip any leading '/' so the per-branch loop's set_prefix() + // produces a single separator; this entry point handles the + // conversion. int findonfs(const Branches::Ptr &branches, const std::string &fusepath, diff --git a/src/fs_fstatat.hpp b/src/fs_fstatat.hpp index dfd7de2d..703bf3fc 100644 --- a/src/fs_fstatat.hpp +++ b/src/fs_fstatat.hpp @@ -48,7 +48,7 @@ namespace fs inline int fstatat(const int dirfd_, - const fs::path &pathname_, + const fs::relpath &pathname_, struct stat *statbuf_, const int flags_) { @@ -75,7 +75,7 @@ namespace fs inline int fstatat_nofollow(const int dirfd_, - const fs::path &pathname_, + const fs::relpath &pathname_, struct stat *statbuf_) { return fs::fstatat(dirfd_, diff --git a/src/fs_futimens_generic.hpp b/src/fs_futimens_generic.hpp index 6f577415..81e03111 100644 --- a/src/fs_futimens_generic.hpp +++ b/src/fs_futimens_generic.hpp @@ -37,9 +37,9 @@ static inline bool -_can_call_lutimes(const int dirfd_, - const std::string &path_, - const int flags_) +_can_call_lutimes(const int dirfd_, + const char *path_, + const int flags_) { return ((flags_ == AT_SYMLINK_NOFOLLOW) && ((dirfd_ == AT_FDCWD) || @@ -117,7 +117,7 @@ static inline int _set_utime_omit_to_current_value(const int dirfd_, - const std::string &path_, + const char *path_, const struct timespec ts_[2], struct timeval tv_[2], const int flags_) @@ -203,7 +203,7 @@ static inline int _convert_timespec_to_timeval(const int dirfd_, - const std::string &path_, + const char *path_, const struct timespec ts_[2], struct timeval tv_[2], struct timeval **tvp_, diff --git a/src/fs_has_space.cpp b/src/fs_has_space.cpp index eb50e61d..e969b468 100644 --- a/src/fs_has_space.cpp +++ b/src/fs_has_space.cpp @@ -21,12 +21,10 @@ #include "fs_statvfs.hpp" #include "statvfs_util.hpp" -#include - bool -fs::has_space(const std::string &str_, - cs64 size_) +fs::has_space(const char *str_, + cs64 size_) { int rv; struct statvfs st; diff --git a/src/fs_has_space.hpp b/src/fs_has_space.hpp index ce00d15a..84ca776b 100644 --- a/src/fs_has_space.hpp +++ b/src/fs_has_space.hpp @@ -26,6 +26,15 @@ namespace fs { bool - has_space(const std::string &str, - cs64 size); + has_space(const char *str, + cs64 size); + + static + inline + bool + has_space(const std::string &path, + cs64 size) + { + return fs::has_space(path.c_str(),size); + } } diff --git a/src/fs_info.cpp b/src/fs_info.cpp index b5bf0d70..399d6542 100644 --- a/src/fs_info.cpp +++ b/src/fs_info.cpp @@ -32,13 +32,13 @@ using std::string; int -fs::info(const string &path_, - fs::info_t *info_) +fs::info(const char *path_, + fs::info_t *info_) { int rv; struct statvfs st; - rv = fs::statvfs_cache(path_.c_str(),&st); + rv = fs::statvfs_cache(path_,&st); if(rv == 0) { info_->readonly = StatVFS::readonly(st); diff --git a/src/fs_info.hpp b/src/fs_info.hpp index 9a96d98e..ae4748f6 100644 --- a/src/fs_info.hpp +++ b/src/fs_info.hpp @@ -26,6 +26,15 @@ namespace fs { int - info(const std::string &path, - fs::info_t *info); + info(const char *path, + fs::info_t *info); + + static + inline + int + info(const std::string &path_, + fs::info_t *info_) + { + return fs::info(path_.c_str(),info_); + } } diff --git a/src/fs_inode.cpp b/src/fs_inode.cpp index e415d3aa..17d79401 100644 --- a/src/fs_inode.cpp +++ b/src/fs_inode.cpp @@ -289,11 +289,11 @@ fs::inode::get_algo(void) return {}; } -fs::inode::ReaddirCalc::ReaddirCalc(const fs::path &branch_path_, - const fs::path &dirpath_) +fs::inode::ReaddirCalc::ReaddirCalc(const std::string &branch_path_, + const fs::relpath &dirpath_) : _algo(::_algo_from_func(g_func.load())), - _branch_seed(::_branch_seed(branch_path_.native())), - _fusepath((dirpath_ / "__mergerfs__").native()), + _branch_seed(::_branch_seed(branch_path_)), + _fusepath(fs::relpath::concat(dirpath_,"__mergerfs__")), _filename_offset(_fusepath.size() - std::string_view("__mergerfs__").size()) { } @@ -337,23 +337,11 @@ fs::inode::ReaddirCalc::calc(const char *name_, u64 fs::inode::calc(const std::string &branch_path_, - const std::string &fusepath_, - const mode_t mode_, - const ino_t ino_) -{ - return g_func.load()(branch_path_, - fusepath_, - mode_, - ino_); -} - -u64 -fs::inode::calc(const fs::path &branch_path_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, const mode_t mode_, const ino_t ino_) { - return g_func.load()(branch_path_.native(), + return g_func.load()(branch_path_, fusepath_.native(), mode_, ino_); @@ -361,18 +349,7 @@ fs::inode::calc(const fs::path &branch_path_, void fs::inode::calc(const std::string &branch_path_, - const std::string &fusepath_, - struct stat *st_) -{ - st_->st_ino = calc(branch_path_, - fusepath_, - st_->st_mode, - st_->st_ino); -} - -void -fs::inode::calc(const fs::path &branch_path_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, struct stat *st_) { st_->st_ino = calc(branch_path_, @@ -382,19 +359,8 @@ fs::inode::calc(const fs::path &branch_path_, } void -fs::inode::calc(const std::string &branch_path_, - const std::string &fusepath_, - struct fuse_statx *st_) -{ - st_->ino = calc(branch_path_, - fusepath_, - st_->mode, - st_->ino); -} - -void -fs::inode::calc(const fs::path &branch_path_, - const fs::path &fusepath_, +fs::inode::calc(const std::string &branch_path_, + const fs::relpath &fusepath_, struct fuse_statx *st_) { st_->ino = calc(branch_path_, diff --git a/src/fs_inode.hpp b/src/fs_inode.hpp index 36ecc0b9..74bfb101 100644 --- a/src/fs_inode.hpp +++ b/src/fs_inode.hpp @@ -47,8 +47,8 @@ namespace fs class ReaddirCalc { public: - ReaddirCalc(const fs::path &branch_path, - const fs::path &dirpath); + ReaddirCalc(const std::string &branch_path, + const fs::relpath &dirpath); public: u64 calc(const char *name, @@ -59,7 +59,7 @@ namespace fs private: Algo _algo; u64 _branch_seed; - std::string _fusepath; + fs::relpath _fusepath; std::size_t _filename_offset; }; @@ -67,25 +67,15 @@ namespace fs std::string get_algo(void); u64 calc(const std::string &basepath, - const std::string &fusepath, - const mode_t mode, - const ino_t ino); - u64 calc(const fs::path &basepath, - const fs::path &fusepath, + const fs::relpath &fusepath, const mode_t mode, const ino_t ino); void calc(const std::string &basepath, - const std::string &fusepath, - struct stat *st); - void calc(const fs::path &basepath, - const fs::path &fusepath, + const fs::relpath &fusepath, struct stat *st); void calc(const std::string &basepath, - const std::string &fusepath, - struct fuse_statx *st); - void calc(const fs::path &basepath, - const fs::path &fusepath, + const fs::relpath &fusepath, struct fuse_statx *st); } } diff --git a/src/fs_is_rofs.hpp b/src/fs_is_rofs.hpp index b8232c7a..8f136175 100644 --- a/src/fs_is_rofs.hpp +++ b/src/fs_is_rofs.hpp @@ -20,11 +20,12 @@ #include "fs_close.hpp" #include "fs_mktemp.hpp" -#include "fs_path.hpp" #include "fs_statvfs.hpp" #include "fs_unlink.hpp" #include "statvfs_util.hpp" +#include + #include @@ -33,7 +34,7 @@ namespace fs static inline bool - is_mounted_rofs(const fs::path &path_) + is_mounted_rofs(const std::string &path_) { int rv; struct statvfs st; @@ -46,7 +47,7 @@ namespace fs static inline bool - is_rofs(const fs::path &path_) + is_rofs(const std::string &path_) { int fd; std::string tmp_filepath; @@ -64,7 +65,7 @@ namespace fs static inline bool - is_rofs_but_not_mounted_ro(const fs::path &path_) + is_rofs_but_not_mounted_ro(const std::string &path_) { if(fs::is_mounted_rofs(path_)) return false; diff --git a/src/fs_lchown.hpp b/src/fs_lchown.hpp index 9ca8e922..a32681a8 100644 --- a/src/fs_lchown.hpp +++ b/src/fs_lchown.hpp @@ -71,7 +71,7 @@ namespace fs static inline int - lchown_check_on_error(const std::string &path_, + lchown_check_on_error(const char *path_, const struct stat &st_) { int rv; @@ -94,4 +94,13 @@ namespace fs return 0; } + + static + inline + int + lchown_check_on_error(const std::string &path_, + const struct stat &st_) + { + return fs::lchown_check_on_error(path_.c_str(),st_); + } } diff --git a/src/fs_link.hpp b/src/fs_link.hpp index 69ddeaed..aa4a8649 100644 --- a/src/fs_link.hpp +++ b/src/fs_link.hpp @@ -30,14 +30,22 @@ namespace fs static inline int - link(const std::string &oldpath_, - const std::string &newpath_) + link(const char *oldpath_, + const char *newpath_) { int rv; - rv = ::link(oldpath_.c_str(), - newpath_.c_str()); + rv = ::link(oldpath_,newpath_); return ::to_neg_errno(rv); } + + static + inline + int + link(const std::string &oldpath_, + const std::string &newpath_) + { + return fs::link(oldpath_.c_str(),newpath_.c_str()); + } } diff --git a/src/fs_lremovexattr.hpp b/src/fs_lremovexattr.hpp index be18416b..abdcef47 100644 --- a/src/fs_lremovexattr.hpp +++ b/src/fs_lremovexattr.hpp @@ -29,17 +29,26 @@ namespace fs static inline int - lremovexattr(const std::string &path_, - const char *attrname_) + lremovexattr(const char *path_, + const char *attrname_) { #ifdef USE_XATTR int rv; - rv = ::lremovexattr(path_.c_str(),attrname_); + rv = ::lremovexattr(path_,attrname_); return ::to_neg_errno(rv); #else return -ENOTSUP; #endif } + + static + inline + int + lremovexattr(const std::string &path_, + const char *attrname_) + { + return fs::lremovexattr(path_.c_str(),attrname_); + } } diff --git a/src/fs_lstatvfs.hpp b/src/fs_lstatvfs.hpp index 666d29be..aad5220a 100644 --- a/src/fs_lstatvfs.hpp +++ b/src/fs_lstatvfs.hpp @@ -36,8 +36,8 @@ namespace fs static inline int - lstatvfs(const std::string &path_, - struct statvfs *st_) + lstatvfs(const char *path_, + struct statvfs *st_) { int fd; int rv; @@ -52,4 +52,13 @@ namespace fs return rv; } + + static + inline + int + lstatvfs(const std::string &path_, + struct statvfs *st_) + { + return fs::lstatvfs(path_.c_str(),st_); + } } diff --git a/src/fs_lutimens.hpp b/src/fs_lutimens.hpp index caecdbfb..5d5952c5 100644 --- a/src/fs_lutimens.hpp +++ b/src/fs_lutimens.hpp @@ -27,7 +27,7 @@ namespace fs static inline int - lutimens(const std::string &path_, + lutimens(const char *path_, const struct timespec ts_[2]) { return fs::utimensat(AT_FDCWD,path_,ts_,AT_SYMLINK_NOFOLLOW); @@ -36,7 +36,16 @@ namespace fs static inline int - lutimens(const std::string &path_, + lutimens(const std::string &path_, + const struct timespec ts_[2]) + { + return fs::lutimens(path_.c_str(),ts_); + } + + static + inline + int + lutimens(const char *path_, const struct stat &st_) { struct timespec ts[2]; @@ -46,4 +55,13 @@ namespace fs return fs::lutimens(path_,ts); } + + static + inline + int + lutimens(const std::string &path_, + const struct stat &st_) + { + return fs::lutimens(path_.c_str(),st_); + } } diff --git a/src/fs_mkdir.hpp b/src/fs_mkdir.hpp index 788cbbfa..bd88ce9e 100644 --- a/src/fs_mkdir.hpp +++ b/src/fs_mkdir.hpp @@ -57,7 +57,7 @@ namespace fs static inline int - mkdir(const fs::path &path_, + mkdir(const fs::relpath &path_, const mode_t mode_) { return fs::mkdir(path_.c_str(), diff --git a/src/fs_mkdirat.hpp b/src/fs_mkdirat.hpp index 76e417d7..1f30b25b 100644 --- a/src/fs_mkdirat.hpp +++ b/src/fs_mkdirat.hpp @@ -56,7 +56,7 @@ namespace fs inline int mkdirat(const int dirfd_, - const fs::path &pathname_, + const fs::relpath &pathname_, const mode_t mode_) { return fs::mkdirat(dirfd_, diff --git a/src/fs_mktemp.cpp b/src/fs_mktemp.cpp index 4ee3ba2a..5a89edeb 100644 --- a/src/fs_mktemp.cpp +++ b/src/fs_mktemp.cpp @@ -28,7 +28,6 @@ #include #include -#include #include #include @@ -39,9 +38,9 @@ static char const CHARS[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmno static size_t const CHARS_SIZE = (sizeof(CHARS) - 1); static -fs::path -_generate_tmp_path(const fs::path &dirpath_, - const std::string &src_filename_) +std::string +_generate_tmp_path(const std::string &dirpath_, + const std::string_view src_filename_) { long name_max; size_t substr_len; @@ -54,24 +53,25 @@ _generate_tmp_path(const fs::path &dirpath_, substr_len = std::min(src_filename_.size(), (size_t)(name_max - PAD_LEN - 2ULL)); - tmp_filename = '.'; - tmp_filename += src_filename_.substr(0,substr_len); + tmp_filename = dirpath_; + tmp_filename += "/."; + tmp_filename.append(src_filename_.substr(0,substr_len)); tmp_filename += '_'; for(size_t i = 0; i < PAD_LEN; i++) tmp_filename += CHARS[RND::rand64(CHARS_SIZE)]; - return dirpath_ / tmp_filename; + return tmp_filename; } -std::tuple -fs::mktemp_in_dir(const fs::path &dirpath_, - const fs::path &filename_, - const int flags_) +std::tuple +fs::mktemp_in_dir(const std::string &dirpath_, + const std::string_view filename_, + const int flags_) { int fd; int count; int flags; - fs::path tmp_filepath; + std::string tmp_filepath; count = MAX_ATTEMPTS; flags = (flags_ | O_EXCL | O_CREAT); @@ -83,19 +83,29 @@ fs::mktemp_in_dir(const fs::path &dirpath_, if(fd == -EEXIST) continue; if(fd < 0) - return std::make_tuple(fd,fs::path()); + return std::make_tuple(fd,std::string()); - return std::make_tuple(fd,tmp_filepath); + return std::make_tuple(fd,std::move(tmp_filepath)); } - return std::make_tuple(-EEXIST,fs::path()); + return std::make_tuple(-EEXIST,std::string()); } -std::tuple -fs::mktemp(const fs::path &filepath_, - const int flags_) +std::tuple +fs::mktemp(const std::string &filepath_, + const int flags_) { - return fs::mktemp_in_dir(filepath_.parent_path(), - filepath_.filename(), - flags_); + // Split filepath_ into dirpath and filename without an allocation: + // the dirpath is referenced as a substring; mktemp_in_dir copies + // it into the result. filename_view aliases the suffix. + auto slash = filepath_.rfind('/'); + std::string dirpath = (slash == std::string::npos) + ? std::string(".") + : filepath_.substr(0,slash); + std::string_view filename_view = + (slash == std::string::npos) + ? std::string_view(filepath_) + : std::string_view(filepath_).substr(slash + 1); + + return fs::mktemp_in_dir(dirpath,filename_view,flags_); } diff --git a/src/fs_mktemp.hpp b/src/fs_mktemp.hpp index 7facec0a..b6b196bc 100644 --- a/src/fs_mktemp.hpp +++ b/src/fs_mktemp.hpp @@ -18,20 +18,19 @@ #pragma once -#include "fs_path.hpp" - #include +#include #include namespace fs { - std::tuple - mktemp_in_dir(const fs::path &dirpath, - const fs::path &filename, - int const flags); + std::tuple + mktemp_in_dir(const std::string &dirpath, + const std::string_view filename, + int const flags); - std::tuple - mktemp(const fs::path &filepath, - const int flags); + std::tuple + mktemp(const std::string &filepath, + const int flags); } diff --git a/src/fs_mount.cpp b/src/fs_mount.cpp index 7f7203aa..68fbd455 100644 --- a/src/fs_mount.cpp +++ b/src/fs_mount.cpp @@ -22,7 +22,7 @@ int -fs::mount(const std::string &tgt_) +fs::mount(const char *tgt_) { return subprocess::call({"mount",tgt_}); } diff --git a/src/fs_mount.hpp b/src/fs_mount.hpp index e4f706db..31dfbedc 100644 --- a/src/fs_mount.hpp +++ b/src/fs_mount.hpp @@ -20,7 +20,16 @@ #include + namespace fs { - int mount(const std::string &tgt); + int mount(const char *tgt); + + static + inline + int + mount(const std::string &tgt_) + { + return fs::mount(tgt_.c_str()); + } } diff --git a/src/fs_mounts.hpp b/src/fs_mounts.hpp index 87591269..dce386f9 100644 --- a/src/fs_mounts.hpp +++ b/src/fs_mounts.hpp @@ -18,8 +18,7 @@ #pragma once -#include "fs_path.hpp" - +#include #include @@ -27,7 +26,7 @@ namespace fs { struct Mount { - fs::path dir; + std::string dir; std::string fsname; std::string type; std::string opts; diff --git a/src/fs_movefile_and_open.cpp b/src/fs_movefile_and_open.cpp index 6f3bc86a..302c40b5 100644 --- a/src/fs_movefile_and_open.cpp +++ b/src/fs_movefile_and_open.cpp @@ -63,22 +63,19 @@ static int _movefile_and_open(const Policy::Create &createFunc_, const Branches::Ptr &branches_, - const fs::path &branchpath_, - const fs::path &fusepath_, + const std::string &branchpath_, + const fs::relpath &fusepath_, int origfd_) { int rv; int dstfd_flags; int origfd_flags; s64 src_size; - fs::path fusedir; - fs::path src_branch; - fs::path src_filepath; - fs::path dst_filepath; + fs::relpath fusedir; + fs::relpath src_filepath; + fs::relpath dst_filepath; std::vector dst_branch; - src_branch = branchpath_; - rv = createFunc_(branches_,fusepath_,dst_branch); if(rv < 0) return rv; @@ -98,12 +95,17 @@ _movefile_and_open(const Policy::Create &createFunc_, fusedir = fusepath_.parent_path(); - rv = fs::clonepath(src_branch,dst_branch[0]->path,fusedir); + rv = fs::clonepath(branchpath_,dst_branch[0]->path,fusedir); if(rv < 0) return -ENOSPC; - src_filepath = src_branch / fusepath_; - dst_filepath = dst_branch[0]->path / fusepath_; + // src_filepath / dst_filepath are fs::relpath in the per-branch + // splice form: rel anchored, prefix written via set_prefix() so the + // syscall sees branch + '/' + fusepath as a single string. + src_filepath = fusepath_; + src_filepath.set_prefix(branchpath_); + dst_filepath = fusepath_; + dst_filepath.set_prefix(dst_branch[0]->path); rv = fs::copyfile(src_filepath,dst_filepath,{.cleanup_failure = true}); if(rv < 0) @@ -122,8 +124,8 @@ _movefile_and_open(const Policy::Create &createFunc_, int fs::movefile_and_open(const Policy::Create &policy_, const Branches::Ptr &branches_, - const fs::path &branchpath_, - const fs::path &fusepath_, + const std::string &branchpath_, + const fs::relpath &fusepath_, const int origfd_) { return ::_movefile_and_open(policy_, @@ -136,8 +138,8 @@ fs::movefile_and_open(const Policy::Create &policy_, int fs::movefile_and_open_as_root(const Policy::Create &policy_, const Branches::Ptr &branches_, - const fs::path &branchpath_, - const fs::path &fusepath_, + const std::string &branchpath_, + const fs::relpath &fusepath_, const int origfd_) { return fs::movefile_and_open(policy_, diff --git a/src/fs_movefile_and_open.hpp b/src/fs_movefile_and_open.hpp index 2510287d..eba83522 100644 --- a/src/fs_movefile_and_open.hpp +++ b/src/fs_movefile_and_open.hpp @@ -30,14 +30,14 @@ namespace fs int movefile_and_open(const Policy::Create &policy, const Branches::Ptr &branches_, - const fs::path &branchpath, - const fs::path &fusepath, + const std::string &branchpath, + const fs::relpath &fusepath, const int origfd); int movefile_and_open_as_root(const Policy::Create &policy, const Branches::Ptr &branches_, - const fs::path &branchpath, - const fs::path &fusepath, + const std::string &branchpath, + const fs::relpath &fusepath, int origfd); } diff --git a/src/fs_open.hpp b/src/fs_open.hpp index ca182fe4..ce2a0582 100644 --- a/src/fs_open.hpp +++ b/src/fs_open.hpp @@ -78,8 +78,16 @@ namespace fs static inline int - open_dir_ro(const std::string &path_) + open_dir_ro(const char *path_) { return fs::open(path_,O_RDONLY|O_DIRECTORY); } + + static + inline + int + open_dir_ro(const std::string &path_) + { + return fs::open(path_.c_str(),O_RDONLY|O_DIRECTORY); + } } diff --git a/src/fs_open_as.hpp b/src/fs_open_as.hpp index 73c78a53..116170b7 100644 --- a/src/fs_open_as.hpp +++ b/src/fs_open_as.hpp @@ -36,7 +36,7 @@ namespace fs inline int open_as(const ugid_t ugid_, - const fs::path &path_, + const fs::relpath &path_, const int flags_, const mode_t mode_) { @@ -54,7 +54,7 @@ namespace fs inline int open_as(const ugid_t ugid_, - const fs::path &path_, + const fs::relpath &path_, const int flags_, const mode_t mode_) { diff --git a/src/fs_openat.hpp b/src/fs_openat.hpp index 6d0158f2..dccc589c 100644 --- a/src/fs_openat.hpp +++ b/src/fs_openat.hpp @@ -62,7 +62,7 @@ namespace fs inline int openat(const int dirfd_, - const fs::path &pathname_, + const fs::relpath &pathname_, const int flags_, const mode_t mode_ = 0) { diff --git a/src/fs_readahead.cpp b/src/fs_readahead.cpp index b9de9513..de5d5ec3 100644 --- a/src/fs_readahead.cpp +++ b/src/fs_readahead.cpp @@ -73,8 +73,8 @@ fs::readahead(cu64 dev_, } int -fs::readahead(const std::string path_, - cu64 size_in_kb_) +fs::readahead(const char *path_, + cu64 size_in_kb_) { int rv; struct stat st; diff --git a/src/fs_readahead.hpp b/src/fs_readahead.hpp index c6dd4933..14c4a3f5 100644 --- a/src/fs_readahead.hpp +++ b/src/fs_readahead.hpp @@ -20,8 +20,6 @@ #include "base_types.h" -#include - namespace fs { @@ -35,6 +33,6 @@ namespace fs cu64 size_in_kb); int - readahead(const std::string path, - cu64 size_in_kb); + readahead(const char *path, + cu64 size_in_kb); } diff --git a/src/fs_readlink.hpp b/src/fs_readlink.hpp index 7711f8bd..57aff3b1 100644 --- a/src/fs_readlink.hpp +++ b/src/fs_readlink.hpp @@ -27,6 +27,20 @@ namespace fs { + static + inline + ssize_t + readlink(const char *path_, + char *buf_, + const size_t bufsiz_) + { + ssize_t rv; + + rv = ::readlink(path_,buf_,bufsiz_); + + return ::to_neg_errno(rv); + } + static inline ssize_t @@ -34,10 +48,6 @@ namespace fs char *buf_, const size_t bufsiz_) { - ssize_t rv; - - rv = ::readlink(path_.c_str(),buf_,bufsiz_); - - return ::to_neg_errno(rv); + return fs::readlink(path_.c_str(),buf_,bufsiz_); } } diff --git a/src/fs_realpath.hpp b/src/fs_realpath.hpp index 08407e4c..c0ed96a2 100644 --- a/src/fs_realpath.hpp +++ b/src/fs_realpath.hpp @@ -18,9 +18,6 @@ #pragma once -#include - -#include #include @@ -29,16 +26,16 @@ namespace fs static inline char * - realpath(const std::string &path_, - char *resolved_path_) + realpath(const char *path_, + char *resolved_path_) { - return ::realpath(path_.c_str(),resolved_path_); + return ::realpath(path_,resolved_path_); } static inline char * - realpath(const std::string &path_) + realpath(const char *path_) { return fs::realpath(path_,NULL); } diff --git a/src/fs_realpathize.cpp b/src/fs_realpathize.cpp index 36bb224b..fa7956a9 100644 --- a/src/fs_realpathize.cpp +++ b/src/fs_realpathize.cpp @@ -23,6 +23,8 @@ #include #include +#include + void fs::realpathize(std::vector *strs_) @@ -32,10 +34,8 @@ fs::realpathize(std::vector *strs_) for(size_t i = 0; i < strs_->size(); i++) { - rv = fs::realpath((*strs_)[i],resolved_path); - if(rv == NULL) - continue; - - (*strs_)[i] = rv; + rv = fs::realpath((*strs_)[i].c_str(),resolved_path); + if(rv != NULL) + (*strs_)[i] = rv; } } diff --git a/src/fs_statvfs_cache.cpp b/src/fs_statvfs_cache.cpp index 891708da..cf28dccf 100644 --- a/src/fs_statvfs_cache.cpp +++ b/src/fs_statvfs_cache.cpp @@ -36,7 +36,26 @@ struct Element struct statvfs st; }; -typedef std::unordered_map statvfs_cache; +// Transparent hash / equality so we can look up by std::string_view +// (or const char*) without constructing a std::string. Requires +// unordered_map's Hash and KeyEqual to be transparent (C++20). The +// insert path still constructs a string for the key when missing, +// but the hot read path is alloc-free. +struct string_hash +{ + using is_transparent = void; + std::size_t operator()(const std::string &s) const noexcept { return std::hash{}(s); } + std::size_t operator()(std::string_view s) const noexcept { return std::hash{}(s); } + std::size_t operator()(const char *s) const noexcept { return std::hash{}(s); } +}; + +struct string_eq +{ + using is_transparent = void; + bool operator()(std::string_view a, std::string_view b) const noexcept { return a == b; } +}; + +typedef std::unordered_map statvfs_cache; // Cache is read heavy. Use a shared mutex so concurrent reads can // work in parallel and only inserts or refreshes take a unique lock. @@ -69,8 +88,8 @@ fs::statvfs_cache_timeout(cu64 timeout_) } int -fs::statvfs_cache(const std::string &path_, - struct statvfs *st_) +fs::statvfs_cache(const char *path_, + struct statvfs *st_) { u64 now; @@ -79,10 +98,11 @@ fs::statvfs_cache(const std::string &path_, now = ::_get_time(); + std::string_view key(path_); { std::shared_lock lk(g_cache_mutex); - auto it = g_cache.find(path_); + auto it = g_cache.find(key); if((it != g_cache.end()) && ((now - it->second.time) <= g_timeout)) { *st_ = it->second.st; @@ -93,7 +113,10 @@ fs::statvfs_cache(const std::string &path_, int rv = 0; std::unique_lock lk(g_cache_mutex); - Element &e = g_cache[path_]; + // operator[] requires a string key (no transparent insert overload), + // so this path does construct a std::string. Only happens on insert / + // refresh -- the read-only fast path above is alloc-free. + Element &e = g_cache[std::string(key)]; if((now - e.time) > g_timeout) { e.time = now; @@ -106,8 +129,8 @@ fs::statvfs_cache(const std::string &path_, } int -fs::statvfs_cache_readonly(const std::string &path_, - bool *readonly_) +fs::statvfs_cache_readonly(const char *path_, + bool *readonly_) { int rv; struct statvfs st; @@ -120,8 +143,8 @@ fs::statvfs_cache_readonly(const std::string &path_, } int -fs::statvfs_cache_spaceavail(const std::string &path_, - u64 *spaceavail_) +fs::statvfs_cache_spaceavail(const char *path_, + u64 *spaceavail_) { int rv; struct statvfs st; @@ -134,8 +157,8 @@ fs::statvfs_cache_spaceavail(const std::string &path_, } int -fs::statvfs_cache_spaceused(const std::string &path_, - u64 *spaceused_) +fs::statvfs_cache_spaceused(const char *path_, + u64 *spaceused_) { int rv; struct statvfs st; diff --git a/src/fs_statvfs_cache.hpp b/src/fs_statvfs_cache.hpp index c37abe05..7a9192b3 100644 --- a/src/fs_statvfs_cache.hpp +++ b/src/fs_statvfs_cache.hpp @@ -33,18 +33,54 @@ namespace fs statvfs_cache_timeout(cu64 timeout); int - statvfs_cache(const std::string &path, - struct statvfs *st); + statvfs_cache(const char *path, + struct statvfs *st); + + static + inline + int + statvfs_cache(const std::string &path_, + struct statvfs *st_) + { + return fs::statvfs_cache(path_.c_str(),st_); + } int - statvfs_cache_readonly(const std::string &path, - bool *readonly); + statvfs_cache_readonly(const char *path, + bool *readonly); + + static + inline + int + statvfs_cache_readonly(const std::string &path_, + bool *readonly_) + { + return fs::statvfs_cache_readonly(path_.c_str(),readonly_); + } int - statvfs_cache_spaceavail(const std::string &path, - u64 *spaceavail); + statvfs_cache_spaceavail(const char *path, + u64 *spaceavail); + + static + inline + int + statvfs_cache_spaceavail(const std::string &path_, + u64 *spaceavail_) + { + return fs::statvfs_cache_spaceavail(path_.c_str(),spaceavail_); + } int - statvfs_cache_spaceused(const std::string &path, - u64 *spaceused); + statvfs_cache_spaceused(const char *path, + u64 *spaceused); + + static + inline + int + statvfs_cache_spaceused(const std::string &path_, + u64 *spaceused_) + { + return fs::statvfs_cache_spaceused(path_.c_str(),spaceused_); + } } diff --git a/src/fs_umount2.hpp b/src/fs_umount2.hpp index edc61e2b..e6eeba2c 100644 --- a/src/fs_umount2.hpp +++ b/src/fs_umount2.hpp @@ -21,6 +21,8 @@ #include "errno.hpp" #include "to_neg_errno.hpp" +#include + #ifdef __FreeBSD__ # include # include @@ -30,19 +32,18 @@ # include #endif -#include namespace fs { static inline int - umount2(const std::string target_, - const int flags_) + umount2(const char *target_, + const int flags_) { int rv; - rv = ::umount2(target_.c_str(), + rv = ::umount2(target_, flags_); return ::to_neg_errno(rv); @@ -51,8 +52,16 @@ namespace fs static inline int - umount_lazy(const std::string target_) + umount_lazy(const char *target_) { return fs::umount2(target_,MNT_DETACH); } + + static + inline + int + umount_lazy(const std::string &target_) + { + return fs::umount_lazy(target_.c_str()); + } } diff --git a/src/fs_utimensat_generic.hpp b/src/fs_utimensat_generic.hpp index 05226213..ab35205c 100644 --- a/src/fs_utimensat_generic.hpp +++ b/src/fs_utimensat_generic.hpp @@ -40,9 +40,9 @@ static inline bool -_can_call_lutimes(const int dirfd_, - const std::string &path_, - const int flags_) +_can_call_lutimes(const int dirfd_, + const char *path_, + const int flags_) { return ((flags_ == AT_SYMLINK_NOFOLLOW) && ((dirfd_ == AT_FDCWD) || @@ -120,7 +120,7 @@ static inline int _set_utime_omit_to_current_value(const int dirfd_, - const std::string &path_, + const char *path_, const struct timespec ts_[2], struct timeval tv_[2], const int flags_) @@ -206,7 +206,7 @@ static inline int _convert_timespec_to_timeval(const int dirfd_, - const std::string &path_, + const char *path_, const struct timespec ts_[2], struct timeval tv_[2], struct timeval **tvp_, @@ -265,7 +265,7 @@ namespace fs inline int utimensat(const int dirfd_, - const std::string &path_, + const char *path_, const struct timespec ts_[2], const int flags_) { @@ -291,4 +291,15 @@ namespace fs return -ENOTSUP; } + + static + inline + int + utimensat(const int dirfd_, + const std::string &path_, + const struct timespec ts_[2], + const int flags_) + { + return fs::utimensat(dirfd_,path_.c_str(),ts_,flags_); + } } diff --git a/src/fs_wait_for_mount.cpp b/src/fs_wait_for_mount.cpp index 22f3ada9..2e454abf 100644 --- a/src/fs_wait_for_mount.cpp +++ b/src/fs_wait_for_mount.cpp @@ -36,17 +36,17 @@ constexpr std::chrono::milliseconds SLEEP_DURATION = std::chrono::milliseconds(3 static bool _branch_is_mounted(const struct stat &src_st_, - const fs::path &branch_path_) + const std::string &branch_path_) { int rv; struct stat st; - fs::path filepath; + std::string filepath; rv = fs::lgetxattr(branch_path_,"user.mergerfs.branch",NULL,0); if(rv >= 0) return true; - filepath = branch_path_ / ".mergerfs.branch"; + filepath = branch_path_ + "/.mergerfs.branch"; rv = fs::exists(filepath); if(rv) return true; @@ -55,7 +55,7 @@ _branch_is_mounted(const struct stat &src_st_, if(rv >= 0) return false; - filepath = branch_path_ / ".mergerfs.branch_mounts_here"; + filepath = branch_path_ + "/.mergerfs.branch_mounts_here"; rv = fs::exists(filepath); if(rv) return false; @@ -69,13 +69,13 @@ _branch_is_mounted(const struct stat &src_st_, static void -_check_mounted(const struct stat &src_st_, - const std::set &tgt_paths_, - std::vector *successes_, - std::vector *failures_) +_check_mounted(const struct stat &src_st_, + const std::set &tgt_paths_, + std::vector *successes_, + std::vector *failures_) { - std::vector &successes = *successes_; - std::vector &failures = *failures_; + std::vector &successes = *successes_; + std::vector &failures = *failures_; for(auto const &tgt_path : tgt_paths_) { @@ -92,13 +92,13 @@ _check_mounted(const struct stat &src_st_, static int _wait_for_mount(const struct stat &src_st_, - const std::vector &tgt_paths_, + const std::vector &tgt_paths_, const std::chrono::milliseconds &timeout_) { bool first_loop; - std::vector successes; - std::vector failures; - std::set tgt_paths; + std::vector successes; + std::vector failures; + std::set tgt_paths; std::chrono::time_point now; std::chrono::time_point deadline; @@ -120,13 +120,13 @@ _wait_for_mount(const struct stat &src_st_, for(const auto &path : successes) { tgt_paths.erase(path); - SysLog::info("{} is ready",path.string()); + SysLog::info("{} is ready",path); } if(first_loop) { for(const auto &path : failures) - SysLog::notice("{} is not ready, waiting",path.string()); + SysLog::notice("{} is not ready, waiting",path); first_loop = false; } @@ -135,14 +135,14 @@ _wait_for_mount(const struct stat &src_st_, } for(const auto &path : failures) - SysLog::notice("{} not ready within timeout",path.string()); + SysLog::notice("{} not ready within timeout",path); return failures.size(); } int -fs::wait_for_mount(const fs::path &src_path_, - const std::vector &tgt_paths_, +fs::wait_for_mount(const std::string &src_path_, + const std::vector &tgt_paths_, const std::chrono::milliseconds &timeout_) { int rv; @@ -151,7 +151,7 @@ fs::wait_for_mount(const fs::path &src_path_, rv = fs::stat(src_path_,&src_st); if(rv < 0) SysLog::error("Error stat'ing mount path: {} ({})", - src_path_.string(), + src_path_, ::strerror(-rv)); for(auto &tgt_path : tgt_paths_) @@ -163,7 +163,7 @@ fs::wait_for_mount(const fs::path &src_path_, rv = fs::mount(tgt_path); SysLog::notice("mount {}: {}", - tgt_path.string(), + tgt_path, ((rv == 0) ? "success" : "fail")); } diff --git a/src/fs_wait_for_mount.hpp b/src/fs_wait_for_mount.hpp index 35e883e6..42dcef12 100644 --- a/src/fs_wait_for_mount.hpp +++ b/src/fs_wait_for_mount.hpp @@ -17,16 +17,15 @@ #pragma once -#include "fs_path.hpp" - #include +#include #include namespace fs { int - wait_for_mount(const fs::path &srcpath, - const std::vector &tgtpaths, + wait_for_mount(const std::string &srcpath, + const std::vector &tgtpaths, const std::chrono::milliseconds &timeout); } diff --git a/src/fuse_access.cpp b/src/fuse_access.cpp index 23e1e28c..936ef0a4 100644 --- a/src/fuse_access.cpp +++ b/src/fuse_access.cpp @@ -31,12 +31,12 @@ static int _access(const Policy::Search &searchFunc_, const Branches::Ptr branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, const int mask_) { int rv; StrVec basepaths; - fs::path fullpath; + fs::relpath fullpath; std::vector branches; rv = searchFunc_(branches_,fusepath_,branches); @@ -45,7 +45,7 @@ _access(const Policy::Search &searchFunc_, if(branches.empty()) return -ENOENT; - fullpath = branches[0]->path / fusepath_; + fullpath.assign_concat(branches[0]->path,fusepath_); rv = fs::eaccess(fullpath,mask_); @@ -54,13 +54,12 @@ _access(const Policy::Search &searchFunc_, int FUSE::access(const fuse_req_ctx_t *ctx_, - const char *fusepath_, + const fs::relpath &fusepath_, int mask_) { - const fs::path fusepath{fusepath_}; return ::_access(cfg.func.access.policy, cfg.branches, - fusepath, + fusepath_, mask_); } diff --git a/src/fuse_access.hpp b/src/fuse_access.hpp index 018a79fc..7570ba2a 100644 --- a/src/fuse_access.hpp +++ b/src/fuse_access.hpp @@ -19,11 +19,12 @@ #pragma once #include "fuse_req_ctx.h" +#include "fs_path.hpp" namespace FUSE { int access(const fuse_req_ctx_t *ctx, - const char *fusepath, + const fs::relpath &fusepath, int mask); } diff --git a/src/fuse_bmap.cpp b/src/fuse_bmap.cpp index b381106f..33979b8a 100644 --- a/src/fuse_bmap.cpp +++ b/src/fuse_bmap.cpp @@ -23,7 +23,7 @@ int FUSE::bmap(const fuse_req_ctx_t *ctx_, - const char *fusepath_, + const fs::relpath &fusepath_, size_t blocksize_, u64 *idx_) { diff --git a/src/fuse_bmap.hpp b/src/fuse_bmap.hpp index a8027bcd..06e72656 100644 --- a/src/fuse_bmap.hpp +++ b/src/fuse_bmap.hpp @@ -20,6 +20,7 @@ #include "base_types.h" #include "fuse_req_ctx.h" +#include "fs_path.hpp" #include @@ -28,7 +29,7 @@ namespace FUSE { int bmap(const fuse_req_ctx_t *ctx, - const char *fusepath, + const fs::relpath &fusepath, size_t blocksize, u64 *idx); } diff --git a/src/fuse_chmod.cpp b/src/fuse_chmod.cpp index 22362c8e..99305d9d 100644 --- a/src/fuse_chmod.cpp +++ b/src/fuse_chmod.cpp @@ -30,33 +30,24 @@ #include -static -void -_chmod_loop_core(const std::string &basepath_, - const fs::path &fusepath_, - const mode_t mode_, - PolicyRV *prv_) -{ - int rv; - fs::path fullpath; - - fullpath = basepath_ / fusepath_; - - rv = fs::lchmod(fullpath,mode_); - - prv_->insert(rv,basepath_); -} - static void _chmod_loop(const std::vector &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, const mode_t mode_, PolicyRV *prv_) { + fs::relpath fullpath = fusepath_; + for(auto &branch : branches_) { - ::_chmod_loop_core(branch->path,fusepath_,mode_,prv_); + int rv; + + fullpath.set_prefix(branch->path); + + rv = fs::lchmod(fullpath,mode_); + + prv_->insert(rv,branch->path); } } @@ -65,7 +56,7 @@ int _chmod(const Policy::Action &actionFunc_, const Policy::Search &searchFunc_, const Branches::Ptr branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, const mode_t mode_) { int rv; @@ -96,7 +87,7 @@ _chmod(const Policy::Action &actionFunc_, static int -_chmod(const fs::path &fusepath_, +_chmod(const fs::relpath &fusepath_, const mode_t mode_) { @@ -109,10 +100,9 @@ _chmod(const fs::path &fusepath_, int FUSE::chmod(const fuse_req_ctx_t *ctx_, - const char *fusepath_, + const fs::relpath &fusepath_, mode_t mode_) { - const fs::path fusepath{fusepath_}; - return ::_chmod(fusepath,mode_); + return ::_chmod(fusepath_,mode_); } diff --git a/src/fuse_chmod.hpp b/src/fuse_chmod.hpp index f9cbc50b..824ba33c 100644 --- a/src/fuse_chmod.hpp +++ b/src/fuse_chmod.hpp @@ -19,6 +19,7 @@ #pragma once #include "fuse_req_ctx.h" +#include "fs_path.hpp" #include @@ -27,6 +28,6 @@ namespace FUSE { int chmod(const fuse_req_ctx_t *ctx, - const char *fusepath, + const fs::relpath &fusepath, mode_t mode); } diff --git a/src/fuse_chown.cpp b/src/fuse_chown.cpp index 466f525d..a9164f0f 100644 --- a/src/fuse_chown.cpp +++ b/src/fuse_chown.cpp @@ -30,35 +30,25 @@ #include -static -void -_chown_loop_core(const fs::path &basepath_, - const fs::path &fusepath_, - const uid_t uid_, - const gid_t gid_, - PolicyRV *prv_) -{ - int rv; - fs::path fullpath; - - fullpath = basepath_ / fusepath_; - - rv = fs::lchown(fullpath,uid_,gid_); - - prv_->insert(rv,basepath_); -} - static void _chown_loop(const std::vector &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, const uid_t uid_, const gid_t gid_, PolicyRV *prv_) { + fs::relpath fullpath = fusepath_; + for(const auto &branch : branches_) { - ::_chown_loop_core(branch->path,fusepath_,uid_,gid_,prv_); + int rv; + + fullpath.set_prefix(branch->path); + + rv = fs::lchown(fullpath,uid_,gid_); + + prv_->insert(rv,branch->path); } } @@ -67,7 +57,7 @@ int _chown(const Policy::Action &actionFunc_, const Policy::Search &searchFunc_, const Branches::Ptr branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, const uid_t uid_, const gid_t gid_) { @@ -100,16 +90,15 @@ _chown(const Policy::Action &actionFunc_, int FUSE::chown(const fuse_req_ctx_t *ctx_, - const char *fusepath_, + const fs::relpath &fusepath_, uid_t uid_, gid_t gid_) { - const fs::path fusepath{fusepath_}; return ::_chown(cfg.func.chown.policy, cfg.func.getattr.policy, cfg.branches, - fusepath, + fusepath_, uid_, gid_); } diff --git a/src/fuse_chown.hpp b/src/fuse_chown.hpp index ae8f3051..69fbc16f 100644 --- a/src/fuse_chown.hpp +++ b/src/fuse_chown.hpp @@ -19,6 +19,7 @@ #pragma once #include "fuse_req_ctx.h" +#include "fs_path.hpp" #include @@ -27,7 +28,7 @@ namespace FUSE { int chown(const fuse_req_ctx_t *ctx, - const char *fusepath, + const fs::relpath &fusepath, uid_t uid, gid_t gid); } diff --git a/src/fuse_create.cpp b/src/fuse_create.cpp index 2fe50b40..65e5772a 100644 --- a/src/fuse_create.cpp +++ b/src/fuse_create.cpp @@ -165,7 +165,7 @@ _config_to_ffi_flags(const int tid_, static int _create_core(const ugid_t ugid_, - const fs::path &fullpath_, + const fs::relpath &fullpath_, mode_t mode_, const mode_t umask_, const int flags_) @@ -180,16 +180,16 @@ static int _create_core(const ugid_t ugid_, const Branch *branch_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, fuse_file_info_t *ffi_, const mode_t mode_, const mode_t umask_) { int rv; FileInfo *fi; - fs::path fullpath; + fs::relpath fullpath; - fullpath = branch_->path / fusepath_; + fullpath.assign_concat(branch_->path,fusepath_); rv = ::_create_core(ugid_,fullpath,mode_,umask_,ffi_->flags); if(rv < 0) @@ -208,14 +208,14 @@ _create(const ugid_t ugid_, const Policy::Search &searchFunc_, const Policy::Create &createFunc_, const Branches::Ptr branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, fuse_file_info_t *ffi_, const mode_t mode_, const mode_t umask_) { int rv; - fs::path fullpath; - fs::path fusedirpath; + fs::relpath fullpath; + fs::relpath fusedirpath; std::vector createpaths; std::vector existingpaths; @@ -258,7 +258,7 @@ _(const PassthroughIOEnum e_, static int _create(const fuse_req_ctx_t *ctx_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, mode_t mode_, fuse_file_info_t *ffi_) { @@ -345,11 +345,10 @@ _create(const fuse_req_ctx_t *ctx_, int FUSE::create(const fuse_req_ctx_t *ctx_, - const char *fusepath_, + const fs::relpath &fusepath_, mode_t mode_, fuse_file_info_t *ffi_) { - const fs::path fusepath{fusepath_}; - return ::_create(ctx_,fusepath,mode_,ffi_); + return ::_create(ctx_,fusepath_,mode_,ffi_); } diff --git a/src/fuse_create.hpp b/src/fuse_create.hpp index 931045c0..e82bd7d3 100644 --- a/src/fuse_create.hpp +++ b/src/fuse_create.hpp @@ -27,7 +27,7 @@ namespace FUSE { int create(const fuse_req_ctx_t *ctx, - const char *fusepath, + const fs::relpath &fusepath, mode_t mode, fuse_file_info_t *ffi); } diff --git a/src/fuse_getattr.cpp b/src/fuse_getattr.cpp index 23174d3c..d3b113f9 100644 --- a/src/fuse_getattr.cpp +++ b/src/fuse_getattr.cpp @@ -37,7 +37,7 @@ static void -_set_stat_if_leads_to_dir(const fs::path &path_, +_set_stat_if_leads_to_dir(const fs::relpath &path_, struct stat *st_) { int rv; @@ -55,7 +55,7 @@ _set_stat_if_leads_to_dir(const fs::path &path_, static void -_set_stat_if_leads_to_reg(const fs::path &path_, +_set_stat_if_leads_to_reg(const fs::relpath &path_, struct stat *st_) { int rv; @@ -129,14 +129,14 @@ static int _getattr(const Policy::Search &searchFunc_, const Branches::Ptr branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, struct stat *st_, const bool symlinkify_, const time_t symlinkify_timeout_, FollowSymlinks followsymlinks_) { int rv; - fs::path fullpath; + fs::relpath fullpath; std::vector branches; rv = searchFunc_(branches_,fusepath_,branches); @@ -145,7 +145,7 @@ _getattr(const Policy::Search &searchFunc_, if(branches.empty()) return -ENOENT; - fullpath = branches[0]->path / fusepath_; + fullpath.assign_concat(branches[0]->path,fusepath_); switch(followsymlinks_) { @@ -183,7 +183,7 @@ _getattr(const Policy::Search &searchFunc_, } int -_getattr(const fs::path &fusepath_, +_getattr(const fs::relpath &fusepath_, struct stat *st_, fuse_timeouts_t *timeout_) { @@ -209,18 +209,7 @@ _getattr(const fs::path &fusepath_, int FUSE::getattr(const fuse_req_ctx_t *ctx_, - const char *fusepath_, - struct stat *st_, - fuse_timeouts_t *timeout_) -{ - const fs::path fusepath{fusepath_}; - - return FUSE::getattr(ctx_,fusepath,st_,timeout_); -} - -int -FUSE::getattr(const fuse_req_ctx_t *ctx_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, struct stat *st_, fuse_timeouts_t *timeout_) { @@ -228,7 +217,7 @@ FUSE::getattr(const fuse_req_ctx_t *ctx_, } int -FUSE::getattr(const fs::path &fusepath_, +FUSE::getattr(const fs::relpath &fusepath_, struct stat *st_, fuse_timeouts_t *timeout_) { diff --git a/src/fuse_getattr.hpp b/src/fuse_getattr.hpp index f2d5141d..53e52e70 100644 --- a/src/fuse_getattr.hpp +++ b/src/fuse_getattr.hpp @@ -31,18 +31,12 @@ namespace FUSE { int getattr(const fuse_req_ctx_t *ctx, - const char *fusepath, + const fs::relpath &fusepath, struct stat *buf, fuse_timeouts_t *timeout); int - getattr(const fuse_req_ctx_t *ctx, - const fs::path &fusepath, - struct stat *buf, - fuse_timeouts_t *timeout); - - int - getattr(const fs::path &fusepath, + getattr(const fs::relpath &fusepath, struct stat *buf, fuse_timeouts_t *timeout); diff --git a/src/fuse_getxattr.cpp b/src/fuse_getxattr.cpp index 8bedd2d7..6565eb95 100644 --- a/src/fuse_getxattr.cpp +++ b/src/fuse_getxattr.cpp @@ -78,9 +78,9 @@ _getxattr_ctrl_file(Config &cfg_, static int -_getxattr_from_string(char *destbuf_, - const size_t destbufsize_, - const std::string &src_) +_getxattr_from_string(char *destbuf_, + const size_t destbufsize_, + const std::string_view src_) { const size_t srcbufsize = src_.size(); @@ -98,7 +98,7 @@ _getxattr_from_string(char *destbuf_, static int _getxattr_user_mergerfs_allpaths(const Branches::Ptr branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, char *buf_, const size_t count_) { @@ -117,9 +117,9 @@ _getxattr_user_mergerfs_allpaths(const Branches::Ptr branches_, static int -_getxattr_user_mergerfs(const fs::path &basepath_, - const fs::path &fusepath_, - const fs::path &fullpath_, +_getxattr_user_mergerfs(const fs::relpath &basepath_, + const fs::relpath &fusepath_, + const fs::relpath &fullpath_, const Branches::Ptr branches_, const char *attrname_, char *buf_, @@ -145,13 +145,13 @@ static int _getxattr(const Policy::Search &searchFunc_, const Branches::Ptr branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, const char *attrname_, char *buf_, const size_t count_) { int rv; - fs::path fullpath; + fs::relpath fullpath; std::vector branches; rv = searchFunc_(branches_,fusepath_,branches); @@ -160,7 +160,7 @@ _getxattr(const Policy::Search &searchFunc_, if(branches.empty()) return -ENOENT; - fullpath = branches[0]->path / fusepath_; + fullpath.assign_concat(branches[0]->path,fusepath_); if(Config::is_mergerfs_xattr(attrname_)) return ::_getxattr_user_mergerfs(branches[0]->path, @@ -176,14 +176,13 @@ _getxattr(const Policy::Search &searchFunc_, int FUSE::getxattr(const fuse_req_ctx_t *ctx_, - const char *fusepath_, + const fs::relpath &fusepath_, const char *attrname_, char *attrvalue_, size_t attrvalue_size_) { - const fs::path fusepath{fusepath_}; - if(Config::is_ctrl_file(fusepath)) + if(Config::is_ctrl_file(fusepath_)) return ::_getxattr_ctrl_file(cfg, attrname_, attrvalue_, @@ -198,7 +197,7 @@ FUSE::getxattr(const fuse_req_ctx_t *ctx_, return ::_getxattr(cfg.func.getxattr.policy, cfg.branches, - fusepath, + fusepath_, attrname_, attrvalue_, attrvalue_size_); diff --git a/src/fuse_getxattr.hpp b/src/fuse_getxattr.hpp index 50181ebf..c10c9fb4 100644 --- a/src/fuse_getxattr.hpp +++ b/src/fuse_getxattr.hpp @@ -19,6 +19,7 @@ #pragma once #include "fuse_req_ctx.h" +#include "fs_path.hpp" #include @@ -27,7 +28,7 @@ namespace FUSE { int getxattr(const fuse_req_ctx_t *ctx, - const char *fusepath, + const fs::relpath &fusepath, const char *attrname, char *attrvalue, size_t attrvalue_size); diff --git a/src/fuse_init.cpp b/src/fuse_init.cpp index de27eb82..abf28aac 100644 --- a/src/fuse_init.cpp +++ b/src/fuse_init.cpp @@ -144,16 +144,16 @@ _want_if_capable_max_pages(fuse_conn_info_t *conn_, static void -_readahead(const fs::path &path_, - const int readahead_) +_readahead(const std::string &path_, + const int readahead_) { int rv; - rv = fs::readahead(path_,readahead_); + rv = fs::readahead(path_.c_str(),readahead_); if(rv == 0) - SysLog::info("{} - readahead set to {}",path_.string(),readahead_); + SysLog::info("{} - readahead set to {}",path_,readahead_); else - SysLog::error("{} - unable to set readahead",path_.string()); + SysLog::error("{} - unable to set readahead",path_); } static diff --git a/src/fuse_ioctl.cpp b/src/fuse_ioctl.cpp index bd0a0259..4e57bcfa 100644 --- a/src/fuse_ioctl.cpp +++ b/src/fuse_ioctl.cpp @@ -131,14 +131,14 @@ static int _ioctl_dir_base(const Policy::Search &searchFunc_, const Branches::Ptr branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, const u32 cmd_, void *data_, u32 *out_bufsz_) { int fd; int rv; - fs::path fullpath; + fs::relpath fullpath; std::vector branches; rv = searchFunc_(branches_,fusepath_,branches); @@ -147,7 +147,7 @@ _ioctl_dir_base(const Policy::Search &searchFunc_, if(branches.empty()) return -ENOENT; - fullpath = branches[0]->path / fusepath_; + fullpath.assign_concat(branches[0]->path,fusepath_); fd = fs::open(fullpath,O_RDONLY|O_NOATIME|O_NONBLOCK); if(fd < 0) diff --git a/src/fuse_link.cpp b/src/fuse_link.cpp index b9b5c3da..659f30b7 100644 --- a/src/fuse_link.cpp +++ b/src/fuse_link.cpp @@ -38,20 +38,20 @@ static int _link_create_path_loop(const std::vector &oldbranches_, const Branch *newbranch_, - const fs::path &oldfusepath_, - const fs::path &newfusepath_, - const fs::path &newfusedirpath_) + const fs::relpath &oldfusepath_, + const fs::relpath &newfusepath_, + const fs::relpath &newfusedirpath_) { int rv; int err; - fs::path oldfullpath; - fs::path newfullpath; + fs::relpath oldfullpath = oldfusepath_; + fs::relpath newfullpath = newfusepath_; err = -ENOENT; for(const auto &oldbranch : oldbranches_) { - oldfullpath = oldbranch->path / oldfusepath_; - newfullpath = oldbranch->path / newfusepath_; + oldfullpath.set_prefix(oldbranch->path); + newfullpath.set_prefix(oldbranch->path); rv = fs::link(oldfullpath,newfullpath); if(rv == -ENOENT) @@ -75,11 +75,11 @@ int _link_create_path(const Policy::Search &searchFunc_, const Policy::Action &actionFunc_, const Branches::Ptr ibranches_, - const fs::path &oldfusepath_, - const fs::path &newfusepath_) + const fs::relpath &oldfusepath_, + const fs::relpath &newfusepath_) { int rv; - fs::path newfusedirpath; + fs::relpath newfusedirpath; std::vector oldbranches; std::vector newbranches; @@ -102,46 +102,30 @@ _link_create_path(const Policy::Search &searchFunc_, newfusedirpath); } -static -int -_link_preserve_path_core(const fs::path &oldbasepath_, - const fs::path &oldfusepath_, - const fs::path &newfusepath_, - struct stat *st_) -{ - int rv; - fs::path oldfullpath; - fs::path newfullpath; - - oldfullpath = oldbasepath_ / oldfusepath_; - newfullpath = oldbasepath_ / newfusepath_; - - rv = fs::link(oldfullpath,newfullpath); - if(rv == -ENOENT) - rv = -EXDEV; - if((rv == 0) && (st_->st_ino == 0)) - rv = fs::lstat(oldfullpath,st_); - - return rv; -} - static int _link_preserve_path_loop(const std::vector &oldbranches_, - const fs::path &oldfusepath_, - const fs::path &newfusepath_, + const fs::relpath &oldfusepath_, + const fs::relpath &newfusepath_, struct stat *st_) { int rv; int err; + fs::relpath oldfullpath = oldfusepath_; + fs::relpath newfullpath = newfusepath_; err = -ENOENT; for(const auto &oldbranch : oldbranches_) { - rv = ::_link_preserve_path_core(oldbranch->path, - oldfusepath_, - newfusepath_, - st_); + oldfullpath.set_prefix(oldbranch->path); + newfullpath.set_prefix(oldbranch->path); + + rv = fs::link(oldfullpath,newfullpath); + if(rv == -ENOENT) + rv = -EXDEV; + if((rv == 0) && (st_->st_ino == 0)) + rv = fs::lstat(oldfullpath,st_); + if(err < 0) err = rv; } @@ -153,8 +137,8 @@ static int _link_preserve_path(const Policy::Action &actionFunc_, const Branches::Ptr branches_, - const fs::path &oldfusepath_, - const fs::path &newfusepath_, + const fs::relpath &oldfusepath_, + const fs::relpath &newfusepath_, struct stat *st_) { int rv; @@ -174,8 +158,8 @@ _link_preserve_path(const Policy::Action &actionFunc_, static int -_link(const fs::path &oldpath_, - const fs::path &newpath_, +_link(const fs::relpath &oldpath_, + const fs::relpath &newpath_, struct stat *st_) { if(cfg.func.create.policy.path_preserving() && !cfg.ignorepponrename) @@ -195,8 +179,8 @@ _link(const fs::path &oldpath_, static int _link(const fuse_req_ctx_t *ctx_, - const fs::path &oldpath_, - const fs::path &newpath_, + const fs::relpath &oldpath_, + const fs::relpath &newpath_, struct stat *st_, fuse_timeouts_t *timeouts_) { @@ -212,18 +196,19 @@ _link(const fuse_req_ctx_t *ctx_, static int _link_exdev_rel_symlink(const fuse_req_ctx_t *ctx_, - const fs::path &oldpath_, - const fs::path &newpath_, + const fs::relpath &oldpath_, + const fs::relpath &newpath_, struct stat *st_, fuse_timeouts_t *timeouts_) { int rv; - fs::path target(oldpath_); - fs::path linkpath(newpath_); + // lexically_relative builds the target as a relpath; newpath_ is + // passed straight through to FUSE::symlink without copying. + fs::relpath target = oldpath_.lexically_relative(newpath_.parent_path()); + if(target.empty()) + return -EXDEV; - target = target.lexically_relative(linkpath.parent_path()); - - rv = FUSE::symlink(ctx_,target.c_str(),linkpath); + rv = FUSE::symlink(ctx_,target.c_str(),newpath_); if(rv == 0) rv = FUSE::getattr(ctx_,oldpath_,st_,timeouts_); @@ -239,13 +224,13 @@ int _link_exdev_abs_base_symlink(const fuse_req_ctx_t *ctx_, const Policy::Search &openPolicy_, const Branches::Ptr ibranches_, - const fs::path &oldpath_, - const fs::path &newpath_, + const fs::relpath &oldpath_, + const fs::relpath &newpath_, struct stat *st_, fuse_timeouts_t *timeouts_) { int rv; - fs::path target; + fs::relpath target; std::vector obranches; rv = openPolicy_(ibranches_,oldpath_,obranches); @@ -270,15 +255,15 @@ _link_exdev_abs_base_symlink(const fuse_req_ctx_t *ctx_, static int _link_exdev_abs_pool_symlink(const fuse_req_ctx_t *ctx_, - const fs::path &mount_, - const fs::path &oldpath_, - const fs::path &newpath_, + const std::string &mount_, + const fs::relpath &oldpath_, + const fs::relpath &newpath_, struct stat *st_, fuse_timeouts_t *timeouts_) { int rv; StrVec basepaths; - fs::path target; + fs::relpath target; target = mount_ / oldpath_; @@ -296,8 +281,8 @@ _link_exdev_abs_pool_symlink(const fuse_req_ctx_t *ctx_, static int _link_exdev(const fuse_req_ctx_t *ctx_, - const fs::path &oldpath_, - const fs::path &newpath_, + const fs::relpath &oldpath_, + const fs::relpath &newpath_, struct stat *st_, fuse_timeouts_t *timeouts_) { @@ -333,18 +318,16 @@ _link_exdev(const fuse_req_ctx_t *ctx_, int FUSE::link(const fuse_req_ctx_t *ctx_, - const char *oldpath_, - const char *newpath_, + const fs::relpath &oldpath_, + const fs::relpath &newpath_, struct stat *st_, fuse_timeouts_t *timeouts_) { int rv; - const fs::path oldpath{oldpath_}; - const fs::path newpath{newpath_}; - rv = ::_link(ctx_,oldpath,newpath,st_,timeouts_); + rv = ::_link(ctx_,oldpath_,newpath_,st_,timeouts_); if(rv == -EXDEV) - rv = ::_link_exdev(ctx_,oldpath,newpath,st_,timeouts_); + rv = ::_link_exdev(ctx_,oldpath_,newpath_,st_,timeouts_); return rv; } diff --git a/src/fuse_link.hpp b/src/fuse_link.hpp index 22760920..91ddc423 100644 --- a/src/fuse_link.hpp +++ b/src/fuse_link.hpp @@ -25,8 +25,8 @@ namespace FUSE { int link(const fuse_req_ctx_t *ctx, - const char *oldpath, - const char *newpath, + const fs::relpath &oldpath, + const fs::relpath &newpath, struct stat *st, fuse_timeouts_t *timeouts); } diff --git a/src/fuse_listxattr.cpp b/src/fuse_listxattr.cpp index ce84e64e..47998dc0 100644 --- a/src/fuse_listxattr.cpp +++ b/src/fuse_listxattr.cpp @@ -25,7 +25,6 @@ #include "fuse.h" -#include #include #include @@ -33,12 +32,12 @@ static ssize_t _listxattr_size(const std::vector &branches_, - const fs::path &fusepath_) + const fs::relpath &fusepath_) { ssize_t size; ssize_t err; bool success; - fs::path fullpath; + fs::relpath fullpath = fusepath_; if(branches_.empty()) return -ENOENT; @@ -50,7 +49,7 @@ _listxattr_size(const std::vector &branches_, { ssize_t rv; - fullpath = branch->path / fusepath_; + fullpath.set_prefix(branch->path); rv = fs::llistxattr(fullpath,NULL,0); if(rv < 0) @@ -73,7 +72,7 @@ _listxattr_size(const std::vector &branches_, static ssize_t _listxattr(const std::vector &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, char *list_, size_t size_) { @@ -81,7 +80,7 @@ _listxattr(const std::vector &branches_, ssize_t size; ssize_t err; bool success; - fs::path fullpath; + fs::relpath fullpath = fusepath_; if(size_ == 0) return ::_listxattr_size(branches_,fusepath_); @@ -91,7 +90,7 @@ _listxattr(const std::vector &branches_, success = false; for(const auto branch : branches_) { - fullpath = branch->path / fusepath_; + fullpath.set_prefix(branch->path); rv = fs::llistxattr(fullpath,list_,size_); if(rv < 0) @@ -122,7 +121,7 @@ static int _listxattr(const Policy::Search &searchFunc_, const Branches::Ptr ibranches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, char *list_, const size_t size_) { @@ -141,13 +140,12 @@ _listxattr(const Policy::Search &searchFunc_, int FUSE::listxattr(const fuse_req_ctx_t *ctx_, - const char *fusepath_, + const fs::relpath &fusepath_, char *list_, size_t size_) { - const fs::path fusepath{fusepath_}; - if(Config::is_ctrl_file(fusepath)) + if(Config::is_ctrl_file(fusepath_)) return cfg.keys_listxattr(list_,size_); switch(cfg.xattr) @@ -162,7 +160,7 @@ FUSE::listxattr(const fuse_req_ctx_t *ctx_, return ::_listxattr(cfg.func.listxattr.policy, cfg.branches, - fusepath, + fusepath_, list_, size_); } diff --git a/src/fuse_listxattr.hpp b/src/fuse_listxattr.hpp index 258a5b84..909e3677 100644 --- a/src/fuse_listxattr.hpp +++ b/src/fuse_listxattr.hpp @@ -19,6 +19,7 @@ #pragma once #include "fuse_req_ctx.h" +#include "fs_path.hpp" #include @@ -26,7 +27,7 @@ namespace FUSE { int listxattr(const fuse_req_ctx_t *ctx, - const char *fusepath, + const fs::relpath &fusepath, char *buf, size_t count); } diff --git a/src/fuse_mkdir.cpp b/src/fuse_mkdir.cpp index bd3e9c68..e670bdb7 100644 --- a/src/fuse_mkdir.cpp +++ b/src/fuse_mkdir.cpp @@ -36,7 +36,7 @@ static int _mkdir_core(const ugid_t ugid_, - const fs::path &fullpath_, + const fs::relpath &fullpath_, mode_t mode_, const mode_t umask_) { @@ -48,16 +48,16 @@ _mkdir_core(const ugid_t ugid_, static int -_mkdir_loop_core(const ugid_t ugid_, - const fs::path &createpath_, - const fs::path &fusepath_, - const mode_t mode_, - const mode_t umask_) +_mkdir_loop_core(const ugid_t ugid_, + const std::string &createpath_, + const fs::relpath &fusepath_, + const mode_t mode_, + const mode_t umask_) { int rv; - fs::path fullpath; + fs::relpath fullpath; - fullpath = createpath_ / fusepath_; + fullpath.assign_concat(createpath_,fusepath_); rv = ::_mkdir_core(ugid_,fullpath,mode_,umask_); @@ -69,8 +69,8 @@ int _mkdir_loop(const ugid_t ugid_, const Branch *existingbranch_, const std::vector &createbranches_, - const fs::path &fusepath_, - const fs::path &fusedirpath_, + const fs::relpath &fusepath_, + const fs::relpath &fusedirpath_, const mode_t mode_, const mode_t umask_) { @@ -104,12 +104,12 @@ _mkdir(const ugid_t ugid_, const Policy::Search &getattrPolicy_, const Policy::Create &mkdirPolicy_, const Branches::Ptr branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, const mode_t mode_, const mode_t umask_) { int rv; - fs::path fusedirpath; + fs::relpath fusedirpath; std::vector createbranches; std::vector existingbranches; @@ -138,17 +138,16 @@ _mkdir(const ugid_t ugid_, int FUSE::mkdir(const fuse_req_ctx_t *ctx_, - const char *fusepath_, + const fs::relpath &fusepath_, mode_t mode_) { int rv; - const fs::path fusepath{fusepath_}; rv = ::_mkdir(ctx_, cfg.func.getattr.policy, cfg.func.mkdir.policy, cfg.branches, - fusepath, + fusepath_, mode_, ctx_->umask); if(rv == -EROFS) @@ -158,7 +157,7 @@ FUSE::mkdir(const fuse_req_ctx_t *ctx_, cfg.func.getattr.policy, cfg.func.mkdir.policy, cfg.branches, - fusepath, + fusepath_, mode_, ctx_->umask); } diff --git a/src/fuse_mkdir.hpp b/src/fuse_mkdir.hpp index a179b8d4..e0a0bb0f 100644 --- a/src/fuse_mkdir.hpp +++ b/src/fuse_mkdir.hpp @@ -19,6 +19,7 @@ #pragma once #include "fuse_req_ctx.h" +#include "fs_path.hpp" #include @@ -27,6 +28,6 @@ namespace FUSE { int mkdir(const fuse_req_ctx_t *ctx, - const char *fusepath, + const fs::relpath &fusepath, mode_t mode); } diff --git a/src/fuse_mknod.cpp b/src/fuse_mknod.cpp index 889f1a96..7444ca6b 100644 --- a/src/fuse_mknod.cpp +++ b/src/fuse_mknod.cpp @@ -37,7 +37,7 @@ static inline int _mknod_core(const ugid_t ugid_, - const fs::path &fullpath_, + const fs::relpath &fullpath_, mode_t mode_, const mode_t umask_, const dev_t dev_) @@ -50,17 +50,17 @@ _mknod_core(const ugid_t ugid_, static int -_mknod_loop_core(const ugid_t ugid_, - const fs::path &createbranch_, - const fs::path &fusepath_, - const mode_t mode_, - const mode_t umask_, - const dev_t dev_) +_mknod_loop_core(const ugid_t ugid_, + const std::string &createbranch_, + const fs::relpath &fusepath_, + const mode_t mode_, + const mode_t umask_, + const dev_t dev_) { int rv; - fs::path fullpath; + fs::relpath fullpath; - fullpath = createbranch_ / fusepath_; + fullpath.assign_concat(createbranch_,fusepath_); rv = ::_mknod_core(ugid_,fullpath,mode_,umask_,dev_); @@ -70,10 +70,10 @@ _mknod_loop_core(const ugid_t ugid_, static int _mknod_loop(const ugid_t ugid_, - const fs::path &existingbranch_, + const std::string &existingbranch_, const std::vector &createbranches_, - const fs::path &fusepath_, - const fs::path &fusedirpath_, + const fs::relpath &fusepath_, + const fs::relpath &fusedirpath_, const mode_t mode_, const mode_t umask_, const dev_t dev_) @@ -109,13 +109,13 @@ _mknod(const ugid_t ugid_, const Policy::Search &searchFunc_, const Policy::Create &createFunc_, const Branches::Ptr branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, const mode_t mode_, const mode_t umask_, const dev_t dev_) { int rv; - fs::path fusedirpath; + fs::relpath fusedirpath; std::vector createbranches; std::vector existingbranches; @@ -145,18 +145,17 @@ _mknod(const ugid_t ugid_, int FUSE::mknod(const fuse_req_ctx_t *ctx_, - const char *fusepath_, + const fs::relpath &fusepath_, mode_t mode_, dev_t rdev_) { int rv; - const fs::path fusepath{fusepath_}; rv = ::_mknod(ctx_, cfg.func.getattr.policy, cfg.func.mknod.policy, cfg.branches, - fusepath, + fusepath_, mode_, ctx_->umask, rdev_); @@ -167,7 +166,7 @@ FUSE::mknod(const fuse_req_ctx_t *ctx_, cfg.func.getattr.policy, cfg.func.mknod.policy, cfg.branches, - fusepath, + fusepath_, mode_, ctx_->umask, rdev_); diff --git a/src/fuse_mknod.hpp b/src/fuse_mknod.hpp index 64fef191..5ca951b6 100644 --- a/src/fuse_mknod.hpp +++ b/src/fuse_mknod.hpp @@ -20,6 +20,7 @@ #pragma once #include "fuse_req_ctx.h" +#include "fs_path.hpp" #include @@ -28,7 +29,7 @@ namespace FUSE { int mknod(const fuse_req_ctx_t *ctx, - const char *fusepath, + const fs::relpath &fusepath, mode_t mode, dev_t rdev); } diff --git a/src/fuse_open.cpp b/src/fuse_open.cpp index 1d247079..dd327aef 100644 --- a/src/fuse_open.cpp +++ b/src/fuse_open.cpp @@ -123,8 +123,8 @@ _rdonly(const int flags_) static int -_lchmod_and_open_if_not_writable_and_empty(const fs::path &fullpath_, - const int flags_) +_lchmod_and_open_if_not_writable_and_empty(const std::string &fullpath_, + const int flags_) { int rv; struct stat st; @@ -151,7 +151,7 @@ _lchmod_and_open_if_not_writable_and_empty(const fs::path &fullpath_, static int -_nfsopenhack(const fs::path &fullpath_, +_nfsopenhack(const std::string &fullpath_, const int flags_, const NFSOpenHack nfsopenhack_) { @@ -163,7 +163,7 @@ _nfsopenhack(const fs::path &fullpath_, case NFSOpenHack::ENUM::GIT: if(::_rdonly(flags_)) return -EACCES; - if(fullpath_.string().find("/.git/") == std::string::npos) + if(fullpath_.find("/.git/") == std::string::npos) return -EACCES; return ::_lchmod_and_open_if_not_writable_and_empty(fullpath_,flags_); case NFSOpenHack::ENUM::ALL: @@ -262,9 +262,9 @@ _config_to_ffi_flags(const int tid_, static int -_open_path(const fs::path &filepath_, +_open_path(const std::string &filepath_, const Branch *branch_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, fuse_file_info_t *ffi_, const NFSOpenHack nfsopenhack_) { @@ -288,7 +288,7 @@ static int _open_fd(const int fd_, const Branch *branch_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, fuse_file_info_t *ffi_) { int fd; @@ -309,13 +309,13 @@ static int _open(const Policy::Search &searchFunc_, const Branches::Ptr ibranches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, fuse_file_info_t *ffi_, const bool link_cow_, const NFSOpenHack nfsopenhack_) { int rv; - fs::path filepath; + std::string filepath; std::vector obranches; rv = searchFunc_(ibranches_,fusepath_,obranches); @@ -324,7 +324,9 @@ _open(const Policy::Search &searchFunc_, if(obranches.empty()) return -ENOENT; - filepath = obranches[0]->path / fusepath_; + filepath = obranches[0]->path; + filepath += '/'; + filepath.append(fusepath_.native()); if(link_cow_ && fs::cow::is_eligible(filepath,ffi_->flags)) fs::cow::break_link(filepath); @@ -349,7 +351,7 @@ _(const PassthroughIOEnum e_, static int _open(const fuse_req_ctx_t *ctx_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, fuse_file_info_t *ffi_) { int rv; @@ -484,10 +486,9 @@ _open(const fuse_req_ctx_t *ctx_, int FUSE::open(const fuse_req_ctx_t *ctx_, - const char *fusepath_, + const fs::relpath &fusepath_, fuse_file_info_t *ffi_) { - const fs::path fusepath{fusepath_}; - return ::_open(ctx_,fusepath,ffi_); + return ::_open(ctx_,fusepath_,ffi_); } diff --git a/src/fuse_open.hpp b/src/fuse_open.hpp index fede0d93..d6f65ba8 100644 --- a/src/fuse_open.hpp +++ b/src/fuse_open.hpp @@ -25,6 +25,6 @@ namespace FUSE { int open(const fuse_req_ctx_t *ctx, - const char *fusepath, + const fs::relpath &fusepath, fuse_file_info_t *ffi); } diff --git a/src/fuse_opendir.cpp b/src/fuse_opendir.cpp index a9003c50..544fc4ee 100644 --- a/src/fuse_opendir.cpp +++ b/src/fuse_opendir.cpp @@ -26,7 +26,7 @@ int FUSE::opendir(const fuse_req_ctx_t *ctx_, - const char *fusepath_, + const fs::relpath &fusepath_, fuse_file_info_t *ffi_) { DirInfo *di; diff --git a/src/fuse_opendir.hpp b/src/fuse_opendir.hpp index 0669bf26..77c6bb2f 100644 --- a/src/fuse_opendir.hpp +++ b/src/fuse_opendir.hpp @@ -25,6 +25,6 @@ namespace FUSE { int opendir(const fuse_req_ctx_t *ctx, - const char *fusepath, + const fs::relpath &fusepath, fuse_file_info_t *ffi); } diff --git a/src/fuse_readdir_cor.cpp b/src/fuse_readdir_cor.cpp index a8ef5c21..475c6adf 100644 --- a/src/fuse_readdir_cor.cpp +++ b/src/fuse_readdir_cor.cpp @@ -54,7 +54,7 @@ inline int _concurrent_readdir(ThreadPool &tp_, const Branches::Ptr branches_, - const fs::path &rel_dirpath_, + const fs::relpath &rel_dirpath_, fuse_dirents_t *dirents_) { HashSet names; diff --git a/src/fuse_readdir_cor_getdents.icpp b/src/fuse_readdir_cor_getdents.icpp index 3df46a5b..ce428b42 100644 --- a/src/fuse_readdir_cor_getdents.icpp +++ b/src/fuse_readdir_cor_getdents.icpp @@ -35,15 +35,15 @@ static inline int -_readdir(const fs::path &branch_path_, - const fs::path &rel_dirpath_, +_readdir(const std::string &branch_path_, + const fs::relpath &rel_dirpath_, HashSet &names_, fuse_dirents_t *dirents_, mutex_t &mutex_) { int fd; fuse_msgbuf_t *buf; - fs::path abs_dirpath; + fs::relpath abs_dirpath; abs_dirpath = branch_path_ / rel_dirpath_; diff --git a/src/fuse_readdir_cor_readdir.icpp b/src/fuse_readdir_cor_readdir.icpp index 3c944917..172e77c6 100644 --- a/src/fuse_readdir_cor_readdir.icpp +++ b/src/fuse_readdir_cor_readdir.icpp @@ -50,14 +50,14 @@ _dirent_exact_namelen(const struct dirent *d_) static inline int -_readdir(const fs::path &branch_path_, - const fs::path &rel_dirpath_, +_readdir(const std::string &branch_path_, + const fs::relpath &rel_dirpath_, HashSet &names_, fuse_dirents_t *dirents_, mutex_t &mutex_) { DIR *dir; - fs::path abs_dirpath; + fs::relpath abs_dirpath; abs_dirpath = branch_path_ / rel_dirpath_; diff --git a/src/fuse_readdir_cosr.cpp b/src/fuse_readdir_cosr.cpp index 6d7bb721..60802369 100644 --- a/src/fuse_readdir_cosr.cpp +++ b/src/fuse_readdir_cosr.cpp @@ -46,7 +46,7 @@ inline int _readdir(ThreadPool &tp_, const Branches::Ptr branches_, - const fs::path &rel_dirpath_, + const fs::relpath &rel_dirpath_, fuse_dirents_t *dirents_) { int rv; diff --git a/src/fuse_readdir_cosr_getdents.icpp b/src/fuse_readdir_cosr_getdents.icpp index 52a48adb..e70763fb 100644 --- a/src/fuse_readdir_cosr_getdents.icpp +++ b/src/fuse_readdir_cosr_getdents.icpp @@ -37,7 +37,7 @@ struct DirRV { - const fs::path *branch_path; + const std::string *branch_path; int fd; }; @@ -46,7 +46,7 @@ inline std::vector> _opendir(ThreadPool &tp_, const Branches::Ptr &branches_, - const fs::path &rel_dirpath_) + const fs::relpath &rel_dirpath_) { std::vector> futures; @@ -58,7 +58,7 @@ _opendir(ThreadPool &tp_, [&branch,&rel_dirpath_]() { int fd; - fs::path abs_dirpath; + fs::relpath abs_dirpath; abs_dirpath = branch.path / rel_dirpath_; @@ -79,7 +79,7 @@ static inline int _readdir(std::vector> &dh_futures_, - const fs::path &rel_dirpath_, + const fs::relpath &rel_dirpath_, fuse_dirents_t *dirents_) { Err err; diff --git a/src/fuse_readdir_cosr_readdir.icpp b/src/fuse_readdir_cosr_readdir.icpp index e8689199..5a0f7956 100644 --- a/src/fuse_readdir_cosr_readdir.icpp +++ b/src/fuse_readdir_cosr_readdir.icpp @@ -32,7 +32,7 @@ struct DirRV { - const fs::path *branch_path; + const std::string *branch_path; DIR *dir; int err; }; @@ -55,7 +55,7 @@ inline std::vector> _opendir(ThreadPool &tp_, const Branches::Ptr &branches_, - const fs::path &rel_dirpath_) + const fs::relpath &rel_dirpath_) { std::vector> futures; @@ -67,7 +67,7 @@ _opendir(ThreadPool &tp_, [&branch,&rel_dirpath_]() { DIR *dir; - fs::path abs_dirpath; + fs::relpath abs_dirpath; abs_dirpath = branch.path / rel_dirpath_; @@ -89,7 +89,7 @@ static inline int _readdir(std::vector> &dh_futures_, - const fs::path &rel_dirpath_, + const fs::relpath &rel_dirpath_, fuse_dirents_t *dirents_) { Err err; diff --git a/src/fuse_readdir_seq_getdents.icpp b/src/fuse_readdir_seq_getdents.icpp index da99bb56..009e1967 100644 --- a/src/fuse_readdir_seq_getdents.icpp +++ b/src/fuse_readdir_seq_getdents.icpp @@ -34,12 +34,12 @@ static int _readdir(const Branches::Ptr &branches_, - const fs::path &rel_dirpath_, + const fs::relpath &rel_dirpath_, fuse_dirents_t *dirents_) { Err err; HashSet names; - fs::path abs_dirpath; + fs::relpath abs_dirpath; fuse_msgbuf_t *buf; fuse_dirents_reset(dirents_); diff --git a/src/fuse_readdir_seq_readdir.icpp b/src/fuse_readdir_seq_readdir.icpp index 4ca4225c..ef4690c2 100644 --- a/src/fuse_readdir_seq_readdir.icpp +++ b/src/fuse_readdir_seq_readdir.icpp @@ -47,12 +47,12 @@ _dirent_exact_namelen(const struct dirent *d_) static int _readdir(const Branches::Ptr &branches_, - const fs::path &rel_dirpath_, + const fs::relpath &rel_dirpath_, fuse_dirents_t *dirents_) { Err err; HashSet names; - fs::path abs_dirpath; + fs::relpath abs_dirpath; fuse_dirents_reset(dirents_); diff --git a/src/fuse_readlink.cpp b/src/fuse_readlink.cpp index cb23a894..85976d79 100644 --- a/src/fuse_readlink.cpp +++ b/src/fuse_readlink.cpp @@ -33,42 +33,39 @@ static ssize_t -_readlink_core_symlinkify(const fs::path &fullpath_, +_readlink_core_symlinkify(const fs::relpath &fullpath_, char *buf_, const size_t bufsize_, const time_t symlinkify_timeout_) { ssize_t rv; struct stat st; - std::string fullpath_str; - fullpath_str = fullpath_.string(); - - rv = fs::lstat(fullpath_str,&st); + rv = fs::lstat(fullpath_,&st); if(rv < 0) return rv; if(!symlinkify::can_be_symlink(st,symlinkify_timeout_)) return fs::readlink(fullpath_,buf_,bufsize_); - rv = std::min(fullpath_str.size(),bufsize_); - memcpy(buf_,fullpath_str.c_str(),rv); + rv = std::min(fullpath_.size(),bufsize_); + memcpy(buf_,fullpath_.data(),rv); return rv; } static ssize_t -_readlink_core(const fs::path &basepath_, - const fs::path &fusepath_, +_readlink_core(const fs::relpath &basepath_, + const fs::relpath &fusepath_, char *buf_, const size_t bufsize_, const bool symlinkify_, const time_t symlinkify_timeout_) { - fs::path fullpath; + fs::relpath fullpath; - fullpath = basepath_ / fusepath_; + fullpath.assign_concat(basepath_,fusepath_); if(symlinkify_) return ::_readlink_core_symlinkify(fullpath,buf_,bufsize_,symlinkify_timeout_); @@ -80,7 +77,7 @@ static ssize_t _readlink(const Policy::Search &searchFunc_, const Branches::Ptr ibranches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, char *buf_, const size_t bufsize_, const bool symlinkify_, @@ -105,18 +102,17 @@ _readlink(const Policy::Search &searchFunc_, ssize_t FUSE::readlink(const fuse_req_ctx_t *ctx_, - const char *fusepath_, + const fs::relpath &fusepath_, char *buf_, size_t bufsize_) { if(bufsize_ == 0) return 0; - const fs::path fusepath{fusepath_}; return ::_readlink(cfg.func.readlink.policy, cfg.branches, - fusepath, + fusepath_, buf_, bufsize_, cfg.symlinkify, diff --git a/src/fuse_readlink.hpp b/src/fuse_readlink.hpp index 531b33f1..becbfd45 100644 --- a/src/fuse_readlink.hpp +++ b/src/fuse_readlink.hpp @@ -19,6 +19,7 @@ #pragma once #include "fuse_req_ctx.h" +#include "fs_path.hpp" #include @@ -27,7 +28,7 @@ namespace FUSE { ssize_t readlink(const fuse_req_ctx_t *ctx, - const char *fusepath, + const fs::relpath &fusepath, char *buf, size_t size); } diff --git a/src/fuse_removexattr.cpp b/src/fuse_removexattr.cpp index f43bb411..6a895a1d 100644 --- a/src/fuse_removexattr.cpp +++ b/src/fuse_removexattr.cpp @@ -29,33 +29,24 @@ #include -static -void -_removexattr_loop_core(const fs::path &basepath_, - const fs::path &fusepath_, - const char *attrname_, - PolicyRV *prv_) -{ - int rv; - fs::path fullpath; - - fullpath = basepath_ / fusepath_; - - rv = fs::lremovexattr(fullpath,attrname_); - - prv_->insert(rv,basepath_); -} - static void _removexattr_loop(const std::vector &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, const char *attrname_, PolicyRV *prv_) { + fs::relpath fullpath = fusepath_; + for(auto &branch : branches_) { - ::_removexattr_loop_core(branch->path,fusepath_,attrname_,prv_); + int rv; + + fullpath.set_prefix(branch->path); + + rv = fs::lremovexattr(fullpath,attrname_); + + prv_->insert(rv,branch->path); } } @@ -64,7 +55,7 @@ int _removexattr(const Policy::Action &actionFunc_, const Policy::Search &searchFunc_, const Branches::Ptr ibranches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, const char *attrname_) { int rv; @@ -95,12 +86,11 @@ _removexattr(const Policy::Action &actionFunc_, int FUSE::removexattr(const fuse_req_ctx_t *ctx_, - const char *fusepath_, + const fs::relpath &fusepath_, const char *attrname_) { - const fs::path fusepath{fusepath_}; - if(Config::is_ctrl_file(fusepath)) + if(Config::is_ctrl_file(fusepath_)) return -ENOATTR; if(cfg.xattr.to_int()) @@ -109,6 +99,6 @@ FUSE::removexattr(const fuse_req_ctx_t *ctx_, return ::_removexattr(cfg.func.removexattr.policy, cfg.func.getxattr.policy, cfg.branches, - fusepath, + fusepath_, attrname_); } diff --git a/src/fuse_removexattr.hpp b/src/fuse_removexattr.hpp index e64b5857..7c52b8ed 100644 --- a/src/fuse_removexattr.hpp +++ b/src/fuse_removexattr.hpp @@ -19,12 +19,13 @@ #pragma once #include "fuse_req_ctx.h" +#include "fs_path.hpp" namespace FUSE { int removexattr(const fuse_req_ctx_t *ctx, - const char *fusepath, + const fs::relpath &fusepath, const char *attrname); } diff --git a/src/fuse_rename.cpp b/src/fuse_rename.cpp index 9aeebdba..76ae1b3f 100644 --- a/src/fuse_rename.cpp +++ b/src/fuse_rename.cpp @@ -72,16 +72,16 @@ int _rename_create_path(const Policy::Search &searchPolicy_, const Policy::Action &actionPolicy_, const Branches::Ptr branches_, - const fs::path &oldfusepath_, - const fs::path &newfusepath_) + const fs::relpath &oldfusepath_, + const fs::relpath &newfusepath_) { int rv; Err err; StrVec toremove; std::vector newbranches; std::vector oldbranches; - fs::path oldfullpath; - fs::path newfullpath; + fs::relpath oldfullpath = oldfusepath_; + fs::relpath newfullpath = newfusepath_; rv = actionPolicy_(branches_,oldfusepath_,oldbranches); if(rv < 0) @@ -97,15 +97,15 @@ _rename_create_path(const Policy::Search &searchPolicy_, for(auto &branch : *branches_) { - newfullpath = branch.path / newfusepath_; + newfullpath.set_prefix(branch.path); if(!::_contains(oldbranches,branch.path)) { - toremove.push_back(newfullpath); + toremove.emplace_back(newfullpath.native()); continue; } - oldfullpath = branch.path / oldfusepath_; + oldfullpath.set_prefix(branch.path); rv = fs::rename(oldfullpath,newfullpath); if(rv < 0) @@ -119,7 +119,7 @@ _rename_create_path(const Policy::Search &searchPolicy_, err = rv; if(rv < 0) - toremove.push_back(oldfullpath); + toremove.emplace_back(oldfullpath.native()); } if(err == 0) @@ -132,15 +132,15 @@ static int _rename_preserve_path(const Policy::Action &actionPolicy_, const Branches::Ptr branches_, - const fs::path &oldfusepath_, - const fs::path &newfusepath_) + const fs::relpath &oldfusepath_, + const fs::relpath &newfusepath_) { int rv; bool success; StrVec toremove; std::vector oldbranches; - fs::path oldfullpath; - fs::path newfullpath; + fs::relpath oldfullpath = oldfusepath_; + fs::relpath newfullpath = newfusepath_; rv = actionPolicy_(branches_,oldfusepath_,oldbranches); if(rv < 0) @@ -149,20 +149,20 @@ _rename_preserve_path(const Policy::Action &actionPolicy_, success = false; for(auto &branch : *branches_) { - newfullpath = branch.path / newfusepath_; + newfullpath.set_prefix(branch.path); if(!::_contains(oldbranches,branch.path)) { - toremove.push_back(newfullpath); + toremove.emplace_back(newfullpath.native()); continue; } - oldfullpath = branch.path / oldfusepath_; + oldfullpath.set_prefix(branch.path); rv = fs::rename(oldfullpath,newfullpath); if(rv < 0) { - toremove.push_back(oldfullpath); + toremove.emplace_back(oldfullpath.native()); continue; } @@ -181,10 +181,10 @@ _rename_preserve_path(const Policy::Action &actionPolicy_, static void _rename_exdev_rename_back(const std::vector &branches_, - const fs::path &oldfusepath_) + const fs::relpath &oldfusepath_) { - fs::path oldpath; - fs::path newpath; + fs::relpath oldpath; + fs::relpath newpath; for(auto &branch : branches_) { @@ -203,12 +203,12 @@ static int _rename_exdev_rename_target(const Policy::Action &actionPolicy_, const Branches::Ptr ibranches_, - const fs::path &oldfusepath_, + const fs::relpath &oldfusepath_, std::vector &obranches_) { int rv; - fs::path clonesrc; - fs::path clonedst; + fs::relpath clonesrc; + fs::relpath clonedst; rv = actionPolicy_(ibranches_,oldfusepath_,obranches_); if(rv < 0) @@ -220,11 +220,11 @@ _rename_exdev_rename_target(const Policy::Action &actionPolicy_, clonedst = branch->path; clonedst /= ".mergerfs_rename_exdev"; - rv = fs::clonepath(clonesrc,clonedst,oldfusepath_.parent_path()); + rv = fs::clonepath(clonesrc.native(),clonedst.native(),oldfusepath_.parent_path()); if(rv == -ENOENT) { fs::mkdir_as({0,0},clonedst,01777); - rv = fs::clonepath(clonesrc,clonedst,oldfusepath_.parent_path()); + rv = fs::clonepath(clonesrc.native(),clonedst.native(),oldfusepath_.parent_path()); } if(rv < 0) @@ -251,24 +251,24 @@ int _rename_exdev_rel_symlink(const fuse_req_ctx_t *ctx_, const Policy::Action &actionPolicy_, const Branches::Ptr branches_, - const fs::path &oldfusepath_, - const fs::path &newfusepath_) + const fs::relpath &oldfusepath_, + const fs::relpath &newfusepath_) { int rv; - fs::path target; - fs::path linkpath; + fs::relpath target; std::vector branches; rv = ::_rename_exdev_rename_target(actionPolicy_,branches_,oldfusepath_,branches); if(rv < 0) return rv; - linkpath = newfusepath_; - target = "/.mergerfs_rename_exdev"; + target = ".mergerfs_rename_exdev"; target /= oldfusepath_; - target = target.lexically_relative(linkpath.parent_path()); + target = target.lexically_relative(newfusepath_.parent_path()); + if(target.empty()) + return -EXDEV; - rv = FUSE::symlink(ctx_,target.c_str(),linkpath); + rv = FUSE::symlink(ctx_,target.c_str(),newfusepath_); if(rv < 0) ::_rename_exdev_rename_back(branches,oldfusepath_); @@ -280,25 +280,23 @@ int _rename_exdev_abs_symlink(const fuse_req_ctx_t *ctx_, const Policy::Action &actionPolicy_, const Branches::Ptr branches_, - const fs::path &mount_, - const fs::path &oldfusepath_, - const fs::path &newfusepath_) + const std::string &mount_, + const fs::relpath &oldfusepath_, + const fs::relpath &newfusepath_) { int rv; - fs::path target; - fs::path linkpath; + fs::relpath target; std::vector branches; rv = ::_rename_exdev_rename_target(actionPolicy_,branches_,oldfusepath_,branches); if(rv < 0) return rv; - linkpath = newfusepath_; target = mount_; target /= ".mergerfs_rename_exdev"; target /= oldfusepath_; - rv = FUSE::symlink(ctx_,target.c_str(),linkpath); + rv = FUSE::symlink(ctx_,target.c_str(),newfusepath_); if(rv < 0) ::_rename_exdev_rename_back(branches,oldfusepath_); @@ -308,8 +306,8 @@ _rename_exdev_abs_symlink(const fuse_req_ctx_t *ctx_, static int _rename_exdev(const fuse_req_ctx_t *ctx_, - const fs::path &oldfusepath_, - const fs::path &newfusepath_) + const fs::relpath &oldfusepath_, + const fs::relpath &newfusepath_) { switch(cfg.rename_exdev) { @@ -335,8 +333,8 @@ _rename_exdev(const fuse_req_ctx_t *ctx_, static int -_rename(const fs::path &oldpath_, - const fs::path &newpath_) +_rename(const fs::relpath &oldpath_, + const fs::relpath &newpath_) { if(cfg.func.create.policy.path_preserving() && !cfg.ignorepponrename) return ::_rename_preserve_path(cfg.func.rename.policy, @@ -353,16 +351,14 @@ _rename(const fs::path &oldpath_, int FUSE::rename(const fuse_req_ctx_t *ctx_, - const char *oldfusepath_, - const char *newfusepath_) + const fs::relpath &oldfusepath_, + const fs::relpath &newfusepath_) { int rv; - const fs::path oldfusepath{oldfusepath_}; - const fs::path newfusepath{newfusepath_}; - rv = ::_rename(oldfusepath,newfusepath); + rv = ::_rename(oldfusepath_,newfusepath_); if(rv == -EXDEV) - return ::_rename_exdev(ctx_,oldfusepath,newfusepath); + return ::_rename_exdev(ctx_,oldfusepath_,newfusepath_); return rv; } diff --git a/src/fuse_rename.hpp b/src/fuse_rename.hpp index 72fe90df..67af6f02 100644 --- a/src/fuse_rename.hpp +++ b/src/fuse_rename.hpp @@ -19,12 +19,13 @@ #pragma once #include "fuse_req_ctx.h" +#include "fs_path.hpp" namespace FUSE { int rename(const fuse_req_ctx_t *ctx, - const char *from, - const char *to); + const fs::relpath &from, + const fs::relpath &to); } diff --git a/src/fuse_rmdir.cpp b/src/fuse_rmdir.cpp index ccecd2dc..4bc48d77 100644 --- a/src/fuse_rmdir.cpp +++ b/src/fuse_rmdir.cpp @@ -85,35 +85,26 @@ _should_unlink(int rv_, (followsymlinks_ != FollowSymlinks::ENUM::NEVER)); } -static -int -_rmdir_core(const fs::path &basepath_, - const fs::path &fusepath_, - const FollowSymlinks followsymlinks_) -{ - int rv; - fs::path fullpath; - - fullpath = basepath_ / fusepath_; - - rv = fs::rmdir(fullpath); - if(::_should_unlink(rv,followsymlinks_)) - rv = fs::unlink(fullpath); - - return rv; -} - static int _rmdir_loop(const std::vector &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, const FollowSymlinks followsymlinks_) { RmdirErr err; + fs::relpath fullpath = fusepath_; for(const auto &branch : branches_) { - err = ::_rmdir_core(branch->path,fusepath_,followsymlinks_); + int rv; + + fullpath.set_prefix(branch->path); + + rv = fs::rmdir(fullpath); + if(::_should_unlink(rv,followsymlinks_)) + rv = fs::unlink(fullpath); + + err = rv; } return err; @@ -124,7 +115,7 @@ int _rmdir(const Policy::Action &actionFunc_, const Branches::Ptr branches_, const FollowSymlinks followsymlinks_, - const fs::path &fusepath_) + const fs::relpath &fusepath_) { int rv; std::vector branches; @@ -138,12 +129,10 @@ _rmdir(const Policy::Action &actionFunc_, int FUSE::rmdir(const fuse_req_ctx_t *ctx_, - const char *fusepath_) + const fs::relpath &fusepath_) { - const fs::path fusepath{fusepath_}; - return ::_rmdir(cfg.func.rmdir.policy, cfg.branches, cfg.follow_symlinks, - fusepath); + fusepath_); } diff --git a/src/fuse_rmdir.hpp b/src/fuse_rmdir.hpp index df4fee8f..25a74979 100644 --- a/src/fuse_rmdir.hpp +++ b/src/fuse_rmdir.hpp @@ -19,11 +19,12 @@ #pragma once #include "fuse_req_ctx.h" +#include "fs_path.hpp" namespace FUSE { int rmdir(const fuse_req_ctx_t *ctx, - const char *fusepath); + const fs::relpath &fusepath); } diff --git a/src/fuse_setxattr.cpp b/src/fuse_setxattr.cpp index 260d2beb..cb291f09 100644 --- a/src/fuse_setxattr.cpp +++ b/src/fuse_setxattr.cpp @@ -100,44 +100,27 @@ _setxattr_ctrl_file(const char *attrname_, return rv; } -static -void -_setxattr_loop_core(const fs::path &basepath_, - const fs::path &fusepath_, - const char *attrname_, - const char *attrval_, - const size_t attrvalsize_, - const int flags_, - PolicyRV *prv_) -{ - int rv; - fs::path fullpath; - - fullpath = basepath_ / fusepath_; - - rv = fs::lsetxattr(fullpath,attrname_,attrval_,attrvalsize_,flags_); - - prv_->insert(rv,basepath_); -} - static void _setxattr_loop(const std::vector &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, const char *attrname_, const char *attrval_, const size_t attrvalsize_, const int flags_, PolicyRV *prv_) { + fs::relpath fullpath = fusepath_; + for(auto &branch : branches_) { - ::_setxattr_loop_core(branch->path, - fusepath_, - attrname_, - attrval_,attrvalsize_, - flags_, - prv_); + int rv; + + fullpath.set_prefix(branch->path); + + rv = fs::lsetxattr(fullpath,attrname_,attrval_,attrvalsize_,flags_); + + prv_->insert(rv,branch->path); } } @@ -146,7 +129,7 @@ int _setxattr(const Policy::Action &setxattrPolicy_, const Policy::Search &getxattrPolicy_, const Branches::Ptr branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, const char *attrname_, const char *attrval_, const size_t attrvalsize_, @@ -181,7 +164,7 @@ _setxattr(const Policy::Action &setxattrPolicy_, static int _setxattr(const fuse_req_ctx_t *ctx_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, const char *attrname_, const char *attrval_, size_t attrvalsize_, @@ -207,22 +190,21 @@ _setxattr(const fuse_req_ctx_t *ctx_, int FUSE::setxattr(const fuse_req_ctx_t *ctx_, - const char *fusepath_, + const fs::relpath &fusepath_, const char *attrname_, const char *attrval_, size_t attrvalsize_, int flags_) { - const fs::path fusepath{fusepath_}; - if(Config::is_ctrl_file(fusepath)) + if(Config::is_ctrl_file(fusepath_)) return ::_setxattr_ctrl_file(attrname_, attrval_, attrvalsize_, flags_); return ::_setxattr(ctx_, - fusepath, + fusepath_, attrname_, attrval_, attrvalsize_, diff --git a/src/fuse_setxattr.hpp b/src/fuse_setxattr.hpp index a0d0245e..3d3b5ce6 100644 --- a/src/fuse_setxattr.hpp +++ b/src/fuse_setxattr.hpp @@ -21,13 +21,14 @@ #include #include "fuse_req_ctx.h" +#include "fs_path.hpp" namespace FUSE { int setxattr(const fuse_req_ctx_t *ctx, - const char *fusepath, + const fs::relpath &fusepath, const char *attrname, const char *attrval, size_t attrvalize, diff --git a/src/fuse_statfs.cpp b/src/fuse_statfs.cpp index 7671883f..9f38fa4c 100644 --- a/src/fuse_statfs.cpp +++ b/src/fuse_statfs.cpp @@ -27,7 +27,6 @@ #include "fuse.h" -#include #include #include #include @@ -81,7 +80,7 @@ _should_ignore(const StatFSIgnore ignore_, static int _statfs(const Branches::Ptr branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, const StatFS mode_, const StatFSIgnore ignore_, struct statvfs *fsstat_) @@ -93,15 +92,17 @@ _statfs(const Branches::Ptr branches_, unsigned long min_frsize; unsigned long min_namemax; std::map fsstats; - fs::path fullpath; + fs::relpath fullpath; min_bsize = std::numeric_limits::max(); min_frsize = std::numeric_limits::max(); min_namemax = std::numeric_limits::max(); + if(mode_ == StatFS::ENUM::FULL) + fullpath = fusepath_; for(const auto &branch : *branches_) { if(mode_ == StatFS::ENUM::FULL) - fullpath = branch.path / fusepath_; + fullpath.set_prefix(branch.path); else fullpath = branch.path; @@ -148,13 +149,12 @@ _statfs(const Branches::Ptr branches_, int FUSE::statfs(const fuse_req_ctx_t *ctx_, - const char *fusepath_, + const fs::relpath &fusepath_, struct statvfs *st_) { - const fs::path fusepath{fusepath_}; return ::_statfs(cfg.branches, - fusepath, + fusepath_, cfg.statfs, cfg.statfs_ignore, st_); diff --git a/src/fuse_statfs.hpp b/src/fuse_statfs.hpp index 15f03241..9e747b77 100644 --- a/src/fuse_statfs.hpp +++ b/src/fuse_statfs.hpp @@ -19,6 +19,7 @@ #pragma once #include "fuse_req_ctx.h" +#include "fs_path.hpp" #include @@ -27,6 +28,6 @@ namespace FUSE { int statfs(const fuse_req_ctx_t *ctx, - const char *fusepath, + const fs::relpath &fusepath, struct statvfs *fsstat); } diff --git a/src/fuse_statx.hpp b/src/fuse_statx.hpp index ca3d920f..5000a6a5 100644 --- a/src/fuse_statx.hpp +++ b/src/fuse_statx.hpp @@ -26,7 +26,7 @@ namespace FUSE { int statx(const fuse_req_ctx_t *ctx, - const char *fusepath, + const fs::relpath &fusepath, cu32 flags, cu32 mask, struct fuse_statx *st, diff --git a/src/fuse_statx_supported.icpp b/src/fuse_statx_supported.icpp index 5ec930a8..0c6c7fdb 100644 --- a/src/fuse_statx_supported.icpp +++ b/src/fuse_statx_supported.icpp @@ -39,7 +39,7 @@ static void -_set_stat_if_leads_to_dir(const fs::path &path_, +_set_stat_if_leads_to_dir(const fs::relpath &path_, struct fuse_statx *st_) { int rv; @@ -57,7 +57,7 @@ _set_stat_if_leads_to_dir(const fs::path &path_, static void -_set_stat_if_leads_to_reg(const fs::path &path_, +_set_stat_if_leads_to_reg(const fs::relpath &path_, struct fuse_statx *st_) { int rv; @@ -125,7 +125,7 @@ static int _statx(const Policy::Search &searchFunc_, const Branches::Ptr branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, const uint32_t flags_, const uint32_t mask_, struct fuse_statx *st_, @@ -134,7 +134,7 @@ _statx(const Policy::Search &searchFunc_, FollowSymlinks followsymlinks_) { int rv; - fs::path fullpath; + fs::relpath fullpath; std::vector branches; rv = searchFunc_(branches_,fusepath_,branches); @@ -182,7 +182,7 @@ _statx(const Policy::Search &searchFunc_, static int -_statx(const fs::path &fusepath_, +_statx(const fs::relpath &fusepath_, const uint32_t flags_, const uint32_t mask_, struct fuse_statx *st_, @@ -236,18 +236,16 @@ _statx(const FileInfo *fi_, int FUSE::statx(const fuse_req_ctx_t *ctx_, - const char *fusepath_, + const fs::relpath &fusepath_, const uint32_t flags_, const uint32_t mask_, struct fuse_statx *st_, fuse_timeouts_t *timeout_) { - const fs::path fusepath{fusepath_}; - - if(Config::is_ctrl_file(fusepath)) + if(Config::is_ctrl_file(fusepath_)) return ::_statx_controlfile(st_,timeout_); - return ::_statx(fusepath, + return ::_statx(fusepath_, flags_|AT_STATX_DONT_SYNC, mask_, st_, diff --git a/src/fuse_statx_unsupported.icpp b/src/fuse_statx_unsupported.icpp index 7894cfd3..d4667219 100644 --- a/src/fuse_statx_unsupported.icpp +++ b/src/fuse_statx_unsupported.icpp @@ -20,7 +20,7 @@ int FUSE::statx(const fuse_req_ctx_t *ctx_, - const char *fusepath_, + const fs::relpath &fusepath_, const uint32_t flags_, const uint32_t mask_, struct fuse_statx *st_, diff --git a/src/fuse_symlink.cpp b/src/fuse_symlink.cpp index 5dbc818a..dac0e92e 100644 --- a/src/fuse_symlink.cpp +++ b/src/fuse_symlink.cpp @@ -37,14 +37,14 @@ static int -_symlink_loop_core(const ugid_t ugid_, - const fs::path &newbranch_, - const char *target_, - const fs::path &linkpath_, - struct stat *st_) +_symlink_loop_core(const ugid_t ugid_, + const std::string &newbranch_, + const char *target_, + const fs::relpath &linkpath_, + struct stat *st_) { int rv; - fs::path fullnewpath; + fs::relpath fullnewpath; fullnewpath = newbranch_ / linkpath_; @@ -64,11 +64,11 @@ _symlink_loop_core(const ugid_t ugid_, static int _symlink_loop(const ugid_t ugid_, - const fs::path &existingbranch_, + const std::string &existingbranch_, const std::vector &newbranches_, const char *target_, - const fs::path &linkpath_, - const fs::path &newdirpath_, + const fs::relpath &linkpath_, + const fs::relpath &newdirpath_, struct stat *st_) { int rv; @@ -99,11 +99,11 @@ _symlink(const ugid_t ugid_, const Policy::Create &createFunc_, const Branches::Ptr branches_, const char *target_, - const fs::path &linkpath_, + const fs::relpath &linkpath_, struct stat *st_) { int rv; - fs::path newdirpath; + fs::relpath newdirpath; std::vector newbranches; std::vector existingbranches; @@ -133,7 +133,7 @@ _symlink(const ugid_t ugid_, int FUSE::symlink(const fuse_req_ctx_t *ctx_, const char *target_, - const fs::path &linkpath_, + const fs::relpath &linkpath_, struct stat *st_, fuse_timeouts_t *timeouts_) { @@ -178,18 +178,3 @@ FUSE::symlink(const fuse_req_ctx_t *ctx_, return rv; } -int -FUSE::symlink(const fuse_req_ctx_t *ctx_, - const char *target_, - const char *linkpath_, - struct stat *st_, - fuse_timeouts_t *timeouts_) -{ - const fs::path linkpath{linkpath_}; - - return FUSE::symlink(ctx_, - target_, - linkpath, - st_, - timeouts_); -} diff --git a/src/fuse_symlink.hpp b/src/fuse_symlink.hpp index 1369329d..81a1024a 100644 --- a/src/fuse_symlink.hpp +++ b/src/fuse_symlink.hpp @@ -31,14 +31,7 @@ namespace FUSE int symlink(const fuse_req_ctx_t *ctx, const char *target, - const char *linkpath, - struct stat *st = NULL, - fuse_timeouts_t *timeouts = NULL); - - int - symlink(const fuse_req_ctx_t *ctx, - const char *target, - const fs::path &linkpath, + const fs::relpath &linkpath, struct stat *st = NULL, fuse_timeouts_t *timeouts = NULL); } diff --git a/src/fuse_tmpfile.cpp b/src/fuse_tmpfile.cpp index e00b5d2e..793c64bd 100644 --- a/src/fuse_tmpfile.cpp +++ b/src/fuse_tmpfile.cpp @@ -25,7 +25,7 @@ int FUSE::tmpfile(const fuse_req_ctx_t *ctx_, - const char *fusepath_, + const fs::relpath &fusepath_, mode_t mode_, fuse_file_info_t *ffi_) { diff --git a/src/fuse_tmpfile.hpp b/src/fuse_tmpfile.hpp index e9db0797..49be2b99 100644 --- a/src/fuse_tmpfile.hpp +++ b/src/fuse_tmpfile.hpp @@ -27,7 +27,7 @@ namespace FUSE { int tmpfile(const fuse_req_ctx_t *ctx, - const char *fusepath, + const fs::relpath &fusepath, mode_t mode, fuse_file_info_t *ffi); } diff --git a/src/fuse_truncate.cpp b/src/fuse_truncate.cpp index 6ea0f8a1..b83c263e 100644 --- a/src/fuse_truncate.cpp +++ b/src/fuse_truncate.cpp @@ -30,33 +30,24 @@ #include -static -void -_truncate_loop_core(const fs::path &basepath_, - const fs::path &fusepath_, - const off_t size_, - PolicyRV *prv_) -{ - int rv; - fs::path fullpath; - - fullpath = basepath_ / fusepath_; - - rv = fs::truncate(fullpath,size_); - - prv_->insert(rv,basepath_); -} - static void _truncate_loop(const std::vector &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, const off_t size_, PolicyRV *prv_) { + fs::relpath fullpath = fusepath_; + for(auto &branch : branches_) { - ::_truncate_loop_core(branch->path,fusepath_,size_,prv_); + int rv; + + fullpath.set_prefix(branch->path); + + rv = fs::truncate(fullpath,size_); + + prv_->insert(rv,branch->path); } } @@ -65,7 +56,7 @@ int _truncate(const Policy::Action &actionFunc_, const Policy::Search &searchFunc_, const Branches::Ptr branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, const off_t size_) { int rv; @@ -96,14 +87,13 @@ _truncate(const Policy::Action &actionFunc_, int FUSE::truncate(const fuse_req_ctx_t *ctx_, - const char *fusepath_, + const fs::relpath &fusepath_, off_t size_) { - const fs::path fusepath{fusepath_}; return ::_truncate(cfg.func.truncate.policy, cfg.func.getattr.policy, cfg.branches, - fusepath, + fusepath_, size_); } diff --git a/src/fuse_truncate.hpp b/src/fuse_truncate.hpp index 76e924e7..c696c93b 100644 --- a/src/fuse_truncate.hpp +++ b/src/fuse_truncate.hpp @@ -19,6 +19,7 @@ #pragma once #include "fuse_req_ctx.h" +#include "fs_path.hpp" #include @@ -27,6 +28,6 @@ namespace FUSE { int truncate(const fuse_req_ctx_t *ctx, - const char *fusepath, + const fs::relpath &fusepath, off_t size); } diff --git a/src/fuse_unlink.cpp b/src/fuse_unlink.cpp index abd7ee35..0374d823 100644 --- a/src/fuse_unlink.cpp +++ b/src/fuse_unlink.cpp @@ -34,14 +34,14 @@ static int _unlink_loop(const std::vector &branches_, - const fs::path &fusepath_) + const fs::relpath &fusepath_) { Err err; - fs::path fullpath; + fs::relpath fullpath = fusepath_; for(const auto &branch : branches_) { - fullpath = branch->path / fusepath_; + fullpath.set_prefix(branch->path); err = fs::unlink(fullpath); } @@ -53,7 +53,7 @@ static int _unlink(const Policy::Action &unlinkPolicy_, const Branches::Ptr branches_, - const fs::path &fusepath_) + const fs::relpath &fusepath_) { int rv; std::vector branches; @@ -67,11 +67,9 @@ _unlink(const Policy::Action &unlinkPolicy_, int FUSE::unlink(const fuse_req_ctx_t *ctx_, - const char *fusepath_) + const fs::relpath &fusepath_) { - const fs::path fusepath{fusepath_}; - return ::_unlink(cfg.func.unlink.policy, cfg.branches, - fusepath); + fusepath_); } diff --git a/src/fuse_unlink.hpp b/src/fuse_unlink.hpp index 9739935f..90409da0 100644 --- a/src/fuse_unlink.hpp +++ b/src/fuse_unlink.hpp @@ -19,11 +19,12 @@ #pragma once #include "fuse_req_ctx.h" +#include "fs_path.hpp" namespace FUSE { int unlink(const fuse_req_ctx_t *ctx, - const char *fusepath); + const fs::relpath &fusepath); } diff --git a/src/fuse_utimens.cpp b/src/fuse_utimens.cpp index 7fbec390..a0003e07 100644 --- a/src/fuse_utimens.cpp +++ b/src/fuse_utimens.cpp @@ -29,33 +29,24 @@ #include -static -void -_utimens_loop_core(const fs::path &basepath_, - const fs::path &fusepath_, - const timespec ts_[2], - PolicyRV *prv_) -{ - int rv; - fs::path fullpath; - - fullpath = basepath_ / fusepath_; - - rv = fs::lutimens(fullpath,ts_); - - prv_->insert(rv,basepath_); -} - static void _utimens_loop(const std::vector &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, const timespec ts_[2], PolicyRV *prv_) { + fs::relpath fullpath = fusepath_; + for(auto &branch : branches_) { - ::_utimens_loop_core(branch->path,fusepath_,ts_,prv_); + int rv; + + fullpath.set_prefix(branch->path); + + rv = fs::lutimens(fullpath,ts_); + + prv_->insert(rv,branch->path); } } @@ -64,7 +55,7 @@ int _utimens(const Policy::Action &utimensPolicy_, const Policy::Search &getattrPolicy_, const Branches::Ptr branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, const timespec ts_[2]) { int rv; @@ -95,14 +86,13 @@ _utimens(const Policy::Action &utimensPolicy_, int FUSE::utimens(const fuse_req_ctx_t *ctx_, - const char *fusepath_, + const fs::relpath &fusepath_, const timespec ts_[2]) { - const fs::path fusepath{fusepath_}; return ::_utimens(cfg.func.utimens.policy, cfg.func.getattr.policy, cfg.branches, - fusepath, + fusepath_, ts_); } diff --git a/src/fuse_utimens.hpp b/src/fuse_utimens.hpp index 9d91d9b5..cefc27d1 100644 --- a/src/fuse_utimens.hpp +++ b/src/fuse_utimens.hpp @@ -19,6 +19,7 @@ #pragma once #include "fuse_req_ctx.h" +#include "fs_path.hpp" #include @@ -27,6 +28,6 @@ namespace FUSE { int utimens(const fuse_req_ctx_t *ctx, - const char *fusepath, + const fs::relpath &fusepath, const timespec ts[2]); } diff --git a/src/mergerfs.cpp b/src/mergerfs.cpp index c4ad8903..7ec4e4d7 100644 --- a/src/mergerfs.cpp +++ b/src/mergerfs.cpp @@ -22,7 +22,6 @@ #include "caps.hpp" #include "config.hpp" -#include "fs_path.hpp" #include "fs_readahead.hpp" #include "fs_umount2.hpp" #include "fs_wait_for_mount.hpp" @@ -90,6 +89,7 @@ #include #include #include +#include #include @@ -189,7 +189,7 @@ bool _wait_for_mount() { int failures; - std::vector paths; + std::vector paths; std::chrono::milliseconds timeout; paths = cfg.branches->to_paths(); @@ -229,7 +229,7 @@ _wait_for_mount() static void -_lazy_umount(const fs::path &target_) +_lazy_umount(const std::string &target_) { int rv; @@ -238,15 +238,15 @@ _lazy_umount(const fs::path &target_) { case 0: SysLog::notice("{} has been successfully lazily unmounted", - target_.string()); + target_); break; case -EINVAL: SysLog::notice("{} was not a mount point needing to be unmounted", - target_.string()); + target_); break; default: SysLog::error("Error unmounting {}: {} - {}", - target_.string(), + target_, -rv, strerror(-rv)); break; @@ -377,10 +377,12 @@ int _pick_app_and_run(int argc_, char **argv_) { - fs::path appname; + std::string_view appname(argv_[0]); - appname = argv_[0]; - appname = appname.filename(); + // basename: drop everything up to and including the last '/'. + auto slash = appname.rfind('/'); + if(slash != std::string_view::npos) + appname.remove_prefix(slash + 1); if(appname == "fsck.mergerfs") return mergerfs::fsck::main(argc_,argv_); diff --git a/src/mergerfs_api.cpp b/src/mergerfs_api.cpp index 6f73a8d9..a43ac289 100644 --- a/src/mergerfs_api.cpp +++ b/src/mergerfs_api.cpp @@ -54,22 +54,22 @@ _lgetxattr(const std::string &input_path_, } bool -mergerfs::api::is_mergerfs(const fs::path &mountpoint_) +mergerfs::api::is_mergerfs(const std::string &mountpoint_) { - fs::path dot_mergerfs_filepath; + std::string dot_mergerfs_filepath; - dot_mergerfs_filepath = mountpoint_ / ".mergerfs"; + dot_mergerfs_filepath = mountpoint_ + "/.mergerfs"; return fs::exists(dot_mergerfs_filepath); } int -mergerfs::api::get_kvs(const fs::path &mountpoint_, +mergerfs::api::get_kvs(const std::string &mountpoint_, std::map *kvs_) { - fs::path dot_mergerfs_filepath; + std::string dot_mergerfs_filepath; - dot_mergerfs_filepath = mountpoint_ / ".mergerfs"; + dot_mergerfs_filepath = mountpoint_ + "/.mergerfs"; return fs::xattr::get(dot_mergerfs_filepath,kvs_); } diff --git a/src/mergerfs_api.hpp b/src/mergerfs_api.hpp index 0aa0e961..213a0cc6 100644 --- a/src/mergerfs_api.hpp +++ b/src/mergerfs_api.hpp @@ -18,8 +18,6 @@ #pragma once -#include "fs_path.hpp" - #include #include #include @@ -30,10 +28,10 @@ namespace mergerfs namespace api { bool - is_mergerfs(const fs::path &path); + is_mergerfs(const std::string &path); int - get_kvs(const fs::path &mountpoint, + get_kvs(const std::string &mountpoint, std::map *kvs); int diff --git a/src/mergerfs_collect_info.cpp b/src/mergerfs_collect_info.cpp index 5680eb3e..4a483dcc 100644 --- a/src/mergerfs_collect_info.cpp +++ b/src/mergerfs_collect_info.cpp @@ -101,7 +101,7 @@ _mount_point_stats(const std::string &output_) { std::vector allpaths; - mergerfs::api::allpaths(mount.dir.string(),allpaths); + mergerfs::api::allpaths(mount.dir,allpaths); for(const auto &path : allpaths) { ::_run({"stat",path.c_str()},output_); @@ -129,13 +129,13 @@ _mergerfs_settings(const std::string &output_) int rv; std::map kvs; - rv = mergerfs::api::get_kvs(mount.dir.string(),&kvs); + rv = mergerfs::api::get_kvs(mount.dir,&kvs); if(rv < 0) continue; std::string output_str; - output_str = fmt::format("=== {}/.mergerfs\n",mount.dir.string()); + output_str = fmt::format("=== {}/.mergerfs\n",mount.dir); for(const auto &[k,v] : kvs) output_str += fmt::format("{}={}\n",k,v); output_str += "\n\n"; diff --git a/src/mergerfs_fsck.cpp b/src/mergerfs_fsck.cpp index 73f9c208..0e8623b2 100644 --- a/src/mergerfs_fsck.cpp +++ b/src/mergerfs_fsck.cpp @@ -35,9 +35,11 @@ #include "base_types.h" -#include +#include +#include +#include -namespace FS = std::filesystem; +#include struct PathStat { @@ -380,32 +382,87 @@ _select_fix_func(const std::string &fix_) static void -_fsck(const FS::path &path_, - const FixFunc fix_func_, - const bool check_size_, - const bool copy_file_) +_fsck_walk(const std::string &path_, + const FixFunc fix_func_, + const bool check_size_, + const bool copy_file_) +{ + DIR *dir = ::opendir(path_.c_str()); + if(dir == NULL) + { + // EACCES is the common case (matches std::filesystem:: + // recursive_directory_iterator's skip_permission_denied); + // anything else is unexpected and worth surfacing. + if(errno != EACCES) + fmt::print(stderr, + "WARNING: cannot opendir {} - {}\n", + path_, + strerror(errno)); + return; + } + + // RAII for the DIR* so a throw from any of the helpers below + // (PathStatVec / std::string allocations) doesn't leak the fd. + struct dir_closer { void operator()(DIR *d) const noexcept { if(d) ::closedir(d); } }; + std::unique_ptr dir_guard(dir); + + struct dirent *ent; + while((ent = ::readdir(dir)) != NULL) + { + const char *name = ent->d_name; + if((name[0] == '.') && + ((name[1] == '\0') || ((name[1] == '.') && (name[2] == '\0')))) + continue; + + std::string sub = path_; + if(!sub.empty() && sub.back() != '/') + sub += '/'; + sub.append(name); + + PathStatVec paths; + int rv = ::_get_allpaths(sub,paths); + if(rv >= 0) + ::_compare_files(sub,paths,fix_func_,check_size_,copy_file_); + + // Prefer d_type when the filesystem populates it (the common + // case on local fs); fall back to lstat for DT_UNKNOWN. Symlinks + // are intentionally not followed -- this matches the original + // std::filesystem::recursive_directory_iterator default + // (directory_options::none) and avoids symlink loops or descent + // outside the branch. + bool is_dir; + if(ent->d_type == DT_DIR) + { + is_dir = true; + } + else if(ent->d_type == DT_UNKNOWN) + { + struct stat st; + is_dir = ((::lstat(sub.c_str(),&st) == 0) && S_ISDIR(st.st_mode)); + } + else + { + is_dir = false; + } + + if(is_dir) + ::_fsck_walk(sub,fix_func_,check_size_,copy_file_); + } +} + +static +void +_fsck(const std::string &path_, + const FixFunc fix_func_, + const bool check_size_, + const bool copy_file_) { - int rv; PathStatVec paths; ::_get_allpaths(path_,paths); ::_compare_files(path_,paths,fix_func_,check_size_,copy_file_); - auto opts = FS::directory_options::skip_permission_denied; - auto rdi = FS::recursive_directory_iterator(path_,opts); - for(const auto &de : rdi) - { - paths.clear(); - rv = ::_get_allpaths(de.path(),paths); - if(rv < 0) - continue; - - ::_compare_files(de.path(), - paths, - fix_func_, - check_size_, - copy_file_); - } + ::_fsck_walk(path_,fix_func_,check_size_,copy_file_); } int @@ -413,7 +470,7 @@ mergerfs::fsck::main(int argc_, char **argv_) { CLI::App app; - FS::path path; + std::string path; std::string fix; bool check_size; bool copy_file; diff --git a/src/option_parser.cpp b/src/option_parser.cpp index 7d885722..2af82e40 100644 --- a/src/option_parser.cpp +++ b/src/option_parser.cpp @@ -20,8 +20,8 @@ #include "ef.hpp" #include "errno.hpp" #include "fmt/core.h" +#include "fs_cleanpath.hpp" #include "fs_glob.hpp" -#include "fs_path.hpp" #include "fs_statvfs_cache.hpp" #include "hw_cpu.hpp" #include "num.hpp" @@ -44,6 +44,7 @@ #include #include #include +#include #include enum @@ -228,22 +229,31 @@ static void _check_for_mount_loop() { - fs::path mount; - std::vector branches; - std::error_code ec; + std::string mount; + std::vector branches; + struct stat mount_st; mount = cfg.mountpoint; branches = cfg.branches->to_paths(); + if(::stat(mount.c_str(),&mount_st) != 0) + return; + for(const auto &branch : branches) { - if(std::filesystem::equivalent(branch,mount,ec)) - { - std::string errstr; + struct stat branch_st; - errstr = fmt::format("branches can not include the mountpoint: {}", - branch.string()); - cfg.errs.push_back({0,errstr}); - } + if(::stat(branch.c_str(),&branch_st) != 0) + continue; + if(branch_st.st_dev != mount_st.st_dev) + continue; + if(branch_st.st_ino != mount_st.st_ino) + continue; + + std::string errstr; + + errstr = fmt::format("branches can not include the mountpoint: {}", + branch); + cfg.errs.push_back({0,errstr}); } } @@ -320,6 +330,7 @@ namespace options if(cfg.mountpoint.empty()) { cfg.mountpoint = prev_nonopt; + fs::cleanpath(&cfg.mountpoint); } else { diff --git a/src/policy.hpp b/src/policy.hpp index d0189f46..53eb20e9 100644 --- a/src/policy.hpp +++ b/src/policy.hpp @@ -40,7 +40,7 @@ namespace Policy public: std::string name; virtual int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const = 0; }; @@ -67,7 +67,7 @@ namespace Policy int operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &output_) const { return (*impl)(branches_,fusepath_,output_); @@ -94,7 +94,7 @@ namespace Policy std::string name; virtual bool path_preserving(void) const = 0; virtual int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const = 0; }; @@ -127,7 +127,7 @@ namespace Policy int operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &output_) const { return (*impl)(branches_,fusepath_,output_); @@ -153,7 +153,7 @@ namespace Policy public: std::string name; virtual int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const = 0; }; @@ -180,7 +180,7 @@ namespace Policy int operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &output_) const { return (*impl)(branches_,fusepath_,output_); diff --git a/src/policy_all.cpp b/src/policy_all.cpp index da55f4dd..3325f148 100644 --- a/src/policy_all.cpp +++ b/src/policy_all.cpp @@ -25,9 +25,7 @@ #include "policy.hpp" #include "policies.hpp" #include "policy_error.hpp" -#include "strvec.hpp" -#include static int @@ -62,7 +60,7 @@ _create(const Branches::Ptr &ibranches_, int Policy::All::Action::operator()(const Branches::Ptr &ibranches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &obranches_) const { return Policies::Action::epall(ibranches_,fusepath_,obranches_); @@ -70,7 +68,7 @@ Policy::All::Action::operator()(const Branches::Ptr &ibranches_, int Policy::All::Create::operator()(const Branches::Ptr &ibranches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &obranches_) const { return ::_create(ibranches_,obranches_); @@ -78,7 +76,7 @@ Policy::All::Create::operator()(const Branches::Ptr &ibranches_, int Policy::All::Search::operator()(const Branches::Ptr &ibranches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &obranches_) const { return Policies::Search::epall(ibranches_,fusepath_,obranches_); diff --git a/src/policy_all.hpp b/src/policy_all.hpp index c02ac123..8d47b8c4 100644 --- a/src/policy_all.hpp +++ b/src/policy_all.hpp @@ -33,7 +33,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; }; @@ -46,7 +46,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; bool path_preserving(void) const final { return false; } }; @@ -60,7 +60,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; }; } diff --git a/src/policy_epall.cpp b/src/policy_epall.cpp index c80aac66..2eae76cf 100644 --- a/src/policy_epall.cpp +++ b/src/policy_epall.cpp @@ -24,29 +24,26 @@ #include "fs_path.hpp" #include "fs_statvfs_cache.hpp" #include "policy.hpp" -#include "policy_epall.hpp" #include "policy_error.hpp" -#include "strvec.hpp" - -#include static int _create(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) { int rv; int error; fs::info_t info; + fs::relpath fullpath(fusepath_); error = ENOENT; for(auto &branch : *branches_) { if(branch.ro_or_nc()) error_and_continue(error,EROFS); - if(!fs::exists(branch.path,fusepath_)) + if(!fs::exists(fullpath,branch.path)) error_and_continue(error,ENOENT); rv = fs::info(branch.path,&info); if(rv < 0) @@ -68,19 +65,20 @@ _create(const Branches::Ptr &branches_, static int _action(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) { int rv; int error; bool readonly; + fs::relpath fullpath(fusepath_); error = ENOENT; for(auto &branch : *branches_) { if(branch.ro()) error_and_continue(error,EROFS); - if(!fs::exists(branch.path,fusepath_)) + if(!fs::exists(fullpath,branch.path)) error_and_continue(error,ENOENT); rv = fs::statvfs_cache_readonly(branch.path,&readonly); if(rv < 0) @@ -100,12 +98,14 @@ _action(const Branches::Ptr &branches_, static int _search(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) { + fs::relpath fullpath(fusepath_); + for(auto &branch : *branches_) { - if(!fs::exists(branch.path,fusepath_)) + if(!fs::exists(fullpath,branch.path)) continue; paths_.emplace_back(&branch); @@ -119,7 +119,7 @@ _search(const Branches::Ptr &branches_, int Policy::EPAll::Action::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return ::_action(branches_,fusepath_,paths_); @@ -127,7 +127,7 @@ Policy::EPAll::Action::operator()(const Branches::Ptr &branches_, int Policy::EPAll::Create::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return ::_create(branches_,fusepath_,paths_); @@ -135,7 +135,7 @@ Policy::EPAll::Create::operator()(const Branches::Ptr &branches_, int Policy::EPAll::Search::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return ::_search(branches_,fusepath_,paths_); diff --git a/src/policy_epall.hpp b/src/policy_epall.hpp index ffac86b7..395e22d0 100644 --- a/src/policy_epall.hpp +++ b/src/policy_epall.hpp @@ -34,7 +34,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; }; @@ -48,7 +48,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; bool path_preserving(void) const final { return true; } }; @@ -63,7 +63,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; }; } diff --git a/src/policy_epff.cpp b/src/policy_epff.cpp index 71323eeb..c38862ae 100644 --- a/src/policy_epff.cpp +++ b/src/policy_epff.cpp @@ -27,7 +27,6 @@ #include "policy.hpp" #include "policy_error.hpp" -#include #include using std::vector; @@ -35,19 +34,20 @@ using std::vector; static int _create(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) { int rv; int error; fs::info_t info; + fs::relpath fullpath(fusepath_); error = ENOENT; for(auto &branch : *branches_) { if(branch.ro_or_nc()) error_and_continue(error,EROFS); - if(!fs::exists(branch.path,fusepath_)) + if(!fs::exists(fullpath,branch.path)) error_and_continue(error,ENOENT); rv = fs::info(branch.path,&info); if(rv < 0) @@ -68,19 +68,20 @@ _create(const Branches::Ptr &branches_, static int _action(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) { int rv; int error; bool readonly; + fs::relpath fullpath(fusepath_); error = ENOENT; for(auto &branch : *branches_) { if(branch.ro()) error_and_continue(error,EROFS); - if(!fs::exists(branch.path,fusepath_)) + if(!fs::exists(fullpath,branch.path)) error_and_continue(error,ENOENT); rv = fs::statvfs_cache_readonly(branch.path,&readonly); if(rv < 0) @@ -99,12 +100,14 @@ _action(const Branches::Ptr &branches_, static int _search(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) { + fs::relpath fullpath(fusepath_); + for(auto &branch : *branches_) { - if(!fs::exists(branch.path,fusepath_)) + if(!fs::exists(fullpath,branch.path)) continue; paths_.emplace_back(&branch); @@ -117,7 +120,7 @@ _search(const Branches::Ptr &branches_, int Policy::EPFF::Action::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return ::_action(branches_,fusepath_,paths_); @@ -125,7 +128,7 @@ Policy::EPFF::Action::operator()(const Branches::Ptr &branches_, int Policy::EPFF::Create::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return ::_create(branches_,fusepath_,paths_); @@ -133,7 +136,7 @@ Policy::EPFF::Create::operator()(const Branches::Ptr &branches_, int Policy::EPFF::Search::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return ::_search(branches_,fusepath_,paths_); diff --git a/src/policy_epff.hpp b/src/policy_epff.hpp index 2e4bcbe5..4a8698ce 100644 --- a/src/policy_epff.hpp +++ b/src/policy_epff.hpp @@ -33,7 +33,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; }; @@ -46,7 +46,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; bool path_preserving(void) const final { return true; } }; @@ -60,7 +60,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; }; } diff --git a/src/policy_eplfs.cpp b/src/policy_eplfs.cpp index a94844b7..9d56391b 100644 --- a/src/policy_eplfs.cpp +++ b/src/policy_eplfs.cpp @@ -28,7 +28,6 @@ #include "policy_error.hpp" #include -#include #include using std::vector; @@ -36,7 +35,7 @@ using std::vector; static int _create(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) { int rv; @@ -44,6 +43,7 @@ _create(const Branches::Ptr &branches_, u64 eplfs; Branch *obranch; fs::info_t info; + fs::relpath fullpath(fusepath_); obranch = nullptr; error = ENOENT; @@ -52,7 +52,7 @@ _create(const Branches::Ptr &branches_, { if(branch.ro_or_nc()) error_and_continue(error,EROFS); - if(!fs::exists(branch.path,fusepath_)) + if(!fs::exists(fullpath,branch.path)) error_and_continue(error,ENOENT); rv = fs::info(branch.path,&info); if(rv < 0) @@ -79,7 +79,7 @@ _create(const Branches::Ptr &branches_, static int _action(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) { int rv; @@ -87,6 +87,7 @@ _action(const Branches::Ptr &branches_, u64 eplfs; Branch *obranch; fs::info_t info; + fs::relpath fullpath(fusepath_); obranch = nullptr; error = ENOENT; @@ -95,7 +96,7 @@ _action(const Branches::Ptr &branches_, { if(branch.ro()) error_and_continue(error,EROFS); - if(!fs::exists(branch.path,fusepath_)) + if(!fs::exists(fullpath,branch.path)) error_and_continue(error,ENOENT); rv = fs::info(branch.path,&info); if(rv < 0) @@ -120,19 +121,20 @@ _action(const Branches::Ptr &branches_, static int _search(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) { int rv; u64 eplfs; u64 spaceavail; Branch *obranch; + fs::relpath fullpath(fusepath_); obranch = nullptr; eplfs = std::numeric_limits::max(); for(auto &branch : *branches_) { - if(!fs::exists(branch.path,fusepath_)) + if(!fs::exists(fullpath,branch.path)) continue; rv = fs::statvfs_cache_spaceavail(branch.path,&spaceavail); if(rv < 0) @@ -154,7 +156,7 @@ _search(const Branches::Ptr &branches_, int Policy::EPLFS::Action::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return ::_action(branches_,fusepath_,paths_); @@ -162,7 +164,7 @@ Policy::EPLFS::Action::operator()(const Branches::Ptr &branches_, int Policy::EPLFS::Create::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return ::_create(branches_,fusepath_,paths_); @@ -170,7 +172,7 @@ Policy::EPLFS::Create::operator()(const Branches::Ptr &branches_, int Policy::EPLFS::Search::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return ::_search(branches_,fusepath_,paths_); diff --git a/src/policy_eplfs.hpp b/src/policy_eplfs.hpp index 4a215ea7..5fa005f5 100644 --- a/src/policy_eplfs.hpp +++ b/src/policy_eplfs.hpp @@ -33,7 +33,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; }; @@ -46,7 +46,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; bool path_preserving(void) const final { return true; } }; @@ -60,7 +60,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; }; } diff --git a/src/policy_eplus.cpp b/src/policy_eplus.cpp index 2884de11..17c38d83 100644 --- a/src/policy_eplus.cpp +++ b/src/policy_eplus.cpp @@ -27,12 +27,11 @@ #include "policy_error.hpp" #include -#include static int _create(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) { int rv; @@ -40,6 +39,7 @@ _create(const Branches::Ptr &branches_, u64 eplus; fs::info_t info; Branch *obranch; + fs::relpath fullpath(fusepath_); obranch = nullptr; error = ENOENT; @@ -48,7 +48,7 @@ _create(const Branches::Ptr &branches_, { if(branch.ro_or_nc()) error_and_continue(error,EROFS); - if(!fs::exists(branch.path,fusepath_)) + if(!fs::exists(fullpath,branch.path)) error_and_continue(error,ENOENT); rv = fs::info(branch.path,&info); if(rv < 0) @@ -75,7 +75,7 @@ _create(const Branches::Ptr &branches_, static int _action(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) { int rv; @@ -83,6 +83,7 @@ _action(const Branches::Ptr &branches_, u64 eplus; fs::info_t info; Branch *obranch; + fs::relpath fullpath(fusepath_); obranch = nullptr; error = ENOENT; @@ -91,7 +92,7 @@ _action(const Branches::Ptr &branches_, { if(branch.ro()) error_and_continue(error,EROFS); - if(!fs::exists(branch.path,fusepath_)) + if(!fs::exists(fullpath,branch.path)) error_and_continue(error,ENOENT); rv = fs::info(branch.path,&info); if(rv < 0) @@ -116,19 +117,20 @@ _action(const Branches::Ptr &branches_, static int _search(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) { int rv; u64 eplus; u64 spaceused; Branch *obranch; + fs::relpath fullpath(fusepath_); obranch = nullptr; eplus = std::numeric_limits::max(); for(auto &branch : *branches_) { - if(!fs::exists(branch.path,fusepath_)) + if(!fs::exists(fullpath,branch.path)) continue; rv = fs::statvfs_cache_spaceused(branch.path,&spaceused); if(rv < 0) @@ -150,7 +152,7 @@ _search(const Branches::Ptr &branches_, int Policy::EPLUS::Action::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return ::_action(branches_,fusepath_,paths_); @@ -158,7 +160,7 @@ Policy::EPLUS::Action::operator()(const Branches::Ptr &branches_, int Policy::EPLUS::Create::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return ::_create(branches_,fusepath_,paths_); @@ -166,7 +168,7 @@ Policy::EPLUS::Create::operator()(const Branches::Ptr &branches_, int Policy::EPLUS::Search::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return ::_search(branches_,fusepath_,paths_); diff --git a/src/policy_eplus.hpp b/src/policy_eplus.hpp index 92a11c9c..b5d7e442 100644 --- a/src/policy_eplus.hpp +++ b/src/policy_eplus.hpp @@ -33,7 +33,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; }; @@ -46,7 +46,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; bool path_preserving(void) const final { return true; } }; @@ -60,7 +60,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; }; } diff --git a/src/policy_epmfs.cpp b/src/policy_epmfs.cpp index ee66ddb4..b4ba12fb 100644 --- a/src/policy_epmfs.cpp +++ b/src/policy_epmfs.cpp @@ -24,17 +24,15 @@ #include "fs_path.hpp" #include "fs_statvfs_cache.hpp" #include "policy.hpp" -#include "policy_epmfs.hpp" #include "policy_error.hpp" #include -#include static int _create(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) { int rv; @@ -42,6 +40,7 @@ _create(const Branches::Ptr &branches_, u64 epmfs; fs::info_t info; Branch *obranch; + fs::relpath fullpath(fusepath_); obranch = nullptr; error = ENOENT; @@ -50,7 +49,7 @@ _create(const Branches::Ptr &branches_, { if(branch.ro_or_nc()) error_and_continue(error,EROFS); - if(!fs::exists(branch.path,fusepath_)) + if(!fs::exists(fullpath,branch.path)) error_and_continue(error,ENOENT); rv = fs::info(branch.path,&info); if(rv < 0) @@ -77,7 +76,7 @@ _create(const Branches::Ptr &branches_, static int _action(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) { int rv; @@ -85,6 +84,7 @@ _action(const Branches::Ptr &branches_, u64 epmfs; fs::info_t info; Branch *obranch; + fs::relpath fullpath(fusepath_); obranch = nullptr; error = ENOENT; @@ -93,7 +93,7 @@ _action(const Branches::Ptr &branches_, { if(branch.ro()) error_and_continue(error,EROFS); - if(!fs::exists(branch.path,fusepath_)) + if(!fs::exists(fullpath,branch.path)) error_and_continue(error,ENOENT); rv = fs::info(branch.path,&info); if(rv < 0) @@ -118,19 +118,20 @@ _action(const Branches::Ptr &branches_, static int _search(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) { int rv; u64 epmfs; u64 spaceavail; Branch *obranch; + fs::relpath fullpath(fusepath_); obranch = nullptr; epmfs = 0; for(auto &branch : *branches_) { - if(!fs::exists(branch.path,fusepath_)) + if(!fs::exists(fullpath,branch.path)) continue; rv = fs::statvfs_cache_spaceavail(branch.path,&spaceavail); if(rv < 0) @@ -152,7 +153,7 @@ _search(const Branches::Ptr &branches_, int Policy::EPMFS::Action::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return ::_action(branches_,fusepath_,paths_); @@ -160,7 +161,7 @@ Policy::EPMFS::Action::operator()(const Branches::Ptr &branches_, int Policy::EPMFS::Create::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return ::_create(branches_,fusepath_,paths_); @@ -168,7 +169,7 @@ Policy::EPMFS::Create::operator()(const Branches::Ptr &branches_, int Policy::EPMFS::Search::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return ::_search(branches_,fusepath_,paths_); diff --git a/src/policy_epmfs.hpp b/src/policy_epmfs.hpp index 48f2ce85..34358369 100644 --- a/src/policy_epmfs.hpp +++ b/src/policy_epmfs.hpp @@ -34,7 +34,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; }; @@ -48,7 +48,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; bool path_preserving(void) const final { return true; } }; @@ -63,7 +63,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; }; } diff --git a/src/policy_eppfrd.cpp b/src/policy_eppfrd.cpp index 6220d2ea..c4ea5c53 100644 --- a/src/policy_eppfrd.cpp +++ b/src/policy_eppfrd.cpp @@ -26,9 +26,7 @@ #include "policy.hpp" #include "policy_error.hpp" #include "rnd.hpp" -#include "strvec.hpp" -#include #include struct BranchInfo @@ -42,13 +40,14 @@ typedef std::vector BranchInfoVec; static int _get_branchinfo_create(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, BranchInfoVec *branchinfo_, u64 *sum_) { int rv; int error; fs::info_t info; + fs::relpath fullpath(fusepath_); *sum_ = 0; error = ENOENT; @@ -56,7 +55,7 @@ _get_branchinfo_create(const Branches::Ptr &branches_, { if(branch.ro_or_nc()) error_and_continue(error,EROFS); - if(!fs::exists(branch.path,fusepath_)) + if(!fs::exists(fullpath,branch.path)) error_and_continue(error,ENOENT); rv = fs::info(branch.path,&info); if(rv < 0) @@ -77,13 +76,14 @@ _get_branchinfo_create(const Branches::Ptr &branches_, static int _get_branchinfo_action(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, BranchInfoVec *branchinfo_, u64 *sum_) { int rv; int error; fs::info_t info; + fs::relpath fullpath(fusepath_); *sum_ = 0; error = ENOENT; @@ -91,7 +91,7 @@ _get_branchinfo_action(const Branches::Ptr &branches_, { if(branch.ro()) error_and_continue(error,EROFS); - if(!fs::exists(branch.path,fusepath_)) + if(!fs::exists(fullpath,branch.path)) error_and_continue(error,ENOENT); rv = fs::info(branch.path,&info); if(rv < 0) @@ -110,17 +110,18 @@ _get_branchinfo_action(const Branches::Ptr &branches_, static int _get_branchinfo_search(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, BranchInfoVec *branchinfo_, u64 *sum_) { int rv; u64 spaceavail; + fs::relpath fullpath(fusepath_); *sum_ = 0; for(auto &branch : *branches_) { - if(!fs::exists(branch.path,fusepath_)) + if(!fs::exists(fullpath,branch.path)) continue; rv = fs::statvfs_cache_spaceavail(branch.path,&spaceavail); if(rv < 0) @@ -166,7 +167,7 @@ _get_branch(const BranchInfoVec &branchinfo_, static int _create(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) { int err; @@ -187,7 +188,7 @@ _create(const Branches::Ptr &branches_, static int _action(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) { int err; @@ -208,7 +209,7 @@ _action(const Branches::Ptr &branches_, static int _search(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) { int err; @@ -228,7 +229,7 @@ _search(const Branches::Ptr &branches_, int Policy::EPPFRD::Action::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return ::_action(branches_,fusepath_,paths_); @@ -236,7 +237,7 @@ Policy::EPPFRD::Action::operator()(const Branches::Ptr &branches_, int Policy::EPPFRD::Create::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return ::_create(branches_,fusepath_,paths_); @@ -244,7 +245,7 @@ Policy::EPPFRD::Create::operator()(const Branches::Ptr &branches_, int Policy::EPPFRD::Search::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return ::_search(branches_,fusepath_,paths_); diff --git a/src/policy_eppfrd.hpp b/src/policy_eppfrd.hpp index 89dd58e5..fd71497a 100644 --- a/src/policy_eppfrd.hpp +++ b/src/policy_eppfrd.hpp @@ -33,7 +33,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; }; @@ -46,7 +46,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; bool path_preserving(void) const final { return true; } }; @@ -60,7 +60,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; }; } diff --git a/src/policy_eprand.cpp b/src/policy_eprand.cpp index 87552b9c..aafbaea5 100644 --- a/src/policy_eprand.cpp +++ b/src/policy_eprand.cpp @@ -27,7 +27,7 @@ int Policy::EPRand::Action::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { @@ -42,7 +42,7 @@ Policy::EPRand::Action::operator()(const Branches::Ptr &branches_, int Policy::EPRand::Create::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { int rv; @@ -56,7 +56,7 @@ Policy::EPRand::Create::operator()(const Branches::Ptr &branches_, int Policy::EPRand::Search::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { int rv; diff --git a/src/policy_eprand.hpp b/src/policy_eprand.hpp index c2e94c05..4bd02a76 100644 --- a/src/policy_eprand.hpp +++ b/src/policy_eprand.hpp @@ -33,7 +33,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; }; @@ -46,7 +46,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; bool path_preserving(void) const final { return true; } }; @@ -60,7 +60,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; }; } diff --git a/src/policy_erofs.cpp b/src/policy_erofs.cpp index 0d0e74f8..bca82079 100644 --- a/src/policy_erofs.cpp +++ b/src/policy_erofs.cpp @@ -21,12 +21,10 @@ #include "errno.hpp" #include "policy.hpp" -#include - int Policy::ERoFS::Action::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return -EROFS; @@ -34,7 +32,7 @@ Policy::ERoFS::Action::operator()(const Branches::Ptr &branches_, int Policy::ERoFS::Create::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return -EROFS; @@ -42,7 +40,7 @@ Policy::ERoFS::Create::operator()(const Branches::Ptr &branches_, int Policy::ERoFS::Search::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return -EROFS; diff --git a/src/policy_erofs.hpp b/src/policy_erofs.hpp index 0d7965bd..e6d70ec1 100644 --- a/src/policy_erofs.hpp +++ b/src/policy_erofs.hpp @@ -33,7 +33,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; }; @@ -46,7 +46,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; bool path_preserving(void) const final { return false; } }; @@ -60,7 +60,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; }; } diff --git a/src/policy_ff.cpp b/src/policy_ff.cpp index 5d632715..40190d5f 100644 --- a/src/policy_ff.cpp +++ b/src/policy_ff.cpp @@ -26,8 +26,6 @@ #include "policy.hpp" #include "policy_error.hpp" -#include - static int @@ -61,7 +59,7 @@ _create(const Branches::Ptr &ibranches_, int Policy::FF::Action::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return Policies::Action::epff(branches_,fusepath_,paths_); @@ -69,7 +67,7 @@ Policy::FF::Action::operator()(const Branches::Ptr &branches_, int Policy::FF::Create::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return ::_create(branches_,paths_); @@ -77,12 +75,14 @@ Policy::FF::Create::operator()(const Branches::Ptr &branches_, int Policy::FF::Search::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &output_) const { + fs::relpath fullpath(fusepath_); + for(auto &branch : *branches_) { - if(!fs::exists(branch.path,fusepath_)) + if(!fs::exists(fullpath,branch.path)) continue; output_.emplace_back(&branch); diff --git a/src/policy_ff.hpp b/src/policy_ff.hpp index 387ca23c..e35ca4db 100644 --- a/src/policy_ff.hpp +++ b/src/policy_ff.hpp @@ -33,7 +33,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; }; @@ -47,7 +47,7 @@ namespace Policy public: bool path_preserving(void) const final { return false; } int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; }; @@ -61,7 +61,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; }; } diff --git a/src/policy_lfs.cpp b/src/policy_lfs.cpp index 3e811ac3..4d1fdb5a 100644 --- a/src/policy_lfs.cpp +++ b/src/policy_lfs.cpp @@ -25,10 +25,8 @@ #include "policies.hpp" #include "policy.hpp" #include "policy_error.hpp" -#include "strvec.hpp" #include -#include static @@ -73,7 +71,7 @@ _create(const Branches::Ptr &branches_, int Policy::LFS::Action::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return Policies::Action::eplfs(branches_,fusepath_,paths_); @@ -81,7 +79,7 @@ Policy::LFS::Action::operator()(const Branches::Ptr &branches_, int Policy::LFS::Create::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return ::_create(branches_,paths_); @@ -89,7 +87,7 @@ Policy::LFS::Create::operator()(const Branches::Ptr &branches_, int Policy::LFS::Search::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return Policies::Search::eplfs(branches_,fusepath_,paths_); diff --git a/src/policy_lfs.hpp b/src/policy_lfs.hpp index 5a85d8a6..190158e0 100644 --- a/src/policy_lfs.hpp +++ b/src/policy_lfs.hpp @@ -33,7 +33,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; }; @@ -46,7 +46,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; bool path_preserving() const final { return false; } }; @@ -60,7 +60,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; }; } diff --git a/src/policy_lup.cpp b/src/policy_lup.cpp index a7f13db4..4a31f8a8 100644 --- a/src/policy_lup.cpp +++ b/src/policy_lup.cpp @@ -30,12 +30,11 @@ #include "base_types.h" #include -#include static int _action(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) { int rv; @@ -44,6 +43,7 @@ _action(const Branches::Ptr &branches_, Branch *obranch; u64 best_used; u64 best_total; + fs::relpath fullpath(fusepath_); best_used = 0; best_total = 1; @@ -54,7 +54,7 @@ _action(const Branches::Ptr &branches_, { if(branch.ro()) error_and_continue(error,EROFS); - if(!fs::exists(branch.path,fusepath_)) + if(!fs::exists(fullpath,branch.path)) error_and_continue(error,ENOENT); rv = fs::info(branch.path,&info); if(rv < 0) @@ -99,7 +99,7 @@ _action(const Branches::Ptr &branches_, static int _search(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) { int rv; @@ -108,6 +108,7 @@ _search(const Branches::Ptr &branches_, u64 best_used; u64 best_total; Branch *obranch; + fs::relpath fullpath(fusepath_); best_used = 0; best_total = 1; @@ -115,7 +116,7 @@ _search(const Branches::Ptr &branches_, for(auto &branch : *branches_) { - if(!fs::exists(branch.path,fusepath_)) + if(!fs::exists(fullpath,branch.path)) continue; rv = fs::statvfs_cache_spaceused(branch.path,&used); if(rv < 0) @@ -222,7 +223,7 @@ _create(const Branches::Ptr &branches_, int Policy::LUP::Action::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return ::_action(branches_,fusepath_,paths_); @@ -230,7 +231,7 @@ Policy::LUP::Action::operator()(const Branches::Ptr &branches_, int Policy::LUP::Create::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return ::_create(branches_,paths_); @@ -238,7 +239,7 @@ Policy::LUP::Create::operator()(const Branches::Ptr &branches_, int Policy::LUP::Search::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return ::_search(branches_,fusepath_,paths_); diff --git a/src/policy_lup.hpp b/src/policy_lup.hpp index 3ef19b53..6dd730e1 100644 --- a/src/policy_lup.hpp +++ b/src/policy_lup.hpp @@ -33,7 +33,7 @@ namespace Policy public: int operator()(const Branches::Ptr &, - const fs::path &, + const fs::relpath &, std::vector &) const final; }; @@ -46,7 +46,7 @@ namespace Policy public: int operator()(const Branches::Ptr &, - const fs::path &, + const fs::relpath &, std::vector &) const final; bool path_preserving() const final { return false; } }; @@ -60,7 +60,7 @@ namespace Policy public: int operator()(const Branches::Ptr &, - const fs::path &, + const fs::relpath &, std::vector &) const final; }; } diff --git a/src/policy_lus.cpp b/src/policy_lus.cpp index ea161737..eaf8232f 100644 --- a/src/policy_lus.cpp +++ b/src/policy_lus.cpp @@ -25,11 +25,8 @@ #include "policies.hpp" #include "policy.hpp" #include "policy_error.hpp" -#include "policy_lus.hpp" -#include "strvec.hpp" #include -#include #include using std::vector; @@ -76,7 +73,7 @@ _create(const Branches::Ptr &branches_, int Policy::LUS::Action::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return Policies::Action::eplus(branches_,fusepath_,paths_); @@ -84,7 +81,7 @@ Policy::LUS::Action::operator()(const Branches::Ptr &branches_, int Policy::LUS::Create::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return ::_create(branches_,paths_); @@ -92,7 +89,7 @@ Policy::LUS::Create::operator()(const Branches::Ptr &branches_, int Policy::LUS::Search::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return Policies::Search::eplus(branches_,fusepath_,paths_); diff --git a/src/policy_lus.hpp b/src/policy_lus.hpp index 813db455..31204e29 100644 --- a/src/policy_lus.hpp +++ b/src/policy_lus.hpp @@ -33,7 +33,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; }; @@ -46,7 +46,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; bool path_preserving() const final { return false; } }; @@ -60,7 +60,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; }; } diff --git a/src/policy_mfs.cpp b/src/policy_mfs.cpp index d7d9447f..cdf551bc 100644 --- a/src/policy_mfs.cpp +++ b/src/policy_mfs.cpp @@ -26,8 +26,6 @@ #include "policy.hpp" #include "policy_error.hpp" -#include - static int @@ -71,7 +69,7 @@ _create(const Branches::Ptr &branches_, int Policy::MFS::Action::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return Policies::Action::epmfs(branches_,fusepath_,paths_); @@ -79,7 +77,7 @@ Policy::MFS::Action::operator()(const Branches::Ptr &branches_, int Policy::MFS::Create::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return ::_create(branches_,paths_); @@ -87,7 +85,7 @@ Policy::MFS::Create::operator()(const Branches::Ptr &branches_, int Policy::MFS::Search::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return Policies::Search::epmfs(branches_,fusepath_,paths_); diff --git a/src/policy_mfs.hpp b/src/policy_mfs.hpp index e381d34a..cb454f8e 100644 --- a/src/policy_mfs.hpp +++ b/src/policy_mfs.hpp @@ -33,7 +33,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; }; @@ -46,7 +46,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; bool path_preserving() const final { return false; } }; @@ -60,7 +60,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; }; } diff --git a/src/policy_msplfs.cpp b/src/policy_msplfs.cpp index d3b88350..9595f57f 100644 --- a/src/policy_msplfs.cpp +++ b/src/policy_msplfs.cpp @@ -26,23 +26,21 @@ #include "policies.hpp" #include "policy.hpp" #include "policy_error.hpp" -#include "strvec.hpp" -#include #include -#include static Branch* _create_1(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, int *err_) { int rv; u64 lfs; fs::info_t info; Branch *obranch; + fs::relpath fullpath(fusepath_); obranch = nullptr; lfs = std::numeric_limits::max(); @@ -50,7 +48,7 @@ _create_1(const Branches::Ptr &branches_, { if(branch.ro_or_nc()) error_and_continue(*err_,EROFS); - if(!fs::exists(branch.path,fusepath_)) + if(!fs::exists(fullpath,branch.path)) error_and_continue(*err_,ENOENT); rv = fs::info(branch.path,&info); if(rv < 0) @@ -72,12 +70,12 @@ _create_1(const Branches::Ptr &branches_, static int _create(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) { int error; Branch *branch; - fs::path fusepath; + fs::relpath fusepath; error = ENOENT; fusepath = fusepath_; @@ -86,7 +84,7 @@ _create(const Branches::Ptr &branches_, branch = ::_create_1(branches_,fusepath,&error); if(branch) break; - if(fusepath == "/") + if(fusepath.empty()) break; fusepath = fusepath.parent_path(); } @@ -101,7 +99,7 @@ _create(const Branches::Ptr &branches_, int Policy::MSPLFS::Action::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return Policies::Action::eplfs(branches_,fusepath_,paths_); @@ -109,7 +107,7 @@ Policy::MSPLFS::Action::operator()(const Branches::Ptr &branches_, int Policy::MSPLFS::Create::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return ::_create(branches_,fusepath_,paths_); @@ -117,7 +115,7 @@ Policy::MSPLFS::Create::operator()(const Branches::Ptr &branches_, int Policy::MSPLFS::Search::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return Policies::Search::eplfs(branches_,fusepath_,paths_); diff --git a/src/policy_msplfs.hpp b/src/policy_msplfs.hpp index 774d5dd9..df788156 100644 --- a/src/policy_msplfs.hpp +++ b/src/policy_msplfs.hpp @@ -33,7 +33,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; }; @@ -46,7 +46,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; bool path_preserving() const final { return true; } }; @@ -60,7 +60,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; }; } diff --git a/src/policy_msplus.cpp b/src/policy_msplus.cpp index 6ce2e58a..ac3746af 100644 --- a/src/policy_msplus.cpp +++ b/src/policy_msplus.cpp @@ -33,13 +33,14 @@ static Branch* _create_1(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, int *err_) { int rv; u64 lus; fs::info_t info; Branch *obranch; + fs::relpath fullpath(fusepath_); obranch = nullptr; lus = std::numeric_limits::max(); @@ -47,7 +48,7 @@ _create_1(const Branches::Ptr &branches_, { if(branch.ro_or_nc()) error_and_continue(*err_,EROFS); - if(!fs::exists(branch.path,fusepath_)) + if(!fs::exists(fullpath,branch.path)) error_and_continue(*err_,ENOENT); rv = fs::info(branch.path,&info); if(rv < 0) @@ -69,12 +70,12 @@ _create_1(const Branches::Ptr &branches_, static int _create(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) { int err; Branch *branch; - fs::path fusepath; + fs::relpath fusepath; err = ENOENT; fusepath = fusepath_; @@ -83,7 +84,7 @@ _create(const Branches::Ptr &branches_, branch = ::_create_1(branches_,fusepath,&err); if(branch) break; - if(fusepath == "/") + if(fusepath.empty()) break; fusepath = fusepath.parent_path(); } @@ -98,7 +99,7 @@ _create(const Branches::Ptr &branches_, int Policy::MSPLUS::Action::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return Policies::Action::eplus(branches_,fusepath_,paths_); @@ -106,7 +107,7 @@ Policy::MSPLUS::Action::operator()(const Branches::Ptr &branches_, int Policy::MSPLUS::Create::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return ::_create(branches_,fusepath_,paths_); @@ -114,7 +115,7 @@ Policy::MSPLUS::Create::operator()(const Branches::Ptr &branches_, int Policy::MSPLUS::Search::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return Policies::Search::eplus(branches_,fusepath_,paths_); diff --git a/src/policy_msplus.hpp b/src/policy_msplus.hpp index d669c495..0e3a6be5 100644 --- a/src/policy_msplus.hpp +++ b/src/policy_msplus.hpp @@ -33,7 +33,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; }; @@ -46,7 +46,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; bool path_preserving() const final { return true; } }; @@ -60,7 +60,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; }; } diff --git a/src/policy_mspmfs.cpp b/src/policy_mspmfs.cpp index 0a090213..83374839 100644 --- a/src/policy_mspmfs.cpp +++ b/src/policy_mspmfs.cpp @@ -26,23 +26,22 @@ #include "policies.hpp" #include "policy.hpp" #include "policy_error.hpp" -#include "policy_mspmfs.hpp" #include -#include #include static Branch* _create_1(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, int *err_) { int rv; u64 mfs; fs::info_t info; Branch *obranch; + fs::relpath fullpath(fusepath_); obranch = nullptr; mfs = std::numeric_limits::min(); @@ -50,7 +49,7 @@ _create_1(const Branches::Ptr &branches_, { if(branch.ro_or_nc()) error_and_continue(*err_,EROFS); - if(!fs::exists(branch.path,fusepath_)) + if(!fs::exists(fullpath,branch.path)) error_and_continue(*err_,ENOENT); rv = fs::info(branch.path,&info); if(rv < 0) @@ -72,12 +71,12 @@ _create_1(const Branches::Ptr &branches_, static int _create(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) { int error; Branch *branch; - fs::path fusepath; + fs::relpath fusepath; error = ENOENT; fusepath = fusepath_; @@ -86,7 +85,7 @@ _create(const Branches::Ptr &branches_, branch = ::_create_1(branches_,fusepath,&error); if(branch) break; - if(fusepath == "/") + if(fusepath.empty()) break; fusepath = fusepath.parent_path(); @@ -102,7 +101,7 @@ _create(const Branches::Ptr &branches_, int Policy::MSPMFS::Action::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return Policies::Action::epmfs(branches_,fusepath_,paths_); @@ -110,7 +109,7 @@ Policy::MSPMFS::Action::operator()(const Branches::Ptr &branches_, int Policy::MSPMFS::Create::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return ::_create(branches_,fusepath_,paths_); @@ -118,7 +117,7 @@ Policy::MSPMFS::Create::operator()(const Branches::Ptr &branches_, int Policy::MSPMFS::Search::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return Policies::Search::epmfs(branches_,fusepath_,paths_); diff --git a/src/policy_mspmfs.hpp b/src/policy_mspmfs.hpp index 29e84a9d..47f3258b 100644 --- a/src/policy_mspmfs.hpp +++ b/src/policy_mspmfs.hpp @@ -33,7 +33,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; }; @@ -46,7 +46,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; bool path_preserving() const final { return true; } }; @@ -60,7 +60,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; }; } diff --git a/src/policy_msppfrd.cpp b/src/policy_msppfrd.cpp index a5c349f2..2153c0f7 100644 --- a/src/policy_msppfrd.cpp +++ b/src/policy_msppfrd.cpp @@ -43,13 +43,14 @@ typedef std::vector BranchInfoVec; static int _create_1(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, BranchInfoVec *branchinfo_, u64 *sum_) { int rv; int error; fs::info_t info; + fs::relpath fullpath(fusepath_); *sum_ = 0; error = ENOENT; @@ -57,7 +58,7 @@ _create_1(const Branches::Ptr &branches_, { if(branch.ro_or_nc()) error_and_continue(error,EROFS); - if(!fs::exists(branch.path,fusepath_)) + if(!fs::exists(fullpath,branch.path)) error_and_continue(error,ENOENT); rv = fs::info(branch.path,&info); if(rv < 0) @@ -78,12 +79,12 @@ _create_1(const Branches::Ptr &branches_, static int _get_branchinfo(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, BranchInfoVec *branchinfo_, u64 *sum_) { int rv; - fs::path fusepath; + fs::relpath fusepath; fusepath = fusepath_; for(;;) @@ -91,7 +92,7 @@ _get_branchinfo(const Branches::Ptr &branches_, rv = ::_create_1(branches_,fusepath,branchinfo_,sum_); if(branchinfo_->size()) break; - if(fusepath == "/") + if(fusepath.empty()) break; fusepath = fusepath.parent_path(); } @@ -128,7 +129,7 @@ _get_branch(const BranchInfoVec &branchinfo_, static int _create(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) { int rv; @@ -148,7 +149,7 @@ _create(const Branches::Ptr &branches_, int Policy::MSPPFRD::Action::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return Policies::Action::eppfrd(branches_,fusepath_,paths_); @@ -156,7 +157,7 @@ Policy::MSPPFRD::Action::operator()(const Branches::Ptr &branches_, int Policy::MSPPFRD::Create::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return ::_create(branches_,fusepath_,paths_); @@ -164,7 +165,7 @@ Policy::MSPPFRD::Create::operator()(const Branches::Ptr &branches_, int Policy::MSPPFRD::Search::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return Policies::Search::eppfrd(branches_,fusepath_,paths_); diff --git a/src/policy_msppfrd.hpp b/src/policy_msppfrd.hpp index 08ebe419..fbe25a91 100644 --- a/src/policy_msppfrd.hpp +++ b/src/policy_msppfrd.hpp @@ -33,7 +33,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; }; @@ -46,7 +46,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; bool path_preserving() const final { return true; }; }; @@ -60,7 +60,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; }; } diff --git a/src/policy_newest.cpp b/src/policy_newest.cpp index fe3a96a2..ca271369 100644 --- a/src/policy_newest.cpp +++ b/src/policy_newest.cpp @@ -26,7 +26,6 @@ #include "policy.hpp" #include "policy_error.hpp" -#include #include #include @@ -34,7 +33,7 @@ static int _create(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) { int rv; @@ -43,6 +42,7 @@ _create(const Branches::Ptr &branches_, struct stat st; fs::info_t info; Branch *obranch; + fs::relpath fullpath(fusepath_); obranch = nullptr; err = ENOENT; @@ -51,7 +51,7 @@ _create(const Branches::Ptr &branches_, { if(branch.ro_or_nc()) error_and_continue(err,EROFS); - if(!fs::exists(branch.path,fusepath_,&st)) + if(!fs::exists(fullpath,branch.path,&st)) error_and_continue(err,ENOENT); if(st.st_mtime < newest) continue; @@ -78,7 +78,7 @@ _create(const Branches::Ptr &branches_, static int _action(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) { int rv; @@ -87,6 +87,7 @@ _action(const Branches::Ptr &branches_, time_t newest; struct stat st; Branch *obranch; + fs::relpath fullpath(fusepath_); obranch = nullptr; err = ENOENT; @@ -95,7 +96,7 @@ _action(const Branches::Ptr &branches_, { if(branch.ro()) error_and_continue(err,EROFS); - if(!fs::exists(branch.path,fusepath_,&st)) + if(!fs::exists(fullpath,branch.path,&st)) error_and_continue(err,ENOENT); if(st.st_mtime < newest) continue; @@ -120,18 +121,19 @@ _action(const Branches::Ptr &branches_, static int _search(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) { time_t newest; struct stat st; Branch *obranch; + fs::relpath fullpath(fusepath_); obranch = nullptr; newest = std::numeric_limits::min(); for(auto &branch : *branches_) { - if(!fs::exists(branch.path,fusepath_,&st)) + if(!fs::exists(fullpath,branch.path,&st)) continue; if(st.st_mtime < newest) continue; @@ -150,7 +152,7 @@ _search(const Branches::Ptr &branches_, int Policy::Newest::Action::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return ::_action(branches_,fusepath_,paths_); @@ -158,7 +160,7 @@ Policy::Newest::Action::operator()(const Branches::Ptr &branches_, int Policy::Newest::Create::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return ::_create(branches_,fusepath_,paths_); @@ -166,7 +168,7 @@ Policy::Newest::Create::operator()(const Branches::Ptr &branches_, int Policy::Newest::Search::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return ::_search(branches_,fusepath_,paths_); diff --git a/src/policy_newest.hpp b/src/policy_newest.hpp index 10a37576..ee502552 100644 --- a/src/policy_newest.hpp +++ b/src/policy_newest.hpp @@ -33,7 +33,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; }; @@ -46,7 +46,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; bool path_preserving() const final { return false; } }; @@ -60,7 +60,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; }; } diff --git a/src/policy_pfrd.cpp b/src/policy_pfrd.cpp index f3b12960..1e5c24ec 100644 --- a/src/policy_pfrd.cpp +++ b/src/policy_pfrd.cpp @@ -24,11 +24,8 @@ #include "policies.hpp" #include "policy.hpp" #include "policy_error.hpp" -#include "policy_pfrd.hpp" #include "rnd.hpp" -#include "strvec.hpp" -#include #include @@ -101,7 +98,7 @@ _get_branch(const BranchInfoVec &branchinfo_, static int _create(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) { int err; @@ -121,7 +118,7 @@ _create(const Branches::Ptr &branches_, int Policy::PFRD::Action::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return Policies::Action::eppfrd(branches_,fusepath_,paths_); @@ -129,7 +126,7 @@ Policy::PFRD::Action::operator()(const Branches::Ptr &branches_, int Policy::PFRD::Create::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return ::_create(branches_,fusepath_,paths_); @@ -137,7 +134,7 @@ Policy::PFRD::Create::operator()(const Branches::Ptr &branches_, int Policy::PFRD::Search::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { return Policies::Search::eppfrd(branches_,fusepath_,paths_); diff --git a/src/policy_pfrd.hpp b/src/policy_pfrd.hpp index ec3e674d..1151bfd8 100644 --- a/src/policy_pfrd.hpp +++ b/src/policy_pfrd.hpp @@ -33,7 +33,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; }; @@ -46,7 +46,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; bool path_preserving() const final { return false; } }; @@ -60,7 +60,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; }; } diff --git a/src/policy_rand.cpp b/src/policy_rand.cpp index ced7537f..bfa6d174 100644 --- a/src/policy_rand.cpp +++ b/src/policy_rand.cpp @@ -25,7 +25,7 @@ int Policy::Rand::Action::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { int rv; @@ -39,7 +39,7 @@ Policy::Rand::Action::operator()(const Branches::Ptr &branches_, int Policy::Rand::Create::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { int rv; @@ -53,7 +53,7 @@ Policy::Rand::Create::operator()(const Branches::Ptr &branches_, int Policy::Rand::Search::operator()(const Branches::Ptr &branches_, - const fs::path &fusepath_, + const fs::relpath &fusepath_, std::vector &paths_) const { int rv; diff --git a/src/policy_rand.hpp b/src/policy_rand.hpp index ee394ebe..24a40526 100644 --- a/src/policy_rand.hpp +++ b/src/policy_rand.hpp @@ -33,7 +33,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; }; @@ -46,7 +46,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; bool path_preserving() const final { return false; } }; @@ -60,7 +60,7 @@ namespace Policy public: int operator()(const Branches::Ptr&, - const fs::path&, + const fs::relpath&, std::vector&) const final; }; } diff --git a/src/policy_rv.hpp b/src/policy_rv.hpp index c470b7e3..da91fc13 100644 --- a/src/policy_rv.hpp +++ b/src/policy_rv.hpp @@ -19,14 +19,15 @@ #pragma once #include +#include #include struct PolicyRV { struct RV { - RV(const int rv_, - const std::string &basepath_) + RV(const int rv_, + const std::string_view basepath_) : rv(rv_), basepath(basepath_) { @@ -40,8 +41,8 @@ struct PolicyRV std::vector errors; void - insert(const int rv_, - const std::string &basepath_) + insert(const int rv_, + const std::string_view basepath_) { if(rv_ >= 0) successes.push_back({rv_,basepath_}); @@ -50,7 +51,7 @@ struct PolicyRV } int - get_error(const std::string &basepath_) + get_error(const std::string_view basepath_) { for(const auto &s : successes) { diff --git a/src/symlinkify.hpp b/src/symlinkify.hpp index 6a07716f..ad7fb64b 100644 --- a/src/symlinkify.hpp +++ b/src/symlinkify.hpp @@ -22,7 +22,7 @@ #include "base_types.h" -#include +#include namespace symlinkify @@ -62,8 +62,8 @@ namespace symlinkify static inline void - convert(const std::string &target_, - struct stat *st_) + convert(const std::string_view target_, + struct stat *st_) { st_->st_mode = (((st_->st_mode & ~S_IFMT) | S_IFLNK) | 0777); st_->st_size = target_.size(); @@ -73,8 +73,8 @@ namespace symlinkify static inline void - convert(const std::string &target_, - struct fuse_statx *st_) + convert(const std::string_view target_, + struct fuse_statx *st_) { st_->mode = (((st_->mode & ~S_IFMT) | S_IFLNK) | 0777); st_->size = target_.size(); @@ -84,9 +84,9 @@ namespace symlinkify static inline void - convert_if_can_be_symlink(const std::string &target_, - struct stat *st_, - const s64 timeout_) + convert_if_can_be_symlink(const std::string_view target_, + struct stat *st_, + const s64 timeout_) { if(timeout_ < 0) return; @@ -99,9 +99,9 @@ namespace symlinkify static inline void - convert_if_can_be_symlink(const std::string &target_, - fuse_statx *st_, - const s64 timeout_) + convert_if_can_be_symlink(const std::string_view target_, + fuse_statx *st_, + const s64 timeout_) { if(timeout_ < 0) return; diff --git a/src/to_string.cpp b/src/to_string.cpp index 9a59828b..53a72ff1 100644 --- a/src/to_string.cpp +++ b/src/to_string.cpp @@ -52,9 +52,3 @@ str::to(const std::string &s_) { return s_; } - -std::string -str::to(const fs::path &path_) -{ - return path_.string(); -} diff --git a/src/to_string.hpp b/src/to_string.hpp index 53ec2e56..274ff4f7 100644 --- a/src/to_string.hpp +++ b/src/to_string.hpp @@ -19,9 +19,7 @@ #pragma once #include "base_types.h" -#include "fs_path.hpp" -#include #include @@ -32,5 +30,4 @@ namespace str std::string to(cu64); std::string to(cs64); std::string to(const std::string&); - std::string to(const fs::path&); } diff --git a/tests/tests.cpp b/tests/tests.cpp index 89ffee31..bea78358 100644 --- a/tests/tests.cpp +++ b/tests/tests.cpp @@ -1,8 +1,11 @@ #include "acutest/acutest.h" #include "config.hpp" +#include "fs_clonepath.hpp" #include "fs_copyfile.hpp" +#include "fs_exists.hpp" #include "fs_inode.hpp" +#include "fs_path.hpp" #include "from_string.hpp" #include "hashset.hpp" #include "num.hpp" @@ -12,6 +15,7 @@ #include "thread_pool.hpp" #include +#include #include #include #include @@ -664,7 +668,7 @@ test_branches_to_paths() TEST_CHECK(b.from_string("/tmp/a:/tmp/b:/tmp/c") == 0); Branches::Ptr p = b; - std::vector paths = (*p).to_paths(); + std::vector paths = (*p).to_paths(); TEST_CHECK(paths.size() == 3); TEST_CHECK(paths[0] == "/tmp/a"); TEST_CHECK(paths[1] == "/tmp/b"); @@ -1322,10 +1326,10 @@ test_config_inodecalc() void test_fs_inode_readdir_calc_matches_calc() { - const fs::path branch_path("/mnt/disk1"); - const std::vector dirpaths = { - fs::path(), - fs::path("tv/season_01"), + const std::string branch_path("/mnt/disk1"); + const std::vector dirpaths = { + fs::relpath(), + fs::relpath("tv/season_01"), }; const std::vector> entries = { {"episode_01.mkv", S_IFREG}, @@ -1354,9 +1358,9 @@ test_fs_inode_readdir_calc_matches_calc() for(const auto &[name,mode] : entries) { - const fs::path fullpath = dirpath / name; - const u64 expected = fs::inode::calc(branch_path.string(), - fullpath.string(), + const fs::relpath fullpath = dirpath / name; + const u64 expected = fs::inode::calc(branch_path, + fullpath, mode, ino); const u64 actual = calc.calc(name.c_str(), @@ -2969,9 +2973,9 @@ test_fs_copyfile_basic() int rv; int src_fd; struct stat st; - fs::path src_path; - fs::path dst_path; - fs::path tmp_dir; + fs::relpath src_path; + fs::relpath dst_path; + fs::relpath tmp_dir; char tmp_template[] = "/tmp/mergerfs-test-copyfile-basic-XXXXXX"; if(::mkdtemp(tmp_template) == nullptr) @@ -2988,7 +2992,7 @@ test_fs_copyfile_basic() TEST_CHECK(src_fd >= 0); if(src_fd < 0) { - std::filesystem::remove_all(tmp_dir); + std::filesystem::remove_all(std::filesystem::path(tmp_dir.native())); return; } @@ -3024,7 +3028,7 @@ test_fs_copyfile_basic() } ::close(src_fd); - std::filesystem::remove_all(tmp_dir); + std::filesystem::remove_all(std::filesystem::path(tmp_dir.native())); } void @@ -3032,9 +3036,9 @@ test_fs_copyfile_source_changes_cleanup_tmpfiles() { int rv; int src_fd; - fs::path src_path; - fs::path dst_path; - fs::path tmp_dir; + fs::relpath src_path; + fs::relpath dst_path; + fs::relpath tmp_dir; std::atomic stop_mutator{false}; std::atomic mutator_updates{0}; char tmp_template[] = "/tmp/mergerfs-test-copyfile-race-XXXXXX"; @@ -3053,7 +3057,7 @@ test_fs_copyfile_source_changes_cleanup_tmpfiles() TEST_CHECK(src_fd >= 0); if(src_fd < 0) { - std::filesystem::remove_all(tmp_dir); + std::filesystem::remove_all(std::filesystem::path(tmp_dir.native())); return; } @@ -3085,9 +3089,9 @@ test_fs_copyfile_source_changes_cleanup_tmpfiles() bool found_tmp_file = false; std::string tmp_prefix; tmp_prefix = "."; - tmp_prefix += dst_path.filename().string(); + tmp_prefix += std::string(dst_path.filename()); tmp_prefix += "_"; - for(const auto &entry : std::filesystem::directory_iterator(tmp_dir)) + for(const auto &entry : std::filesystem::directory_iterator(std::filesystem::path(tmp_dir.native()))) { const std::string name = entry.path().filename().string(); if(name.rfind(tmp_prefix,0) == 0) @@ -3099,7 +3103,189 @@ test_fs_copyfile_source_changes_cleanup_tmpfiles() TEST_CHECK(found_tmp_file == false); ::close(src_fd); - std::filesystem::remove_all(tmp_dir); + std::filesystem::remove_all(std::filesystem::path(tmp_dir.native())); +} + +// Helper: build a real srcdir/dstdir pair under /tmp, with srcdir +// pre-populated to contain `rel` as a chain of nested directories. +// Returns the tmpdir; cleans up on destruction. +namespace { +struct ClonepathFixture +{ + fs::relpath tmp_dir; + fs::relpath srcdir; + fs::relpath dstdir; + + ClonepathFixture() + { + char tmp_template[] = "/tmp/mergerfs-test-clonepath-XXXXXX"; + if(::mkdtemp(tmp_template) == nullptr) + { + TEST_CHECK(false); + return; + } + tmp_dir = tmp_template; + srcdir = tmp_dir / "src"; + dstdir = tmp_dir / "dst"; + ::mkdir(srcdir.c_str(),0755); + ::mkdir(dstdir.c_str(),0755); + } + + ~ClonepathFixture() + { + if(!tmp_dir.empty()) + std::filesystem::remove_all(std::filesystem::path(tmp_dir.native())); + } + + // Populate srcdir/ as a directory chain. + bool make_src_chain(std::string_view rel) + { + fs::relpath p = srcdir; + std::size_t pos = 0; + while(pos < rel.size()) + { + std::size_t next = rel.find('/',pos); + if(next == std::string_view::npos) + next = rel.size(); + std::string_view component = rel.substr(pos,next - pos); + pos = next + 1; + if(component.empty()) + continue; + p /= component; + if(::mkdir(p.c_str(),0755) < 0 && errno != EEXIST) + return false; + } + return true; + } +}; +} + +void +test_fs_clonepath_empty_relpath() +{ + // Empty relpath is a no-op, returns 0. + ClonepathFixture fix; + if(fix.tmp_dir.empty()) return; + + int rv = fs::clonepath(fix.srcdir,fix.dstdir,fs::relpath()); + TEST_CHECK(rv == 0); +} + +void +test_fs_clonepath_single_component() +{ + ClonepathFixture fix; + if(fix.tmp_dir.empty()) return; + + TEST_CHECK(fix.make_src_chain("a")); + + int rv = fs::clonepath(fix.srcdir,fix.dstdir,fs::relpath("a")); + TEST_CHECK(rv == 0); + + struct stat st; + fs::relpath expect = fix.dstdir / "a"; + TEST_CHECK(::lstat(expect.c_str(),&st) == 0); + TEST_CHECK(S_ISDIR(st.st_mode)); +} + +void +test_fs_clonepath_many_components() +{ + // Stack-budget test: a 100-component relpath. The recursive version + // would have used ~12 KB * 100 = 1.2 MB of stack just for path + // objects; the iterative version uses ~8 KB total. + ClonepathFixture fix; + if(fix.tmp_dir.empty()) return; + + std::string rel; + for(int i = 0; i < 100; ++i) + { + if(!rel.empty()) rel += '/'; + rel += "d"; + rel += std::to_string(i); + } + TEST_CHECK(fix.make_src_chain(rel)); + + int rv = fs::clonepath(fix.srcdir,fix.dstdir,fs::relpath(rel)); + TEST_CHECK(rv == 0); + + struct stat st; + fs::relpath expect = fix.dstdir / rel; + TEST_CHECK(::lstat(expect.c_str(),&st) == 0); + TEST_CHECK(S_ISDIR(st.st_mode)); +} + +void +test_fs_clonepath_intermediate_exists() +{ + // mkdir on an already-existing intermediate dir must not fail and + // must not re-copy metadata; the walk continues to deeper components. + ClonepathFixture fix; + if(fix.tmp_dir.empty()) return; + + TEST_CHECK(fix.make_src_chain("a/b/c")); + + // Pre-create dstdir/a so mkdir hits EEXIST at the first step. + fs::relpath pre = fix.dstdir / "a"; + TEST_CHECK(::mkdir(pre.c_str(),0700) == 0); + + int rv = fs::clonepath(fix.srcdir,fix.dstdir,fs::relpath("a/b/c")); + TEST_CHECK(rv == 0); + + struct stat st; + fs::relpath expect = fix.dstdir / "a/b/c"; + TEST_CHECK(::lstat(expect.c_str(),&st) == 0); + TEST_CHECK(S_ISDIR(st.st_mode)); +} + +void +test_fs_clonepath_non_dir_intermediate() +{ + // If an intermediate src component is a regular file, must return + // -ENOTDIR. + ClonepathFixture fix; + if(fix.tmp_dir.empty()) return; + + TEST_CHECK(fix.make_src_chain("a")); + fs::relpath file = fix.srcdir / "a/b"; + int fd = ::open(file.c_str(),O_CREAT|O_WRONLY,0644); + TEST_CHECK(fd >= 0); + if(fd >= 0) ::close(fd); + + int rv = fs::clonepath(fix.srcdir,fix.dstdir,fs::relpath("a/b")); + TEST_CHECK(rv == -ENOTDIR); +} + +void +test_fs_clonepath_lstat_failure() +{ + // If a src component does not exist, must return -ENOENT. + ClonepathFixture fix; + if(fix.tmp_dir.empty()) return; + + // Don't make any src chain; "missing/dir" doesn't exist. + int rv = fs::clonepath(fix.srcdir,fix.dstdir,fs::relpath("missing/dir")); + TEST_CHECK(rv == -ENOENT); +} + +void +test_fs_clonepath_double_slash_skip() +{ + // Defensive: double-slash in rel form is malformed but should not + // re-stat the same path or fail; empty segments are skipped. + ClonepathFixture fix; + if(fix.tmp_dir.empty()) return; + + TEST_CHECK(fix.make_src_chain("a/b")); + + // "a//b" -- the // produces an empty segment that should be skipped. + int rv = fs::clonepath(fix.srcdir,fix.dstdir,fs::relpath("a//b")); + TEST_CHECK(rv == 0); + + struct stat st; + fs::relpath expect = fix.dstdir / "a/b"; + TEST_CHECK(::lstat(expect.c_str(),&st) == 0); + TEST_CHECK(S_ISDIR(st.st_mode)); } void @@ -3496,8 +3682,8 @@ test_fs_inode_passthrough_returns_raw_ino() { TEST_CHECK(fs::inode::set_algo("passthrough") == 0); - const fs::path branch("/mnt/disk1"); - const fs::path fusepath("some/file"); + const std::string branch("/mnt/disk1"); + const fs::relpath fusepath("some/file"); TEST_CHECK(fs::inode::calc(branch, fusepath, S_IFREG, 42) == 42); TEST_CHECK(fs::inode::calc(branch, fusepath, S_IFDIR, 999) == 999); @@ -3507,8 +3693,8 @@ test_fs_inode_passthrough_returns_raw_ino() void test_fs_inode_different_algos_produce_distinct_inodes() { - const fs::path branch("/mnt/disk1"); - const fs::path fusepath("some/file"); + const std::string branch("/mnt/disk1"); + const fs::relpath fusepath("some/file"); const std::vector algos = { "path-hash", "devino-hash", @@ -3526,6 +3712,758 @@ test_fs_inode_different_algos_produce_distinct_inodes() TEST_CHECK(inodes[i] != inodes[0]); } +void +test_fs_relpath_default_construct() +{ + fs::relpath p; + TEST_CHECK(p.size() == 0); + TEST_CHECK(p.empty()); + TEST_CHECK(p.c_str()[0] == '\0'); + TEST_CHECK(p.capacity() == fs::relpath::BUF_SIZE - fs::relpath::REL_OFFSET - 1); + TEST_CHECK(p.native() == std::string_view{}); +} + +void +test_fs_relpath_construct_from_strings() +{ + fs::relpath a("/mnt/disk1"); + TEST_CHECK(a.size() == 10); + TEST_CHECK(a.native() == "/mnt/disk1"); + TEST_CHECK(std::strcmp(a.c_str(),"/mnt/disk1") == 0); + + std::string s = "/foo/bar"; + fs::relpath b(s); + TEST_CHECK(b == "/foo/bar"); + + std::string_view sv = "baz"; + fs::relpath c(sv); + TEST_CHECK(c == "baz"); + + fs::relpath empty1((const char*)nullptr); + TEST_CHECK(empty1.empty()); + + fs::relpath empty2(""); + TEST_CHECK(empty2.empty()); +} + +void +test_fs_relpath_concat_canonical() +{ + // Canonical contract: base has no trailing '/', rel has no leading '/'. + // concat inserts exactly one '/' between non-empty base and rel. + TEST_CHECK(fs::relpath::concat("/mnt/disk1","foo/bar") == "/mnt/disk1/foo/bar"); + TEST_CHECK(fs::relpath::concat("a","b") == "a/b"); + + // Empty handling: empty side passes the other through, no separator. + TEST_CHECK(fs::relpath::concat("","foo") == "foo"); + TEST_CHECK(fs::relpath::concat("/mnt/disk1","") == "/mnt/disk1"); + TEST_CHECK(fs::relpath::concat("","").empty()); +} + +void +test_fs_relpath_assign_concat_reuses_capacity() +{ + fs::relpath p; + const std::size_t cap = p.capacity(); + + p.assign_concat("/mnt/disk1","foo/bar"); + TEST_CHECK(p == "/mnt/disk1/foo/bar"); + TEST_CHECK(p.capacity() == cap); // no growth + + p.assign_concat("/a","b"); + TEST_CHECK(p == "/a/b"); + TEST_CHECK(p.capacity() == cap); + + p.assign_concat("/mnt/disk1","much/longer/sub/path"); + TEST_CHECK(p == "/mnt/disk1/much/longer/sub/path"); + TEST_CHECK(p.capacity() == cap); +} + +void +test_fs_relpath_operator_slash() +{ + fs::relpath branch("/mnt/disk1"); + fs::relpath full = branch / "foo/bar"; + TEST_CHECK(full == "/mnt/disk1/foo/bar"); + + fs::relpath p2("/a"); + p2 /= "b"; + TEST_CHECK(p2 == "/a/b"); + + // Free-function form via two string_views. + fs::relpath mixed = fs::operator/(std::string_view("/x"),std::string_view("y")); + TEST_CHECK(mixed == "/x/y"); +} + +void +test_fs_relpath_filename() +{ + TEST_CHECK(fs::relpath("/foo/bar").filename() == "bar"); + TEST_CHECK(fs::relpath("/foo/").filename() == std::string_view{}); + TEST_CHECK(fs::relpath("/").filename() == std::string_view{}); + TEST_CHECK(fs::relpath("foo").filename() == "foo"); + TEST_CHECK(fs::relpath("").filename() == std::string_view{}); + TEST_CHECK(fs::relpath("foo.txt").filename() == "foo.txt"); +} + +void +test_fs_relpath_parent_path() +{ + TEST_CHECK(fs::relpath("/foo/bar").parent_path() == "/foo"); + TEST_CHECK(fs::relpath("/foo").parent_path() == "/"); + TEST_CHECK(fs::relpath("/").parent_path() == std::string_view{}); + TEST_CHECK(fs::relpath("foo").parent_path() == std::string_view{}); + TEST_CHECK(fs::relpath("").parent_path() == std::string_view{}); + TEST_CHECK(fs::relpath("/foo/bar/").parent_path() == "/foo/bar"); + TEST_CHECK(fs::relpath("a/b/c").parent_path() == "a/b"); +} + +void +test_fs_relpath_extension_and_stem() +{ + fs::relpath a("/foo/bar.txt"); + TEST_CHECK(a.extension() == ".txt"); + TEST_CHECK(a.stem() == "bar"); + + fs::relpath b("/foo/.hidden"); + TEST_CHECK(b.extension() == std::string_view{}); + TEST_CHECK(b.stem() == ".hidden"); + + fs::relpath c("/foo/archive.tar.gz"); + TEST_CHECK(c.extension() == ".gz"); + TEST_CHECK(c.stem() == "archive.tar"); + + fs::relpath d("/foo/noext"); + TEST_CHECK(d.extension() == std::string_view{}); + TEST_CHECK(d.stem() == "noext"); + + fs::relpath dot("/foo/."); + TEST_CHECK(dot.extension() == std::string_view{}); +} + +void +test_fs_relpath_remove_filename() +{ + fs::relpath p("/foo/bar"); + p.remove_filename(); + TEST_CHECK(p == "/foo/"); + + fs::relpath q("/foo/"); + q.remove_filename(); + TEST_CHECK(q == "/foo/"); + + fs::relpath r("foo"); + r.remove_filename(); + TEST_CHECK(r == ""); + + fs::relpath s("/"); + s.remove_filename(); + TEST_CHECK(s == "/"); +} + +void +test_fs_relpath_lexically_relative() +{ + // Same root: simple suffix. + fs::relpath a("/mnt/disk1/data/file.txt"); + TEST_CHECK(a.lexically_relative("/mnt/disk1") == "data/file.txt"); + + // Real symlink-relativization use case from fuse_link.cpp / fuse_rename.cpp: + // target relative to the directory holding the link. + fs::relpath target("/mnt/disk1/data/file.txt"); + TEST_CHECK(target.lexically_relative("/mnt/disk1/links") + == "../data/file.txt"); + + // Identical paths give ".". + TEST_CHECK(fs::relpath("/a/b").lexically_relative("/a/b") == "."); + + // Multiple ".." steps. + TEST_CHECK(fs::relpath("/x").lexically_relative("/a/b/c") == "../../../x"); + + // ".." in base disqualifies the result -- empty so the caller can + // distinguish from "" / "." (a successful relativization never + // produces empty; it returns "." for equal paths). + fs::relpath dotdot_in_base = fs::relpath("/a").lexically_relative("/a/.."); + TEST_CHECK(dotdot_in_base.empty()); + + // Mismatched absoluteness also yields empty. + fs::relpath shape_mismatch = fs::relpath("/a").lexically_relative("a"); + TEST_CHECK(shape_mismatch.empty()); +} + +void +test_fs_relpath_copy_and_move() +{ + fs::relpath a("/mnt/disk1/foo"); + fs::relpath b = a; + TEST_CHECK(b == a); + TEST_CHECK(b == "/mnt/disk1/foo"); + + fs::relpath c; + c = a; + TEST_CHECK(c == a); + + fs::relpath d(std::move(b)); + TEST_CHECK(d == "/mnt/disk1/foo"); + + fs::relpath e; + e = std::move(d); + TEST_CHECK(e == "/mnt/disk1/foo"); + + // Self-assignment is a no-op. + e = e; + TEST_CHECK(e == "/mnt/disk1/foo"); + e = std::move(e); + TEST_CHECK(e == "/mnt/disk1/foo"); +} + +void +test_fs_relpath_equality() +{ + fs::relpath a("/a/b"); + fs::relpath b("/a/b"); + fs::relpath c("/a/c"); + + TEST_CHECK(a == b); + TEST_CHECK(!(a == c)); + TEST_CHECK(a != c); + TEST_CHECK(a == std::string_view("/a/b")); + TEST_CHECK(a != std::string_view("/x")); +} + +void +test_fs_relpath_implicit_conversions() +{ + // The two implicit conversions are the load-bearing pieces of the + // drop-in story. Confirm they pick up correctly in overload resolution. + fs::relpath p("/etc/hostname"); + + // const char* selection: pass to a function taking const char*. + auto strlen_via_cstr = [](const char *s) { return std::strlen(s); }; + TEST_CHECK(strlen_via_cstr(p) == p.size()); + + // string_view selection. + auto svsize = [](std::string_view sv) { return sv.size(); }; + TEST_CHECK(svsize(p) == p.size()); +} + +void +test_fs_relpath_resize_and_append() +{ + // ReaddirCalc-style: pin a prefix, vary the suffix. + fs::relpath p("/mnt/disk1/"); + const std::size_t prefix = p.size(); + + p.append("foo",3); + TEST_CHECK(p == "/mnt/disk1/foo"); + + p.resize(prefix); + TEST_CHECK(p == "/mnt/disk1/"); + + p.append(std::string_view("longer_name")); + TEST_CHECK(p == "/mnt/disk1/longer_name"); + + p.clear(); + TEST_CHECK(p.empty()); + TEST_CHECK(p.c_str()[0] == '\0'); +} + +void +test_fs_relpath_construct_stores_verbatim() +{ + // _init stores input verbatim; no leading-slash strip. This lets + // fs::relpath also hold a fully-built absolute fullpath without + // losing the leading '/'. + fs::relpath abs("/mnt/disk1"); + TEST_CHECK(abs == "/mnt/disk1"); + TEST_CHECK(abs.size() == 10); + + fs::relpath rel("foo/bar"); + TEST_CHECK(rel == "foo/bar"); + + // Empty starts empty. + fs::relpath empty; + TEST_CHECK(empty.empty()); + + // assign() replaces verbatim too. + fs::relpath p("foo"); + p.assign("/etc"); + TEST_CHECK(p == "/etc"); + p.assign("bar"); + TEST_CHECK(p == "bar"); +} + +void +test_fs_relpath_set_prefix() +{ + // Hot-path use case: a fusepath stored as canonical rel (no leading + // slash), branch_path written into its prefix area in place. + // Canonical form throughout (no trailing slash on prefix, no leading + // slash on rel). + fs::relpath p("foo/bar"); + TEST_CHECK(p == "foo/bar"); + + p.set_prefix("/mnt/disk1"); + TEST_CHECK(p == "/mnt/disk1/foo/bar"); + + // Replace prefix in place. + p.set_prefix("/mnt/disk2"); + TEST_CHECK(p == "/mnt/disk2/foo/bar"); + + // Clear prefix returns to the rel. + p.clear_prefix(); + TEST_CHECK(p == "foo/bar"); + + // Empty prefix is equivalent to clear_prefix. + p.set_prefix("/etc"); + TEST_CHECK(p == "/etc/foo/bar"); + p.set_prefix(""); + TEST_CHECK(p == "foo/bar"); + + // Empty rel: prefix becomes the whole path with no separator. + fs::relpath e; + e.set_prefix("/mnt/disk1"); + TEST_CHECK(e == "/mnt/disk1"); +} + +void +test_fs_relpath_concat() +{ + // concat / assign_concat splice base + '/' + rel, no stripping. + // The result is itself an fs::relpath that can be re-prefixed. + fs::relpath fullpath = fs::relpath::concat("/mnt/disk1","foo/bar"); + TEST_CHECK(fullpath == "/mnt/disk1/foo/bar"); + + // Re-prefix the result. set_prefix replaces the full prefix area + // (which currently contains "/mnt/disk1/"). + fullpath.set_prefix("/mnt/disk2"); + TEST_CHECK(fullpath == "/mnt/disk2/foo/bar"); +} + +void +test_fs_relpath_prepend() +{ + // libfuse-style backward build: walk leaf->root, prepending each + // ancestor name as "/name". prepend() writes the slash automatically. + fs::relpath p; + TEST_CHECK(p.empty()); + + p.prepend("leaf.txt"); + TEST_CHECK(p == "/leaf.txt"); + TEST_CHECK(p.size() == 9); + + p.prepend("intermediate"); + TEST_CHECK(p == "/intermediate/leaf.txt"); + + p.prepend("root"); + TEST_CHECK(p == "/root/intermediate/leaf.txt"); + + // Single-character names work too. + fs::relpath q; + q.prepend("c"); + q.prepend("b"); + q.prepend("a"); + TEST_CHECK(q == "/a/b/c"); + + // Empty-string prepend just adds a slash. + fs::relpath r; + r.prepend(""); + TEST_CHECK(r == "/"); + TEST_CHECK(r.size() == 1); +} + +void +test_fs_relpath_consolidate_to_rel() +{ + // After backward-build via prepend(), data lives at some _start + // before REL_OFFSET. consolidate_to_rel() shifts it to the REL anchor + // so set_prefix() can be applied by callers downstream. + fs::relpath p; + p.prepend("leaf.txt"); + p.prepend("dir"); + TEST_CHECK(p == "/dir/leaf.txt"); + + // Default: keep the leading '/'. + p.consolidate_to_rel(); + TEST_CHECK(p == "/dir/leaf.txt"); + + // strip_leading_slash=true: drop the '/' that prepend left at the + // front. + fs::relpath q; + q.prepend("file"); + q.prepend("dir"); + TEST_CHECK(q == "/dir/file"); + + q.consolidate_to_rel(/*strip_leading_slash=*/true); + TEST_CHECK(q == "dir/file"); + + // Now in canonical mergerfs form: rel without leading '/'. set_prefix + // is applied in place by the caller's per-branch loop. + q.set_prefix("/branch"); + TEST_CHECK(q == "/branch/dir/file"); + + // Idempotent: consolidating an already-canonical-rel path is a no-op. + fs::relpath r("foo/bar"); + r.consolidate_to_rel(); + TEST_CHECK(r == "foo/bar"); + + // Empty path: strip_leading_slash should be safe (no leading '/' to + // strip, no underflow). + fs::relpath s; + s.consolidate_to_rel(/*strip_leading_slash=*/true); + TEST_CHECK(s.empty()); + + const std::size_t rel_capacity = fs::relpath::BUF_SIZE - fs::relpath::REL_OFFSET - 1; + + // Boundary: enough components to land exactly at REL_OFFSET bytes + // (each prepend adds component_size + 1 for the slash). After + // strip_leading_slash this is exactly rel_capacity. Math is + // parameterized off REL_OFFSET so this test scales with the buffer + // size; with the symmetric 8 KB layout this is 4 components of 1023 + // bytes each = 4096 bytes total, consolidating to 4095 = rel_capacity. + constexpr std::size_t component_count = 4; + static_assert((fs::relpath::REL_OFFSET % component_count) == 0, + "boundary test assumes REL_OFFSET divides evenly"); + const std::size_t component_size = (fs::relpath::REL_OFFSET / component_count) - 1; + const std::string max_component(component_size,'m'); + fs::relpath max_rel; + for(std::size_t i = 0; i < component_count; ++i) + max_rel.prepend(max_component); + TEST_CHECK(max_rel.size() == rel_capacity + 1); + + max_rel.consolidate_to_rel(/*strip_leading_slash=*/true); + TEST_CHECK(max_rel.size() == rel_capacity); + TEST_CHECK(max_rel.prefix().empty()); + TEST_CHECK(max_rel.rel().size() == rel_capacity); + + // Note: with the symmetric BUF_SIZE = 2 * REL_OFFSET layout, the + // "prepend fits in the prefix area but consolidate_to_rel overflows + // the rel area" window collapses to a single byte and is unreachable + // with strip_leading_slash=true (the strip absorbs the off-by-one). +} + +void +test_fs_relpath_clear_resets_state() +{ + // clear() must return to the canonical empty state regardless of + // prior contents, so that subsequent prepend() works. This is the + // libfuse try_get_path contract. + fs::relpath p("/mnt/disk1/foo"); + TEST_CHECK(!p.empty()); + + p.clear(); + TEST_CHECK(p.empty()); + + // prepend() must work after clear(). + p.prepend("file"); + p.prepend("dir"); + TEST_CHECK(p == "/dir/file"); + + // Round-trip through consolidate_to_rel(strip=true) → set_prefix. + p.consolidate_to_rel(/*strip_leading_slash=*/true); + TEST_CHECK(p == "dir/file"); + p.set_prefix("/branch"); + TEST_CHECK(p == "/branch/dir/file"); +} + +void +test_fs_relpath_self_aliasing() +{ + // assign_concat / operator/= must handle the case where one of the + // inputs aliases *this's own buffer (e.g. `p /= "x"`). Naive ordering + // writes the rel slot first, which corrupts the base if base aliases. + fs::relpath a("a"); + a /= "b"; + TEST_CHECK(a == "a/b"); + + fs::relpath foo("foo/bar"); + foo /= "baz"; + TEST_CHECK(foo == "foo/bar/baz"); + + // Leading-slash self-aliasing too. + fs::relpath abs("/mnt/disk1"); + abs /= "data"; + TEST_CHECK(abs == "/mnt/disk1/data"); + + // Repeated /= + fs::relpath p("a"); + p /= "b"; + p /= "c"; + p /= "d"; + TEST_CHECK(p == "a/b/c/d"); + + // assign_concat with native() as the base + fs::relpath q("rel"); + q.assign_concat(q.native(),"more"); + TEST_CHECK(q == "rel/more"); +} + +void +test_fs_relpath_prefix_and_rel_accessors() +{ + // Path with no prefix set: prefix() empty, rel() is the whole thing. + fs::relpath p("foo/bar"); + TEST_CHECK(p.prefix().empty()); + TEST_CHECK(p.rel() == "foo/bar"); + + // After set_prefix the views reflect each region. + p.set_prefix("/mnt/disk1"); + TEST_CHECK(p.prefix() == "/mnt/disk1/"); + TEST_CHECK(p.rel() == "foo/bar"); + TEST_CHECK(p == "/mnt/disk1/foo/bar"); // native is the concatenation + + // clear_prefix resets prefix() to empty. + p.clear_prefix(); + TEST_CHECK(p.prefix().empty()); + TEST_CHECK(p.rel() == "foo/bar"); + + // For a path constructed verbatim with a leading slash, the data + // sits at REL_OFFSET as the rel; prefix() is empty. + fs::relpath abs("/etc/hostname"); + TEST_CHECK(abs.prefix().empty()); + TEST_CHECK(abs.rel() == "/etc/hostname"); + TEST_CHECK(abs.rel() == abs.native()); +} + +void +test_fs_relpath_resize_zero_fills() +{ + // Growing via resize must zero-fill the new bytes so a subsequent + // read of [old_size, new_size) doesn't return garbage from a + // previous use of the buffer. + fs::relpath p("foo"); + p.append("dirty",5); // _buf now contains "foodirty" + TEST_CHECK(p == "foodirty"); + + p.resize(3); // back to "foo" + TEST_CHECK(p == "foo"); + TEST_CHECK(p.size() == 3); + + // Grow back to 8 bytes. Bytes 3..7 must be '\0', not the leftover + // "dirty" content. + p.resize(8); + TEST_CHECK(p.size() == 8); + // The whole path read as a string_view should be "foo\0\0\0\0\0". + TEST_CHECK(p.native().size() == 8); + for(std::size_t i = 3; i < 8; i++) + TEST_CHECK(p.native()[i] == '\0'); + // c_str() stops at the first '\0' so it reads "foo". + TEST_CHECK(std::strcmp(p.c_str(),"foo") == 0); + + // Shrinking truncates and writes a NUL at the new end. + p.resize(2); + TEST_CHECK(p == "fo"); + TEST_CHECK(p.c_str()[2] == '\0'); +} + +void +test_fs_relpath_rel_size_invariant() +{ + // _rel_size is internal but visible through rel(). Verify it stays + // consistent across the full set of mutations: assign_concat, + // set_prefix (must NOT touch rel size), clear_prefix, append, + // resize, remove_filename, clear, copy/move, swap. + fs::relpath p; + p.assign_concat("/mnt/disk1","foo/bar"); + TEST_CHECK(p.rel() == "foo/bar"); // rel cached as 7 + TEST_CHECK(p.rel().size() == 7); + + // set_prefix only changes the prefix area; rel must be unchanged. + p.set_prefix("/mnt/disk2"); + TEST_CHECK(p.rel() == "foo/bar"); + TEST_CHECK(p.rel().size() == 7); + + // Replace with a different-length prefix; rel still unchanged. + p.set_prefix("/x"); + TEST_CHECK(p == "/x/foo/bar"); + TEST_CHECK(p.rel() == "foo/bar"); + + // clear_prefix drops prefix; rel unchanged. + p.clear_prefix(); + TEST_CHECK(p == "foo/bar"); + TEST_CHECK(p.rel() == "foo/bar"); + TEST_CHECK(p.size() == 7); + + // append extends the rel. + p.append("/baz",4); + TEST_CHECK(p == "foo/bar/baz"); + TEST_CHECK(p.rel() == "foo/bar/baz"); + + // resize shrinks; rel shrinks with it. + p.resize(3); + TEST_CHECK(p == "foo"); + TEST_CHECK(p.rel() == "foo"); + + // remove_filename truncates to the directory form. + p.assign_concat("/mnt","a/b"); + TEST_CHECK(p == "/mnt/a/b"); + TEST_CHECK(p.rel() == "a/b"); + p.remove_filename(); + TEST_CHECK(p == "/mnt/a/"); + TEST_CHECK(p.rel() == "a/"); + + // Construct from a leading-'/' literal: stored verbatim, so the + // entire payload is the rel. + fs::relpath abs("/etc/hostname"); + TEST_CHECK(abs.rel() == "/etc/hostname"); + + // clear resets size; rel is empty. + abs.clear(); + TEST_CHECK(abs.empty()); + TEST_CHECK(abs.rel().empty()); + + // Copy/move preserve _rel_size (visible via rel()). + fs::relpath src; + src.assign_concat("/p","r"); + fs::relpath cpy = src; + TEST_CHECK(cpy.rel() == "r"); + fs::relpath mv = std::move(src); + TEST_CHECK(mv.rel() == "r"); + + // Swap exchanges _rel_size too. + fs::relpath a; + a.assign_concat("/x","aaa"); + fs::relpath b; + b.assign_concat("/y","bbbb"); + a.swap(b); + TEST_CHECK(a.rel() == "bbbb"); + TEST_CHECK(b.rel() == "aaa"); +} + +void +test_fs_relpath_swap() +{ + // Swap two paths with content in both the prefix area and rel. + fs::relpath a = fs::relpath::concat("/x","mnt/disk1"); + fs::relpath b("foo/bar"); + TEST_CHECK(a == "/x/mnt/disk1"); + TEST_CHECK(b == "foo/bar"); + + a.swap(b); + TEST_CHECK(a == "foo/bar"); + TEST_CHECK(b == "/x/mnt/disk1"); + + // Swap between leading-'/' and rel-form paths. + fs::relpath abs("/etc/hostname"); + fs::relpath rel("relpath"); + abs.swap(rel); + TEST_CHECK(abs == "relpath"); + TEST_CHECK(rel == "/etc/hostname"); + + // Self-swap is a no-op. + fs::relpath c("self"); + c.swap(c); + TEST_CHECK(c == "self"); +} + +void +test_fs_relpath_set_prefix_oversize() +{ + // Oversized-prefix fallback: when a prefix won't fit in front of + // the default rel anchor (REL_OFFSET), set_prefix shifts the rel + // rightward so the full BUF_SIZE-1 can be used. Unreachable under + // Linux PATH_MAX but the class supports it for correctness. + fs::relpath p("foo"); + TEST_CHECK(p.size() == 3); + TEST_CHECK(p.rel() == "foo"); + + // 5000-byte prefix exceeds REL_OFFSET (4096) but the total path + // (5000 + 1 + 3) fits in BUF_SIZE-1. + const std::size_t big_prefix_len = 5000; + std::string big_prefix(big_prefix_len,'A'); + p.set_prefix(big_prefix); + TEST_CHECK(p.size() == big_prefix_len + 1 + 3); + TEST_CHECK(p.native().substr(0,big_prefix_len) == big_prefix); + TEST_CHECK(p.native().substr(big_prefix_len) == "/foo"); + TEST_CHECK(p.rel() == "foo"); + TEST_CHECK(p.prefix() == big_prefix + "/"); + + // A subsequent fitting prefix still works (rel stays at its grown + // anchor; this is allowed since the prefix area can be larger than + // REL_OFFSET without harm). Verify the prefix slice and that several + // back-to-back fast-path calls in a row leave a clean buffer (the + // per-branch loop pattern). + p.set_prefix("/branch"); + TEST_CHECK(p == "/branch/foo"); + TEST_CHECK(p.rel() == "foo"); + TEST_CHECK(p.prefix() == "/branch/"); + + p.set_prefix("/another"); + TEST_CHECK(p == "/another/foo"); + TEST_CHECK(p.rel() == "foo"); + TEST_CHECK(p.prefix() == "/another/"); + + p.set_prefix("/x"); + TEST_CHECK(p == "/x/foo"); + TEST_CHECK(p.rel() == "foo"); + TEST_CHECK(p.prefix() == "/x/"); + + // Oversized prefix with empty rel: no separator, prefix becomes the + // whole path, can fill up to BUF_SIZE-1. + fs::relpath e; + std::string max_prefix(fs::relpath::BUF_SIZE - 1,'C'); + e.set_prefix(max_prefix); + TEST_CHECK(e.size() == fs::relpath::BUF_SIZE - 1); + TEST_CHECK(e.native() == max_prefix); +} + +void +test_fs_relpath_root_nodeid_dispatch_contract() +{ + // libfuse's try_get_path() walks leaf-to-root. Two cases produce an + // empty fs::relpath after the call: + // 1. nodeid == FUSE_ROOT_ID: the walk has zero components to + // prepend, so an empty path is the legitimate result. The + // dispatcher (fuse_lib_getattr / fuse_lib_setattr / + // fuse_lib_statx_path) issues a path-based op for this case. + // 2. ESTALE was masked above (a nameless node, e.g. an unlinked + // open file): try_get_path's error path resets the buffer to + // empty, so the caller observes empty here too. The + // dispatcher falls back to the fh-based op (fXXX(0)) which + // lets mergerfs find the file via OpenFiles by handle. + // + // The dispatcher discriminator is (nodeid == FUSE_ROOT_ID), not + // empty() alone. Lock the no-allocation, no-leading-slash contract + // of the empty path here so a future change to clear() / prepend() / + // consolidate_to_rel() doesn't silently flip the discriminator. + fs::relpath p; + p.clear(); // libfuse start state + p.consolidate_to_rel(/*strip_leading_slash=*/false); // zero prepends + TEST_CHECK(p.empty()); + TEST_CHECK(p.size() == 0); + TEST_CHECK(p.native() == std::string_view{}); + TEST_CHECK(p.c_str()[0] == '\0'); + + // The path-vs-fh dispatcher uses empty() as part of the test; make + // sure the implicit conversion to const char* yields a non-null, + // empty C string (callers may pass it to fs::* wrappers that take + // const char*). + const char *cs = p; + TEST_CHECK(cs != nullptr); + TEST_CHECK(cs[0] == '\0'); + + // After consolidate_to_rel(strip=true) on a zero-prepend path the + // result is also empty (the strip is a no-op when the first byte + // isn't '/'; _size > 0 guards prevent underflow). + fs::relpath q; + q.clear(); + q.consolidate_to_rel(/*strip_leading_slash=*/true); + TEST_CHECK(q.empty()); + + // A non-root path (one prepend, then consolidate with strip) yields + // canonical-rel form: "name", no leading slash. Combined with the + // empty cases above, the dispatcher sees: + // nodeid == FUSE_ROOT_ID, empty() -> path-based op (root) + // nodeid != FUSE_ROOT_ID, empty() -> fh-based op (nameless) + // nodeid != FUSE_ROOT_ID, !empty() -> path-based op (named) + fs::relpath r; + r.clear(); + r.prepend("file"); + r.consolidate_to_rel(/*strip_leading_slash=*/true); + TEST_CHECK(!r.empty()); + TEST_CHECK(r == "file"); +} + void test_rnd_rand64_basic() { @@ -3754,6 +4692,13 @@ TEST_LIST = {"config_prune_cmd_xattr",test_config_prune_cmd_xattr}, {"fs_copyfile_basic",test_fs_copyfile_basic}, {"fs_copyfile_source_changes_cleanup_tmpfiles",test_fs_copyfile_source_changes_cleanup_tmpfiles}, + {"fs_clonepath_empty_relpath",test_fs_clonepath_empty_relpath}, + {"fs_clonepath_single_component",test_fs_clonepath_single_component}, + {"fs_clonepath_many_components",test_fs_clonepath_many_components}, + {"fs_clonepath_intermediate_exists",test_fs_clonepath_intermediate_exists}, + {"fs_clonepath_non_dir_intermediate",test_fs_clonepath_non_dir_intermediate}, + {"fs_clonepath_lstat_failure",test_fs_clonepath_lstat_failure}, + {"fs_clonepath_double_slash_skip",test_fs_clonepath_double_slash_skip}, {"str_eq_nullptr",test_str_eq_nullptr}, {"str_startswith_char_nullptr",test_str_startswith_char_nullptr}, {"str_from_u64_suffixes",test_str_from_u64_suffixes}, @@ -3777,6 +4722,33 @@ TEST_LIST = {"fs_inode_set_algo_invalid",test_fs_inode_set_algo_invalid}, {"fs_inode_passthrough_returns_raw_ino",test_fs_inode_passthrough_returns_raw_ino}, {"fs_inode_different_algos_produce_distinct_inodes",test_fs_inode_different_algos_produce_distinct_inodes}, + {"fs_path_default_construct",test_fs_relpath_default_construct}, + {"fs_path_construct_from_strings",test_fs_relpath_construct_from_strings}, + {"fs_path_concat_canonical",test_fs_relpath_concat_canonical}, + {"fs_path_assign_concat_reuses_capacity",test_fs_relpath_assign_concat_reuses_capacity}, + {"fs_path_operator_slash",test_fs_relpath_operator_slash}, + {"fs_path_filename",test_fs_relpath_filename}, + {"fs_path_parent_path",test_fs_relpath_parent_path}, + {"fs_path_extension_and_stem",test_fs_relpath_extension_and_stem}, + {"fs_path_remove_filename",test_fs_relpath_remove_filename}, + {"fs_path_lexically_relative",test_fs_relpath_lexically_relative}, + {"fs_path_copy_and_move",test_fs_relpath_copy_and_move}, + {"fs_relpath_equality",test_fs_relpath_equality}, + {"fs_relpath_implicit_conversions",test_fs_relpath_implicit_conversions}, + {"fs_relpath_resize_and_append",test_fs_relpath_resize_and_append}, + {"fs_relpath_construct_stores_verbatim",test_fs_relpath_construct_stores_verbatim}, + {"fs_relpath_set_prefix",test_fs_relpath_set_prefix}, + {"fs_relpath_concat",test_fs_relpath_concat}, + {"fs_relpath_prepend",test_fs_relpath_prepend}, + {"fs_relpath_consolidate_to_rel",test_fs_relpath_consolidate_to_rel}, + {"fs_relpath_clear_resets_state",test_fs_relpath_clear_resets_state}, + {"fs_relpath_self_aliasing",test_fs_relpath_self_aliasing}, + {"fs_relpath_prefix_and_rel_accessors",test_fs_relpath_prefix_and_rel_accessors}, + {"fs_relpath_resize_zero_fills",test_fs_relpath_resize_zero_fills}, + {"fs_relpath_rel_size_invariant",test_fs_relpath_rel_size_invariant}, + {"fs_relpath_swap",test_fs_relpath_swap}, + {"fs_relpath_set_prefix_oversize",test_fs_relpath_set_prefix_oversize}, + {"fs_relpath_root_nodeid_dispatch_contract",test_fs_relpath_root_nodeid_dispatch_contract}, {"rnd_rand64_basic",test_rnd_rand64_basic}, {"rnd_rand64_max",test_rnd_rand64_max}, {"rnd_rand64_range",test_rnd_rand64_range}, diff --git a/vendored/libfuse/include/fs_path.hpp b/vendored/libfuse/include/fs_path.hpp new file mode 100644 index 00000000..eccc37f8 --- /dev/null +++ b/vendored/libfuse/include/fs_path.hpp @@ -0,0 +1,378 @@ +/* + ISC License + + Copyright (c) 2026, Antonio SJ Musumeci + + 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 +#include +#include +#include +#include +#include + + +namespace fs +{ + // relpath: a relative path (mergerfs's "fusepath") backed by a fixed + // 8192-byte inline buffer, NUL-terminated, no heap. + // + // Layout: rel data is anchored at _rel_start, which defaults to + // REL_OFFSET (4096) on construction, clear(), assign(), and + // consolidate_to_rel(). The bytes before _rel_start form the + // "prefix area" -- a long base/prefix can be written there in place + // via set_prefix() without touching the rel. The remaining bytes + // after the rel are available for appending (filenames in + // readdir-style loops, etc.). + // + // Buffer is sized at 2 * PATH_MAX. The fast path -- a prefix that + // fits in REL_OFFSET bytes -- writes only the prefix area; rel + // stays anchored at REL_OFFSET. If a caller hands in a prefix + // larger than REL_OFFSET (contract violation or future-larger + // PATH_MAX), set_prefix shifts the rel rightward to make room and + // updates _rel_start, so the class's effective capacity is the + // full BUF_SIZE - 1 rather than artificially capping at REL_OFFSET. + // set_prefix's cost is O(prefix size + shift size), independent of + // BUF_SIZE -- only the actually-used bytes are touched -- so the + // buffer size is a stack-footprint question, not a hot-path cost. + // FUSE worker threads use the pthread default stack (~8 MB on + // Linux); a 4-relpath frame in fuse_link/fuse_rename is 32 KB, + // ~0.4% of the stack budget. + // + // Mergerfs has exactly one path-manipulation type: a relative + // fusepath. Branch paths and other absolute paths are stored as + // std::string -- they are spliced into the prefix area of an + // fs::relpath at syscall time but are never themselves mutated as + // path objects. + // + // Canonical mergerfs form -- prefix has no trailing '/', rel has + // no leading '/'. set_prefix() inserts exactly one '/' between a + // non-empty prefix and a non-empty rel. No sanitization; violating + // the contract yields double slashes. Mergerfs satisfies the + // contract by construction: + // - libfuse's try_get_path() walks leaf-to-root via prepend(), + // then calls consolidate_to_rel(strip=true) which drops the + // leading '/' that prepend produces. + // - branch paths are realpath-canonicalized at config load so + // they have no trailing '/'. + class relpath final + { + public: + static constexpr std::size_t BUF_SIZE = 8192; + static constexpr std::size_t REL_OFFSET = 4096; + + public: + relpath() noexcept; + relpath(const char *s); + relpath(std::string_view s); + relpath(const std::string &s); + relpath(const relpath&); + relpath(relpath&&) noexcept; + ~relpath() = default; + + public: + relpath& operator=(const relpath&); + relpath& operator=(relpath&&) noexcept; + relpath& operator=(const char *s); + relpath& operator=(std::string_view s); + relpath& operator=(const std::string &s); + + public: + // Replace value entirely. The input is stored verbatim -- no + // leading-'/' stripping. The caller is responsible for canonical + // rel form (no leading '/') if the result is going to be re- + // prefixed via set_prefix(); mergerfs's pipeline guarantees this + // via libfuse's consolidate_to_rel(strip=true) step. Storing + // verbatim means an fs::relpath can also hold a fully-built + // absolute fullpath (`branch.path / fusepath`) without losing the + // leading '/'. + relpath& assign(std::string_view s); + + // The hot-path primitive: replace the prefix in place. The rel + // portion is undisturbed; only [_start, _rel_start) is touched + // when the prefix fits in front of the current rel anchor. + // + // Always inserts exactly one '/' between non-empty prefix and rel. + // No sanitization: prefix must have no trailing '/' (canonical + // mergerfs branch path form). Empty prefix drops the prefix and + // restores bare-rel state. + // + // Each call is a fresh prefix assignment. If the prefix is + // larger than the current prefix area, the rel data is shifted + // rightward to enlarge the prefix area; this fallback engages + // only when p_size + sep > _rel_start, which is unreachable + // under Linux PATH_MAX (the fast path always fires). + // + // Capacity contract: prefix + sep + rel must fit in BUF_SIZE - 1. + // BUF_SIZE = 2 * PATH_MAX, so any prefix and rel that came from + // the kernel necessarily fit. Exceeding the capacity is a + // programming error; the syscall layer (Linux PATH_MAX) returns + // ENAMETOOLONG for any oversized path long before the path can + // be assembled here. + void set_prefix(std::string_view prefix); + + // Restore to just the rel portion (start = _rel_start, no prefix). + void clear_prefix() noexcept + { + _start = _rel_start; + _size = _rel_size; + _buf[_start + _size] = '\0'; + } + + // Prepend "/component" to the path, growing leftward into the + // prefix area. Used for libfuse's path-building pattern: walk + // leaf-to-root, prepending each ancestor's name. The component + // must be a bare name (no leading '/'); the slash separator is + // inserted automatically. + // + // Capacity contract: the cumulative prepend chain must fit in + // REL_OFFSET bytes (PATH_MAX); the kernel's own PATH_MAX guarantees + // this for any path that could be delivered to a FUSE op. + void prepend(std::string_view component) noexcept + { + const std::size_t total = component.size() + 1; // '/' + component + // Contract guard: the cumulative prepend chain must not underflow + // _start (a uint16_t). Silent underflow would land somewhere in + // the upper half of _buf and corrupt unrelated data. + assert(_start >= total); + _start -= total; + _buf[_start] = '/'; + std::memcpy(_buf + _start + 1,component.data(),component.size()); + _size += total; + // _rel_size unchanged: the prefix area grew; the rel anchor stays. + } + + // Move current data to the REL anchor, optionally stripping a + // single leading '/' (the one libfuse's prepend chain produces). + // After this call: + // _start == REL_OFFSET + // _rel_start == REL_OFFSET + // _rel_size == _size + // so set_prefix from a caller works as expected. This is the + // bridge between the prepend-style backward build (data in the + // prefix area) and the set_prefix-style forward use (rel anchored + // at REL_OFFSET). + // + // When strip_leading_slash is true and the first byte is '/', it + // is dropped. The libfuse path-build chain produces this slash + // via prepend; the canonical mergerfs rel form does not include + // it. If the first byte is not '/' (e.g. a zero-prepend root + // case), nothing is stripped. + void consolidate_to_rel(const bool strip_leading_slash = false) noexcept + { + if(strip_leading_slash && _size > 0 && _buf[_start] == '/') + { + _start += 1; + _size -= 1; + } + if((_start != REL_OFFSET) && (_size > 0)) + std::memmove(_buf + REL_OFFSET,_buf + _start,_size); + _start = REL_OFFSET; + _rel_start = REL_OFFSET; + _rel_size = _size; + _buf[REL_OFFSET + _size] = '\0'; + } + + // View into the prefix area (i.e. [_start, REL_OFFSET)). + // + // NOTE: prefix() includes the trailing '/' that set_prefix + // inserts between prefix and rel. set_prefix("/foo") produces a + // path whose prefix() is "/foo/" -- the input doesn't have a + // trailing slash but the view does. This is asymmetric but + // matches the in-buffer representation and avoids a copy. + // + // Lifetime caveat applies: see the LIFETIME block on the + // structural queries below. + std::string_view prefix() const noexcept; + + // View into the rel portion: [REL_OFFSET, REL_OFFSET + _rel_size). + std::string_view rel() const noexcept; + + public: + // Inserts a single '/' between non-empty base and rel (no + // sanitization -- same contract as set_prefix: base no trailing + // '/', rel no leading '/'). The result is a relpath, so it can + // be re-prefixed in place by a downstream set_prefix(). + static relpath concat(std::string_view base, std::string_view rel); + relpath& assign_concat(std::string_view base, std::string_view rel); + + relpath operator/(std::string_view rhs) const; + relpath& operator/=(std::string_view rhs); + + public: + // Structural queries -- return string_view into _buf, zero + // allocation. + // + // LIFETIME: all of native/filename/parent_path/extension/stem/ + // prefix/rel return views into this path's internal buffer. ANY + // non-const operation on *this (set_prefix, clear_prefix, assign, + // assign_concat, append, resize, remove_filename, clear, swap, + // operator=, operator/=) may invalidate previously-returned views + // -- using one after a mutation is undefined behavior. Same rule + // as std::string -> std::string_view, but easier to hit here + // because set_prefix is the natural per-iteration mutation in the + // per-branch loop pattern. + std::string_view native() const noexcept { return {c_str(),_size}; } + std::string_view filename() const noexcept; + std::string_view parent_path() const noexcept; + std::string_view extension() const noexcept; + std::string_view stem() const noexcept; + + // Truncate to the directory form: drops the final component but + // retains the trailing '/'. For example, "foo/bar" -> "foo/", + // "foo/" stays "foo/". Use parent_path() if you want the + // dirname without the trailing slash. + relpath& remove_filename(); + + // Lexical relativization. Both *this and base must be in the + // same form (both with or both without leading '/'). Result is a + // relpath (in canonical form, no leading '/'); on shape mismatch + // or an unresolvable ".." in base, returns an empty relpath. A + // successful relativization never produces an empty result (it + // is "." for equal paths), so callers can use .empty() to detect + // failure. + relpath lexically_relative(std::string_view base) const; + + public: + const char* c_str() const noexcept { return _buf + _start; } + const char* data() const noexcept { return _buf + _start; } + std::size_t size() const noexcept { return _size; } + // Practical capacity for a freshly-assigned bare-rel path: the + // rel area is REL_OFFSET-wide and holds the trailing NUL, leaving + // (BUF_SIZE - REL_OFFSET - 1) bytes (= 4095) for the rel content. + // A path that has both a prefix and a rel can total up to + // BUF_SIZE - 1, but assigning more than capacity() to a bare-rel + // overflows. + std::size_t capacity() const noexcept { return BUF_SIZE - REL_OFFSET - 1; } + bool empty() const noexcept { return _size == 0; } + + void clear() noexcept; + // Capacity is fixed at construction; reserve() exists for + // std::string API compatibility only. Anything exceeding + // capacity() is a programming error. + void reserve(std::size_t /*n*/) noexcept {} + void swap(relpath &o) noexcept; + + // std::string-style mutators. These work from the END of the + // current data. Growing zero-fills the new bytes to match + // std::string semantics. + void resize(std::size_t n); + void append(const char*,std::size_t); + void append(std::string_view); + + std::string string() const { return std::string(c_str(),_size); } + + public: + // Implicit conversions are intentional: they let an fs::relpath + // flow into fs::* syscall wrappers that take const char* / + // std::string_view without an explicit .c_str() / .native() at + // every call site. The lifetime caveat from native() applies to + // both -- the returned pointer / view becomes invalid on any + // non-const op. + // + // FOOTGUN: `std::string s = some_relpath;` silently allocates -- + // it picks `string(const char*)` via the implicit conversion. + // When you actually want a std::string copy use `.string()` + // explicitly so the cost is visible at the call site. + operator const char*() const noexcept { return c_str(); } + operator std::string_view() const noexcept { return native(); } + + bool operator==(const relpath &o) const noexcept + { return native() == o.native(); } + bool operator==(std::string_view s) const noexcept + { return native() == s; } + bool operator==(const char *s) const noexcept + { return s ? native() == std::string_view(s) : _size == 0; } + + bool operator!=(const relpath &o) const noexcept { return !(*this == o); } + bool operator!=(std::string_view s) const noexcept { return !(*this == s); } + bool operator!=(const char *s) const noexcept { return !(*this == s); } + + bool operator<(const relpath &o) const noexcept + { return native() < o.native(); } + bool operator<(std::string_view s) const noexcept + { return native() < s; } + + private: + void _init(std::string_view s); + bool _aliases(std::string_view s) const noexcept; + + private: + // Invariants: + // _buf[_start + _size] == '\0' + // data lives at [_start, _start + _size). + // _start is the offset where the (optionally prefixed) data + // begins; _start <= _rel_start. + // The rel portion is at [_rel_start, _rel_start + _rel_size). + // _size == (_rel_start - _start) + _rel_size. + // _rel_start >= REL_OFFSET; defaults to REL_OFFSET and only + // grows when set_prefix needs more prefix room than + // REL_OFFSET provides (oversized-prefix fallback). + // + // _rel_size is cached so set_prefix / clear_prefix / rel() can + // read it directly instead of recomputing (_start + _size) - + // _rel_start. It is maintained on every mutation (assign / + // assign_concat / consolidate_to_rel set it; set_prefix and + // clear_prefix leave it; resize/append adjust it). + alignas(8) char _buf[BUF_SIZE]; + std::uint16_t _start; + std::uint16_t _size; + std::uint16_t _rel_start; + std::uint16_t _rel_size; + }; + + inline + relpath + operator/(std::string_view a_, + std::string_view b_) + { + return relpath::concat(a_,b_); + } +} + +namespace std +{ + template<> + struct hash + { + std::size_t + operator()(const fs::relpath &p_) const noexcept + { + return std::hash{}(p_.native()); + } + }; +} + +#include +namespace fs +{ + inline + std::ostream& + operator<<(std::ostream &os_, + const relpath &p_) + { + return os_ << p_.native(); + } +} + +// Note: there is no fmt::formatter specialization here. The previous +// fs::path version included one because it was used directly in +// SysLog::warning("... {}", path) calls. After the path/string split, +// every formatted "path" is std::string (branches, mountpoint, etc.) +// or a structural-query string_view -- both fmt formats natively. +// Keeping fmt out of this header avoids pulling fmt/format.h into +// every translation unit that includes it. diff --git a/vendored/libfuse/include/fuse.h b/vendored/libfuse/include/fuse.h index c97d2062..08eb20fe 100644 --- a/vendored/libfuse/include/fuse.h +++ b/vendored/libfuse/include/fuse.h @@ -10,6 +10,7 @@ #define _FUSE_H_ #include "extern_c.h" +#include "fs_path.hpp" #include "fuse_common.h" #include "fuse_kernel.h" #include "fuse_req_ctx.h" @@ -66,45 +67,45 @@ typedef struct fuse_dirents_t fuse_dirents_t; struct fuse_operations { int (*getattr)(const fuse_req_ctx_t *, - const char *, + const fs::relpath &, struct stat *, fuse_timeouts_t *); ssize_t (*readlink)(const fuse_req_ctx_t *, - const char *, + const fs::relpath &, char *, size_t); int (*mknod)(const fuse_req_ctx_t *, - const char *, + const fs::relpath &, mode_t, dev_t); int (*mkdir)(const fuse_req_ctx_t *, - const char *, + const fs::relpath &, mode_t); int (*unlink)(const fuse_req_ctx_t *, - const char *); + const fs::relpath &); int (*rmdir)(const fuse_req_ctx_t *, - const char *); + const fs::relpath &); int (*symlink)(const fuse_req_ctx_t *, const char *, - const char *, + const fs::relpath &, struct stat *, fuse_timeouts_t *); int (*rename)(const fuse_req_ctx_t *, - const char *, - const char *); + const fs::relpath &, + const fs::relpath &); int (*link)(const fuse_req_ctx_t *, - const char *, - const char *, + const fs::relpath &, + const fs::relpath &, struct stat *, fuse_timeouts_t *); int (*chmod)(const fuse_req_ctx_t *, - const char *, + const fs::relpath &, mode_t); int (*fchmod)(const fuse_req_ctx_t *, const uint64_t, const mode_t); int (*chown)(const fuse_req_ctx_t *, - const char *, + const fs::relpath &, uid_t, gid_t); int (*fchown)(const fuse_req_ctx_t *, @@ -112,13 +113,13 @@ struct fuse_operations const uid_t, const gid_t); int (*truncate)(const fuse_req_ctx_t *, - const char *, + const fs::relpath &, off_t); int (*open)(const fuse_req_ctx_t *, - const char *, + const fs::relpath &, fuse_file_info_t *); int (*statfs)(const fuse_req_ctx_t *, - const char *, + const fs::relpath &, struct statvfs *); int (*flush)(const fuse_req_ctx_t *, const fuse_file_info_t *); @@ -128,25 +129,25 @@ struct fuse_operations const uint64_t, int); int (*setxattr)(const fuse_req_ctx_t *, - const char *, + const fs::relpath &, const char *, const char *, size_t, int); int (*getxattr)(const fuse_req_ctx_t *, - const char *, + const fs::relpath &, const char *, char *, size_t); int (*listxattr)(const fuse_req_ctx_t *, - const char *, + const fs::relpath &, char *, size_t); int (*removexattr)(const fuse_req_ctx_t *, - const char *, + const fs::relpath &, const char *); int (*opendir)(const fuse_req_ctx_t *, - const char *, + const fs::relpath &, fuse_file_info_t *); int (*readdir)(const fuse_req_ctx_t *, const fuse_file_info_t *, @@ -162,10 +163,10 @@ struct fuse_operations void *(*init)(fuse_conn_info_t *conn); void (*destroy)(void); int (*access)(const fuse_req_ctx_t *, - const char *, + const fs::relpath &, int); int (*create)(const fuse_req_ctx_t *, - const char *, + const fs::relpath &, mode_t, fuse_file_info_t *); int (*ftruncate)(const fuse_req_ctx_t *, @@ -176,13 +177,13 @@ struct fuse_operations struct stat *, fuse_timeouts_t *); int (*utimens)(const fuse_req_ctx_t *, - const char *, + const fs::relpath &, const struct timespec tv[2]); int (*futimens)(const fuse_req_ctx_t *, const uint64_t fh, const struct timespec tv[2]); int (*bmap)(const fuse_req_ctx_t *, - const char *, + const fs::relpath &, size_t blocksize, uint64_t *idx); int (*ioctl)(const fuse_req_ctx_t *ctx, @@ -227,11 +228,11 @@ struct fuse_operations int (*removemapping)(const fuse_req_ctx_t *); int (*syncfs)(const fuse_req_ctx_t *); int (*tmpfile)(const fuse_req_ctx_t *, - const char *, + const fs::relpath &, mode_t, fuse_file_info_t *); int (*statx)(const fuse_req_ctx_t *, - const char *fusepath, + const fs::relpath &fusepath, const uint32_t flags, const uint32_t mask, struct fuse_statx *st, diff --git a/vendored/libfuse/lib/fs_path.cpp b/vendored/libfuse/lib/fs_path.cpp new file mode 100644 index 00000000..0f82b7f4 --- /dev/null +++ b/vendored/libfuse/lib/fs_path.cpp @@ -0,0 +1,555 @@ +/* + ISC License + + Copyright (c) 2026, Antonio SJ Musumeci + + 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. +*/ + +#include "fs_path.hpp" + +#include +#include + + +fs::relpath::relpath() noexcept + : _start(REL_OFFSET), + _size(0), + _rel_start(REL_OFFSET), + _rel_size(0) +{ + _buf[REL_OFFSET] = '\0'; +} + +fs::relpath::relpath(const char *s_) + : fs::relpath() +{ + if(s_ != nullptr) + assign(std::string_view(s_)); +} + +fs::relpath::relpath(std::string_view s_) + : fs::relpath() +{ + assign(s_); +} + +fs::relpath::relpath(const std::string &s_) + : fs::relpath() +{ + assign(std::string_view(s_)); +} + +fs::relpath::relpath(const fs::relpath &o_) + : _start(o_._start), + _size(o_._size), + _rel_start(o_._rel_start), + _rel_size(o_._rel_size) +{ + std::memcpy(_buf + _start,o_._buf + _start,_size + 1); +} + +fs::relpath::relpath(fs::relpath &&o_) noexcept + : _start(o_._start), + _size(o_._size), + _rel_start(o_._rel_start), + _rel_size(o_._rel_size) +{ + std::memcpy(_buf + _start,o_._buf + _start,_size + 1); +} + +fs::relpath& +fs::relpath::operator=(const fs::relpath &o_) +{ + if(this == &o_) + return *this; + _start = o_._start; + _size = o_._size; + _rel_start = o_._rel_start; + _rel_size = o_._rel_size; + std::memcpy(_buf + _start,o_._buf + _start,_size + 1); + return *this; +} + +fs::relpath& +fs::relpath::operator=(fs::relpath &&o_) noexcept +{ + if(this == &o_) + return *this; + _start = o_._start; + _size = o_._size; + _rel_start = o_._rel_start; + _rel_size = o_._rel_size; + std::memcpy(_buf + _start,o_._buf + _start,_size + 1); + return *this; +} + +fs::relpath& +fs::relpath::operator=(const char *s_) +{ + return assign(s_ ? std::string_view(s_) : std::string_view()); +} + +fs::relpath& +fs::relpath::operator=(std::string_view s_) +{ + return assign(s_); +} + +fs::relpath& +fs::relpath::operator=(const std::string &s_) +{ + return assign(std::string_view(s_)); +} + +void +fs::relpath::_init(std::string_view s_) +{ + // No transformation: store the input verbatim. The caller is + // responsible for canonical rel form (no leading '/'). Mergerfs's + // pipeline guarantees this: + // - libfuse's try_get_path() ends with consolidate_to_rel(strip= + // true), which drops the leading '/' produced by prepend(). + // - branch.path / fusepath via the free operator/ flows through + // concat() which does no stripping; the rel side comes in + // canonical and stays canonical. + // + // Storing inputs verbatim means an fs::relpath can also hold a + // fully-built absolute fullpath (e.g. the result of `branch.path / + // fusepath_`) without losing the leading '/'. Such an object is + // not a candidate for set_prefix() rebuilding -- doing so would + // produce double slashes -- but that combination is explicitly + // outside the documented contract. + + const std::size_t n = s_.size(); + if(n > 0) + std::memmove(_buf + REL_OFFSET,s_.data(),n); + _start = REL_OFFSET; + _size = static_cast(n); + _rel_start = REL_OFFSET; + _rel_size = static_cast(n); + _buf[REL_OFFSET+n] = '\0'; +} + +fs::relpath& +fs::relpath::assign(std::string_view s_) +{ + _init(s_); + return *this; +} + +void +fs::relpath::clear() noexcept +{ + // Reset to the canonical default-constructed state: anchored at + // REL_OFFSET, empty. This makes prepend() safe immediately after + // clear() so the path can be reused. + _size = 0; + _rel_start = REL_OFFSET; + _rel_size = 0; + _start = REL_OFFSET; + _buf[_start] = '\0'; +} + +void +fs::relpath::set_prefix(std::string_view p_) +{ + // Contract: paths are in canonical mergerfs form. + // - prefix has no trailing '/' + // - rel has no leading '/' + // No sanitization happens here; if you violate the contract you + // get double slashes. Mergerfs's fusepaths come from libfuse with + // the leading '/' already stripped (consolidate_to_rel(strip=true) + // in try_get_path) and branch paths are realpath-canonicalized at + // config load, so the contract holds. + // + // Fast path: the prefix (plus '/' separator) fits in front of the + // current rel anchor _rel_start. Rel data is not touched, only + // [_start, _rel_start) is rewritten. + // + // Slow path: prefix is larger than the current prefix area. The + // rel data is shifted rightward, _rel_start advances to make room, + // then the prefix is written linearly. Under Linux PATH_MAX this + // branch is unreachable; it engages only under contract violation + // or future-larger PATH_MAX environments. + + const std::size_t rel_size = _rel_size; // cached + const std::size_t p_size = p_.size(); + + if(p_size == 0) + { + // Drop the prefix. Rel stays at its current anchor. + _start = _rel_start; + _size = static_cast(rel_size); + _buf[_start + _size] = '\0'; + return; + } + + const std::size_t sep = (rel_size > 0) ? 1 : 0; + const std::size_t needed = p_size + sep; // bytes of prefix-area required + + // Contract guard: prefix + sep + rel + NUL must fit in BUF_SIZE. + // Violations should be caught at the source rather than silently + // corrupting the buffer via uint16 overflow. + assert(needed + rel_size < BUF_SIZE); + + if(needed > _rel_start) + { + // Slow path: shift rel rightward so the prefix area can grow. + const std::size_t new_rel_start = needed; + if(rel_size > 0) + std::memmove(_buf + new_rel_start,_buf + _rel_start,rel_size); + _rel_start = static_cast(new_rel_start); + } + + const std::size_t new_start = _rel_start - needed; + std::memmove(_buf + new_start,p_.data(),p_size); + if(sep) + _buf[new_start + p_size] = '/'; + _start = static_cast(new_start); + _size = static_cast(needed + rel_size); + _buf[_start + _size] = '\0'; + // _rel_size unchanged: set_prefix only rewrites the prefix area + // (and, in the slow path, relocates the rel without changing its + // length). +} + +bool +fs::relpath::_aliases(std::string_view s_) const noexcept +{ + // Returns true when the view's data pointer lies inside _buf. + // Used to detect self-aliasing in assign_concat so we can stash the + // source before clobbering it. + std::uintptr_t p = reinterpret_cast(s_.data()); + std::uintptr_t b = reinterpret_cast(_buf); + return (p >= b) && (p < b + BUF_SIZE); +} + +fs::relpath& +fs::relpath::assign_concat(std::string_view base_, + std::string_view rel_) +{ + // The common case is that neither input aliases _buf (e.g. + // `branch.path / fusepath`, where branch.path is std::string and + // fusepath is a stack-owned fs::relpath belonging to a different + // object). Skip the BUF_SIZE stack reservation in that case and only + // allocate the temp when at least one input does alias. + // + // When aliasing is detected, stash the aliasing input(s) to a + // stack-local temp so the subsequent writes don't corrupt our own + // source data (e.g. `p /= "x"` where base_ is p.native()). The temp + // is BUF_SIZE so it can fit any well-formed base+rel. + const bool base_aliases = _aliases(base_); + const bool rel_aliases = _aliases(rel_); + if(base_aliases || rel_aliases) + { + alignas(8) char tmp[BUF_SIZE]; + std::size_t tmp_pos = 0; + if(base_aliases) + { + std::memcpy(tmp + tmp_pos,base_.data(),base_.size()); + base_ = std::string_view(tmp + tmp_pos,base_.size()); + tmp_pos += base_.size(); + } + if(rel_aliases) + { + std::memcpy(tmp + tmp_pos,rel_.data(),rel_.size()); + rel_ = std::string_view(tmp + tmp_pos,rel_.size()); + } + // base_ and rel_ now point into `tmp`. The rel write below and + // set_prefix both complete before this scope ends, so tmp's + // lifetime covers all reads. + _init(rel_); + set_prefix(base_); + return *this; + } + + // Common path: no aliasing -- write rel at REL_OFFSET via _init, + // then prepend base via set_prefix. + _init(rel_); + set_prefix(base_); + return *this; +} + +fs::relpath +fs::relpath::concat(std::string_view base_, + std::string_view rel_) +{ + fs::relpath p; + p.assign_concat(base_,rel_); + return p; +} + +fs::relpath +fs::relpath::operator/(std::string_view rhs_) const +{ + return fs::relpath::concat(native(),rhs_); +} + +fs::relpath& +fs::relpath::operator/=(std::string_view rhs_) +{ + return assign_concat(native(),rhs_); +} + +std::string_view +fs::relpath::filename() const noexcept +{ + if(_size == 0) + return {}; + std::string_view sv = native(); + auto pos = sv.rfind('/'); + if(pos == std::string_view::npos) + return sv; + return sv.substr(pos + 1); +} + +std::string_view +fs::relpath::parent_path() const noexcept +{ + if(_size <= 1) + return {}; // "" or "/" -> "" + std::string_view sv = native(); + auto pos = sv.rfind('/'); + if(pos == std::string_view::npos) + return {}; + if(pos == 0) + return sv.substr(0,1); // "/foo" -> "/" + return sv.substr(0,pos); +} + +std::string_view +fs::relpath::extension() const noexcept +{ + std::string_view fn = filename(); + if(fn.empty() || fn == "." || fn == "..") + return {}; + auto pos = fn.rfind('.'); + if(pos == std::string_view::npos || pos == 0) + return {}; + return fn.substr(pos); +} + +std::string_view +fs::relpath::stem() const noexcept +{ + std::string_view fn = filename(); + if(fn == "." || fn == "..") + return fn; + std::string_view ext = extension(); + return fn.substr(0,fn.size() - ext.size()); +} + +fs::relpath& +fs::relpath::remove_filename() +{ + if(_size == 0) + return *this; + std::string_view sv = native(); + auto pos = sv.rfind('/'); + if(pos == std::string_view::npos) + { + clear(); + return *this; + } + // Keep the trailing slash so the result is the directory form. + // Use parent_path() if you want the dirname without the trailing '/'. + const std::size_t new_size = pos + 1; + const std::size_t prefix_len = _rel_start - _start; + _size = static_cast(new_size); + // Truncating into the prefix area drops the rel portion entirely; + // clamp to 0 so the uint16 can't underflow. + _rel_size = (new_size > prefix_len) + ? static_cast(new_size - prefix_len) + : 0; + _buf[_start + _size] = '\0'; + return *this; +} + +void +fs::relpath::resize(std::size_t n_) +{ + if(n_ > _size) + // Zero-fill new bytes to match std::string::resize semantics. + std::memset(_buf + _start + _size,'\0',n_ - _size); + const std::size_t prefix_len = _rel_start - _start; + _size = static_cast(n_); + // Shrinking below the prefix area drops the rel portion entirely; + // clamp to 0 so the uint16 can't underflow. + _rel_size = (n_ > prefix_len) + ? static_cast(n_ - prefix_len) + : 0; + _buf[_start + _size] = '\0'; +} + +void +fs::relpath::swap(fs::relpath &o_) noexcept +{ + // Self-swap is a no-op; bail before touching memcpy with identical + // src/dst pointers (UB per the C standard) and avoid the round-trip + // through the stack temp. + if(this == &o_) + return; + + // Copy this's used portion to a stack temp, slot in o's data, then + // restore the saved data into o. Touches only the actually-used + // bytes plus the metadata quadruplet. + alignas(8) char tmp[BUF_SIZE]; + const std::uint16_t my_start = _start; + const std::uint16_t my_size = _size; + const std::uint16_t my_rel_start = _rel_start; + const std::uint16_t my_rel_size = _rel_size; + + std::memcpy(tmp + my_start,_buf + my_start,my_size + 1); + + _start = o_._start; + _size = o_._size; + _rel_start = o_._rel_start; + _rel_size = o_._rel_size; + std::memcpy(_buf + _start,o_._buf + _start,_size + 1); + + o_._start = my_start; + o_._size = my_size; + o_._rel_start = my_rel_start; + o_._rel_size = my_rel_size; + std::memcpy(o_._buf + my_start,tmp + my_start,my_size + 1); +} + +std::string_view +fs::relpath::prefix() const noexcept +{ + const std::size_t plen = _rel_start - _start; + return {_buf + _start,plen}; +} + +std::string_view +fs::relpath::rel() const noexcept +{ + return {_buf + _rel_start,_rel_size}; +} + +void +fs::relpath::append(const char *s_, + std::size_t n_) +{ + if(n_ == 0) + return; + std::memcpy(_buf + _start + _size,s_,n_); + _size = static_cast(_size + n_); + _rel_size = static_cast(_rel_size + n_); + _buf[_start + _size] = '\0'; +} + +void +fs::relpath::append(std::string_view s_) +{ + append(s_.data(),s_.size()); +} + +namespace +{ + // Extracts the next path component from sv (which has leading '/' + // already consumed), advancing sv past it. Returns empty when no + // more components. + inline + std::string_view + _next_component(std::string_view &sv_) + { + while(!sv_.empty() && sv_.front() == '/') + sv_.remove_prefix(1); + if(sv_.empty()) + return {}; + auto pos = sv_.find('/'); + std::string_view c = + (pos == std::string_view::npos) ? sv_ : sv_.substr(0,pos); + sv_.remove_prefix(c.size()); + return c; + } +} + +fs::relpath +fs::relpath::lexically_relative(std::string_view base_) const +{ + std::string_view a = native(); + std::string_view b = base_; + + // Both sides must have the same shape. fs::relpath always starts in + // canonical (no-leading-slash) form, but legacy callers and the + // libfuse symlink-rewrite path may pass leading-slash literals + // through native(); accept either as long as both match. Shape + // mismatch returns an empty result; a successful relativization + // produces "." (for equal paths) so empty is unambiguous failure. + const bool a_abs = (!a.empty() && a.front() == '/'); + const bool b_abs = (!b.empty() && b.front() == '/'); + if(a_abs != b_abs) + return fs::relpath(); + if(a_abs) + { + a.remove_prefix(1); + b.remove_prefix(1); + } + + for(;;) + { + auto sa = a; + auto sb = b; + auto ca = _next_component(sa); + auto cb = _next_component(sb); + if(ca.empty() && cb.empty()) + break; + if(ca != cb) + break; + a = sa; + b = sb; + } + + std::size_t up = 0; + { + std::string_view tmp = b; + for(;;) + { + auto c = _next_component(tmp); + if(c.empty()) + break; + if(c == "..") + // ".." in base means the relativization can't resolve + // without filesystem access; return empty. + return fs::relpath(); + if(c != ".") + ++up; + } + } + + fs::relpath result; + for(std::size_t i = 0; i < up; ++i) + { + if(i > 0) + result.append("/",1); + result.append("..",2); + } + + for(;;) + { + auto c = _next_component(a); + if(c.empty()) + break; + if(!result.empty()) + result.append("/",1); + result.append(c); + } + + if(result.empty()) + result.assign("."); + + return result; +} diff --git a/vendored/libfuse/lib/fuse.cpp b/vendored/libfuse/lib/fuse.cpp index ea875882..bd135eb3 100644 --- a/vendored/libfuse/lib/fuse.cpp +++ b/vendored/libfuse/lib/fuse.cpp @@ -15,6 +15,7 @@ #include "crc32b.h" #include "debug.hpp" #include "fatal.hpp" +#include "fs_path.hpp" #include "kvec.h" #include "mutex.hpp" #include "node.hpp" @@ -78,11 +79,18 @@ struct lock_queue_element pthread_cond_t cond; uint64_t nodeid1; const char *name1; - char **path1; + // path1 / path2 are non-owning pointers to the caller's stack- + // allocated fs::relpath objects (try_get_path / try_get_path2 take + // a relpath* and fill it in place). Lifetime is bounded by the + // calling FUSE op, which blocks on `cond` until the lock holder + // signals -- the caller's stack frame is therefore guaranteed to + // outlive the queue entry. Do not copy a lock_queue_element to a + // longer-lived storage without reconsidering this invariant. + fs::relpath *path1; node_t **wnode1; uint64_t nodeid2; const char *name2; - char **path2; + fs::relpath *path2; node_t **wnode2; int err; bool done : 1; @@ -655,46 +663,6 @@ find_node(uint64_t parent, return node; } -static -char* -add_name(char **buf, - unsigned *bufsize, - char *s, - const char *name) -{ - size_t len = strlen(name); - - if(s - len <= *buf) - { - unsigned pathlen = *bufsize - (s - *buf); - unsigned newbufsize = *bufsize; - char *newbuf; - - while(newbufsize < pathlen + len + 1) - { - if(newbufsize >= PATH_BUFSIZE_HALF_MAX) - newbufsize = PATH_BUFSIZE_MAX; - else - newbufsize *= 2; - } - - newbuf = (char*)realloc(*buf,newbufsize); - if(newbuf == NULL) - return NULL; - - *buf = newbuf; - s = newbuf + newbufsize - pathlen; - memmove(s,newbuf + *bufsize - pathlen,pathlen); - *bufsize = newbufsize; - } - s -= len; - strncpy(s,name,len); - s--; - *s = '/'; - - return s; -} - static void unlock_path(uint64_t nodeid, @@ -728,45 +696,33 @@ unlock_path(uint64_t nodeid, } /* - Throughout this file all calls to the op.func() callbacks will be - '&fusepath[1]' because the current path generation always prefixes a - '/' for each path but we need a relative path for usage with - `openat()` functions and would rather do that here than in the - called function. + Path storage is the caller's stack-allocated fs::relpath; this function + fills it via prepend() (walk leaf-to-root, prepending "/component"). + No heap allocation per op. + + Final step before returning: consolidate_to_rel(true) moves the data + to the rel anchor (REL_OFFSET) and strips the leading '/'. Callers + (f.ops.* callbacks) receive a REL-layout fs::relpath with no leading + slash, ready for set_prefix-style assembly in mergerfs's per-branch + loops without further conversion. */ static int try_get_path(uint64_t nodeid, const char *name, - char **path, + fs::relpath *path, node_t **wnodep, bool need_lock) { - unsigned bufsize = 256; - char *buf; - char *s; node_t *node; node_t *wnode = NULL; int err; - *path = NULL; - - err = -ENOMEM; - buf = (char*)malloc(bufsize); - if(buf == NULL) - goto out_err; - - s = buf + bufsize - 1; - *s = '\0'; + path->clear(); if(name != NULL) - { - s = add_name(&buf,&bufsize,s,name); - err = -ENOMEM; - if(s == NULL) - goto out_free; - } + path->prepend(name); if(wnodep) { @@ -779,7 +735,7 @@ try_get_path(uint64_t nodeid, if(wnode->treelock > 0) wnode->treelock += TREELOCK_WAIT_OFFSET; err = -EAGAIN; - goto out_free; + goto out_err; } wnode->treelock = TREELOCK_WRITE; } @@ -798,10 +754,7 @@ try_get_path(uint64_t nodeid, if(node->name == NULL || node->parent == NULL) goto out_unlock; - err = -ENOMEM; - s = add_name(&buf,&bufsize,s,node->name); - if(s == NULL) - goto out_unlock; + path->prepend(node->name); if(need_lock) { @@ -813,12 +766,12 @@ try_get_path(uint64_t nodeid, } } - if(s[0]) - memmove(buf,s,bufsize - (s - buf)); - else - strcpy(buf,"/"); + // Move data from the prefix area to the rel anchor and strip the + // leading '/' produced by prepend(). After this the path is in + // mergerfs's canonical REL form (no leading slash, anchored at + // REL_OFFSET). + path->consolidate_to_rel(/*strip_leading_slash=*/true); - *path = buf; if(wnodep) *wnodep = wnode; @@ -827,9 +780,15 @@ try_get_path(uint64_t nodeid, out_unlock: if(need_lock) unlock_path(nodeid,wnode,node); - out_free: - free(buf); - + // Reset the path to empty on every error exit. This is load-bearing + // for callers that mask -ESTALE to 0 (statx_path, see fuse_lib_ + // statx_path): a nameless node yields a partial prepend chain that + // must not be observed as a valid path. After reset, the + // dispatcher's "is the path empty?" test cleanly distinguishes + // (nodeid == FUSE_ROOT_ID) "root walk produced empty" from + // (nodeid != FUSE_ROOT_ID) "nameless / ESTALE-masked" without + // looking at `err`. + path->clear(); out_err: return err; } @@ -840,8 +799,8 @@ try_get_path2(uint64_t nodeid1, const char *name1, uint64_t nodeid2, const char *name2, - char **path1, - char **path2, + fs::relpath *path1, + fs::relpath *path2, node_t **wnode1, node_t **wnode2) { @@ -856,7 +815,7 @@ try_get_path2(uint64_t nodeid1, node_t *wn1 = wnode1 ? *wnode1 : NULL; unlock_path(nodeid1,wn1,NULL); - free(*path1); + // path1 storage is stack-owned by the caller; no free. } } @@ -964,7 +923,7 @@ static int get_path_common(uint64_t nodeid, const char *name, - char **path, + fs::relpath *path, node_t **wnode) { int err; @@ -990,7 +949,7 @@ get_path_common(uint64_t nodeid, static int get_path(uint64_t nodeid, - char **path) + fs::relpath *path) { return get_path_common(nodeid,NULL,path,NULL); } @@ -999,7 +958,7 @@ static int get_path_name(uint64_t nodeid, const char *name, - char **path) + fs::relpath *path) { return get_path_common(nodeid,name,path,NULL); } @@ -1008,7 +967,7 @@ static int get_path_wrlock(uint64_t nodeid, const char *name, - char **path, + fs::relpath *path, node_t **wnode) { return get_path_common(nodeid,name,path,wnode); @@ -1020,8 +979,8 @@ get_path2(uint64_t nodeid1, const char *name1, uint64_t nodeid2, const char *name2, - char **path1, - char **path2, + fs::relpath *path1, + fs::relpath *path2, node_t **wnode1, node_t **wnode2) { @@ -1053,15 +1012,13 @@ get_path2(uint64_t nodeid1, static void free_path_wrlock(uint64_t nodeid, - node_t *wnode, - char *path) + node_t *wnode) { mutex_lock(f.lock); unlock_path(nodeid,wnode,NULL); if(f.lockq) wake_up_queued(); mutex_unlock(f.lock); - free(path); } static @@ -1089,17 +1046,21 @@ update_stat(node_t *node_, static void -free_path(uint64_t nodeid, - char *path) +free_path(uint64_t nodeid, + const fs::relpath *path) { - if(path) - free_path_wrlock(nodeid,NULL,path); + // The caller's stack-owned fs::relpath is empty when no get_path*() + // succeeded for this op (or when an intermediate "free + reuse" + // step like free_path_and_inc_open already released the read locks + // and clear()d the path). In either case there is nothing to do. + if(path->empty()) + return; + free_path_wrlock(nodeid,NULL); } static node_t* free_path_and_inc_open(u64 free_nodeid_, - char *path_, u64 inc_open_ino_, bool remember_too_) { @@ -1119,7 +1080,6 @@ free_path_and_inc_open(u64 free_nodeid_, wake_up_queued(); mutex_unlock(f.lock); - free(path_); return node; } @@ -1127,7 +1087,6 @@ free_path_and_inc_open(u64 free_nodeid_, static bool free_path_and_update_stat(u64 nodeid_, - char *path_, const struct stat *stnew_) { bool ok; @@ -1144,7 +1103,6 @@ free_path_and_update_stat(u64 nodeid_, wake_up_queued(); mutex_unlock(f.lock); - free(path_); return ok; } @@ -1154,17 +1112,13 @@ void free_path2(uint64_t nodeid1, uint64_t nodeid2, node_t *wnode1, - node_t *wnode2, - char *path1, - char *path2) + node_t *wnode2) { mutex_lock(f.lock); unlock_path(nodeid1,wnode1,NULL); unlock_path(nodeid2,wnode2,NULL); wake_up_queued(); mutex_unlock(f.lock); - free(path1); - free(path2); } static @@ -1359,7 +1313,7 @@ int lookup_path(fuse_req_t *req_, uint64_t nodeid, const char *name, - const char *fusepath, + const fs::relpath &fusepath, struct fuse_entry_param *e, fuse_file_info_t *fi, bool remember = true) @@ -1371,7 +1325,7 @@ lookup_path(fuse_req_t *req_, memset(e,0,sizeof(struct fuse_entry_param)); rv = ((fi == NULL) ? - f.ops.getattr(&req_->ctx,&fusepath[1],&e->attr,&e->timeout) : + f.ops.getattr(&req_->ctx,fusepath,&e->attr,&e->timeout) : f.ops.fgetattr(&req_->ctx,fi->fh,&e->attr,&e->timeout)); if(rv) @@ -1422,7 +1376,7 @@ fuse_lib_lookup(fuse_req_t *req_, { int err; uint64_t nodeid; - char *fusepath; + fs::relpath fusepath; const char *name; node_t *dot = NULL; struct fuse_entry_param e = {}; @@ -1475,7 +1429,7 @@ fuse_lib_lookup(fuse_req_t *req_, e.ino = 0; err = 0; } - free_path(nodeid,fusepath); + free_path(nodeid,&fusepath); } if(dot) @@ -1537,7 +1491,7 @@ fuse_lib_getattr(fuse_req_t *req_, struct fuse_in_header *hdr_) { int err; - char *fusepath; + fs::relpath fusepath; uint64_t fh; struct stat buf; node_t *node; @@ -1553,7 +1507,7 @@ fuse_lib_getattr(fuse_req_t *req_, memset(&buf,0,sizeof(buf)); err = 0; - fusepath = NULL; + fusepath.clear(); if(fh == 0) { err = get_path(hdr_->nodeid,&fusepath); @@ -1564,13 +1518,29 @@ fuse_lib_getattr(fuse_req_t *req_, if(!err) { bool node_ok = true; - if(fusepath != NULL) + // Three-way dispatch: + // - explicit fh (fh != 0) -> fgetattr(fh) + // - empty fusepath, not FUSE_ROOT_ID -> nameless node + // (ESTALE was masked + // above): fall back + // to fgetattr(0) so + // mergerfs can find + // the file via + // OpenFiles. + // - otherwise -> path-based getattr, + // including + // FUSE_ROOT_ID where + // the rel is empty. + const bool path_based = + (fh == 0) && + (!fusepath.empty() || hdr_->nodeid == FUSE_ROOT_ID); + if(path_based) { - err = f.ops.getattr(&req_->ctx,&fusepath[1],&buf,&timeouts); + err = f.ops.getattr(&req_->ctx,fusepath,&buf,&timeouts); if(!err) - node_ok = free_path_and_update_stat(hdr_->nodeid,fusepath,&buf); + node_ok = free_path_and_update_stat(hdr_->nodeid,&buf); else - free_path(hdr_->nodeid,fusepath); + free_path(hdr_->nodeid,&fusepath); } else { @@ -1583,6 +1553,10 @@ fuse_lib_getattr(fuse_req_t *req_, if(node_ok) update_stat(node,&buf); } + // get_path may have run and produced an empty path on the + // ESTALE-masked nameless branch; release any state it left. + if(fh == 0) + free_path(hdr_->nodeid,&fusepath); } if(!err && !node_ok) @@ -1610,7 +1584,7 @@ fuse_lib_statx_path(fuse_req_t *req_, fuse_statx_in *inarg_) { int err; - char *fusepath; + fs::relpath fusepath; struct fuse_statx st{}; fuse_timeouts_t timeouts{}; @@ -1623,9 +1597,22 @@ fuse_lib_statx_path(fuse_req_t *req_, return; } - if(fusepath != NULL) + // Three cases for an empty fusepath after get_path: + // 1. nodeid == FUSE_ROOT_ID, walk produced 0 components -- a real + // path query for the mountpoint root. + // 2. nodeid != FUSE_ROOT_ID, ESTALE was masked above -- a nameless + // node (file held open after unlink). The named-path lookup + // cannot resolve it; fall back to fh-based statx, which lets + // mergerfs find the file via OpenFiles by its FUSE handle. + // 3. nodeid != FUSE_ROOT_ID, success -- the path is actually + // empty only if the call returned 0 with empty(); the only + // such path is FUSE_ROOT_ID, so this case does not arise. + // try_get_path()'s error paths now reset the path to empty, so + // case 2 always observably produces an empty fusepath -- the + // (nodeid, empty) pair uniquely identifies the nameless case. + if(!fusepath.empty() || hdr_->nodeid == FUSE_ROOT_ID) err = f.ops.statx(&req_->ctx, - &fusepath[1], + fusepath, inarg_->sx_flags, inarg_->sx_mask, &st, @@ -1638,7 +1625,7 @@ fuse_lib_statx_path(fuse_req_t *req_, &st, &timeouts); - free_path(hdr_->nodeid,fusepath); + free_path(hdr_->nodeid,&fusepath); if(err) fuse_reply_err(req_,err); @@ -1691,7 +1678,7 @@ fuse_lib_setattr(fuse_req_t *req_, { uint64_t fh; struct stat stbuf = {}; - char *fusepath; + fs::relpath fusepath; int err; fuse_timeouts_t timeouts{}; struct fuse_setattr_in *arg; @@ -1703,7 +1690,7 @@ fuse_lib_setattr(fuse_req_t *req_, fh = arg->fh; err = 0; - fusepath = NULL; + fusepath.clear(); if(fh == 0) { err = get_path(hdr_->nodeid,&fusepath); @@ -1713,10 +1700,20 @@ fuse_lib_setattr(fuse_req_t *req_, if(!err) { + // Three-way dispatch (see fuse_lib_getattr for full notes): + // - explicit fh -> fXXX(fh) + // - empty fusepath, not FUSE_ROOT_ID -> nameless node + // (ESTALE was masked): + // fall back to fXXX(0) + // - otherwise -> path-based op + const bool path_based = + (fh == 0) && + (!fusepath.empty() || hdr_->nodeid == FUSE_ROOT_ID); + err = 0; if(!err && (arg->valid & FATTR_MODE)) - err = ((fusepath != NULL) ? - f.ops.chmod(&req_->ctx,&fusepath[1],arg->mode) : + err = (path_based ? + f.ops.chmod(&req_->ctx,fusepath,arg->mode) : f.ops.fchmod(&req_->ctx,fh,arg->mode)); if(!err && (arg->valid & (FATTR_UID | FATTR_GID))) @@ -1724,14 +1721,14 @@ fuse_lib_setattr(fuse_req_t *req_, uid_t uid = ((arg->valid & FATTR_UID) ? arg->uid : (uid_t)-1); gid_t gid = ((arg->valid & FATTR_GID) ? arg->gid : (gid_t)-1); - err = ((fusepath != NULL) ? - f.ops.chown(&req_->ctx,&fusepath[1],uid,gid) : + err = (path_based ? + f.ops.chown(&req_->ctx,fusepath,uid,gid) : f.ops.fchown(&req_->ctx,fh,uid,gid)); } if(!err && (arg->valid & FATTR_SIZE)) - err = ((fusepath != NULL) ? - f.ops.truncate(&req_->ctx,&fusepath[1],arg->size) : + err = (path_based ? + f.ops.truncate(&req_->ctx,fusepath,arg->size) : f.ops.ftruncate(&req_->ctx,fh,arg->size)); if(!err && (arg->valid & (FATTR_ATIME | FATTR_MTIME))) @@ -1753,18 +1750,22 @@ fuse_lib_setattr(fuse_req_t *req_, else if(arg->valid & FATTR_MTIME) tv[1] = (struct timespec){ static_cast(arg->mtime), static_cast(arg->mtimensec) }; - err = ((fusepath != NULL) ? - f.ops.utimens(&req_->ctx,&fusepath[1],tv) : + err = (path_based ? + f.ops.utimens(&req_->ctx,fusepath,tv) : f.ops.futimens(&req_->ctx,fh,tv)); } if(!err) - err = ((fusepath != NULL) ? - f.ops.getattr(&req_->ctx,&fusepath[1],&stbuf,&timeouts) : + err = (path_based ? + f.ops.getattr(&req_->ctx,fusepath,&stbuf,&timeouts) : f.ops.fgetattr(&req_->ctx,fh,&stbuf,&timeouts)); bool stat_updated = false; - if(!err && (fusepath != NULL)) + // Only the path-based branch held a path lock that needs + // unlock_path; the fh-based branch (incl. nameless fallback) + // never acquired one. free_path() handles both cases on the + // shared cleanup edge below. + if(!err && path_based) { node_t *node; mutex_lock(f.lock); @@ -1775,12 +1776,11 @@ fuse_lib_setattr(fuse_req_t *req_, if(f.lockq) wake_up_queued(); mutex_unlock(f.lock); - free(fusepath); stat_updated = true; } else { - free_path(hdr_->nodeid,fusepath); + free_path(hdr_->nodeid,&fusepath); } if(!err) @@ -1814,7 +1814,7 @@ fuse_lib_access(fuse_req_t *req_, struct fuse_in_header *hdr_) { int err; - char *fusepath; + fs::relpath fusepath; struct fuse_access_in *arg; arg = (fuse_access_in*)fuse_hdr_arg(hdr_); @@ -1823,9 +1823,9 @@ fuse_lib_access(fuse_req_t *req_, if(!err) { err = f.ops.access(&req_->ctx, - &fusepath[1], + fusepath, arg->mask); - free_path(hdr_->nodeid,fusepath); + free_path(hdr_->nodeid,&fusepath); } fuse_reply_err(req_,err); @@ -1837,17 +1837,17 @@ fuse_lib_readlink(fuse_req_t *req_, struct fuse_in_header *hdr_) { ssize_t rv; - char *fusepath; + fs::relpath fusepath; char linkname[PATH_MAX + 1]; rv = get_path(hdr_->nodeid,&fusepath); if(!rv) { rv = f.ops.readlink(&req_->ctx, - &fusepath[1], + fusepath, linkname, sizeof(linkname)); - free_path(hdr_->nodeid,fusepath); + free_path(hdr_->nodeid,&fusepath); } if(rv >= 0) @@ -1862,7 +1862,7 @@ fuse_lib_mknod(fuse_req_t *req_, struct fuse_in_header *hdr_) { int err; - char *fusepath; + fs::relpath fusepath; const char* name; struct fuse_entry_param e; struct fuse_mknod_in *arg; @@ -1885,7 +1885,7 @@ fuse_lib_mknod(fuse_req_t *req_, ffi.flags = O_CREAT | O_EXCL | O_WRONLY; err = f.ops.create(&req_->ctx, - &fusepath[1], + fusepath, arg->mode, &ffi); if(!err) @@ -1899,14 +1899,14 @@ fuse_lib_mknod(fuse_req_t *req_, if(err == -ENOSYS) { err = f.ops.mknod(&req_->ctx, - &fusepath[1], + fusepath, arg->mode, arg->rdev); if(!err) err = lookup_path(req_,hdr_->nodeid,name,fusepath,&e,NULL); } - free_path(hdr_->nodeid,fusepath); + free_path(hdr_->nodeid,&fusepath); } reply_entry(req_,&e,err); @@ -1918,7 +1918,7 @@ fuse_lib_mkdir(fuse_req_t *req_, struct fuse_in_header *hdr_) { int err; - char *fusepath; + fs::relpath fusepath; const char *name; struct fuse_entry_param e; struct fuse_mkdir_in *arg; @@ -1932,11 +1932,11 @@ fuse_lib_mkdir(fuse_req_t *req_, if(!err) { err = f.ops.mkdir(&req_->ctx, - &fusepath[1], + fusepath, arg->mode); if(!err) err = lookup_path(req_,hdr_->nodeid,name,fusepath,&e,NULL); - free_path(hdr_->nodeid,fusepath); + free_path(hdr_->nodeid,&fusepath); } reply_entry(req_,&e,err); @@ -1948,7 +1948,7 @@ fuse_lib_unlink(fuse_req_t *req_, struct fuse_in_header *hdr_) { int err; - char *fusepath; + fs::relpath fusepath; const char *name; node_t *wnode; @@ -1962,11 +1962,11 @@ fuse_lib_unlink(fuse_req_t *req_, req_->ctx.nodeid = wnode->nodeid; err = f.ops.unlink(&req_->ctx, - &fusepath[1]); + fusepath); if(!err) remove_node(hdr_->nodeid,name); - free_path_wrlock(hdr_->nodeid,wnode,fusepath); + free_path_wrlock(hdr_->nodeid,wnode); } fuse_reply_err(req_,err); @@ -1978,7 +1978,7 @@ fuse_lib_rmdir(fuse_req_t *req_, struct fuse_in_header *hdr_) { int err; - char *fusepath; + fs::relpath fusepath; const char *name; node_t *wnode; @@ -1988,10 +1988,10 @@ fuse_lib_rmdir(fuse_req_t *req_, if(!err) { err = f.ops.rmdir(&req_->ctx, - &fusepath[1]); + fusepath); if(!err) remove_node(hdr_->nodeid,name); - free_path_wrlock(hdr_->nodeid,wnode,fusepath); + free_path_wrlock(hdr_->nodeid,wnode); } fuse_reply_err(req_,err); @@ -2003,7 +2003,7 @@ fuse_lib_symlink(fuse_req_t *req_, struct fuse_in_header *hdr_) { int rv; - char *fusepath; + fs::relpath fusepath; const char *name; const char *linkname; struct fuse_entry_param e = {}; @@ -2016,12 +2016,12 @@ fuse_lib_symlink(fuse_req_t *req_, { rv = f.ops.symlink(&req_->ctx, linkname, - &fusepath[1], + fusepath, &e.attr, &e.timeout); if(rv == 0) rv = set_path_info(hdr_->nodeid,name,&e); - free_path(hdr_->nodeid,fusepath); + free_path(hdr_->nodeid,&fusepath); } reply_entry(req_,&e,rv); @@ -2033,8 +2033,8 @@ fuse_lib_rename(fuse_req_t *req_, struct fuse_in_header *hdr_) { int err; - char *oldpath; - char *newpath; + fs::relpath oldpath; + fs::relpath newpath; const char *oldname; const char *newname; node_t *wnode1; @@ -2057,12 +2057,12 @@ fuse_lib_rename(fuse_req_t *req_, if(!err) { err = f.ops.rename(&req_->ctx, - &oldpath[1], - &newpath[1]); + oldpath, + newpath); if(!err) err = rename_node(hdr_->nodeid,oldname,arg->newdir,newname); - free_path2(hdr_->nodeid,arg->newdir,wnode1,wnode2,oldpath,newpath); + free_path2(hdr_->nodeid,arg->newdir,wnode1,wnode2); } fuse_reply_err(req_,err); @@ -2092,8 +2092,8 @@ fuse_lib_link(fuse_req_t *req_, struct fuse_in_header *hdr_) { int rv; - char *oldpath; - char *newpath; + fs::relpath oldpath; + fs::relpath newpath; const char *newname; struct fuse_link_in *arg; struct fuse_entry_param e = {}; @@ -2112,13 +2112,13 @@ fuse_lib_link(fuse_req_t *req_, if(!rv) { rv = f.ops.link(&req_->ctx, - &oldpath[1], - &newpath[1], + oldpath, + newpath, &e.attr, &e.timeout); if(rv == 0) rv = set_path_info(hdr_->nodeid,newname,&e); - free_path2(arg->oldnodeid,hdr_->nodeid,NULL,NULL,oldpath,newpath); + free_path2(arg->oldnodeid,hdr_->nodeid,NULL,NULL); } reply_entry(req_,&e,rv); @@ -2153,7 +2153,7 @@ fuse_lib_create(fuse_req_t *req_, struct fuse_in_header *hdr_) { int err; - char *fusepath; + fs::relpath fusepath; const char *name; uint64_t new_nodeid; fuse_file_info_t ffi = {}; @@ -2190,7 +2190,7 @@ fuse_lib_create(fuse_req_t *req_, if(!err) { err = f.ops.create(&req_->ctx, - &fusepath[1], + fusepath, arg->mode, &ffi); if(!err) @@ -2211,8 +2211,8 @@ fuse_lib_create(fuse_req_t *req_, if(!err) { - free_path_and_inc_open(hdr_->nodeid,fusepath,e.ino,true); - fusepath = NULL; + free_path_and_inc_open(hdr_->nodeid,e.ino,true); + fusepath.clear(); if(fuse_reply_create(req_,&e,&ffi) == -ENOENT) { @@ -2228,7 +2228,7 @@ fuse_lib_create(fuse_req_t *req_, fuse_reply_err(req_,err); } - free_path(hdr_->nodeid,fusepath); + free_path(hdr_->nodeid,&fusepath); } static @@ -2288,7 +2288,7 @@ fuse_lib_open(fuse_req_t *req_, struct fuse_in_header *hdr_) { int err; - char *fusepath; + fs::relpath fusepath; fuse_file_info_t ffi = {}; struct fuse_open_in *arg; @@ -2300,7 +2300,7 @@ fuse_lib_open(fuse_req_t *req_, if(!err) { err = f.ops.open(&req_->ctx, - &fusepath[1], + fusepath, &ffi); if(!err) { @@ -2311,8 +2311,8 @@ fuse_lib_open(fuse_req_t *req_, if(!err) { - free_path_and_inc_open(hdr_->nodeid,fusepath,hdr_->nodeid,false); - fusepath = NULL; + free_path_and_inc_open(hdr_->nodeid,hdr_->nodeid,false); + fusepath.clear(); /* The open syscall was interrupted,so it must be cancelled */ if(fuse_reply_open(req_,&ffi) == -ENOENT) @@ -2323,7 +2323,7 @@ fuse_lib_open(fuse_req_t *req_, fuse_reply_err(req_,err); } - free_path(hdr_->nodeid,fusepath); + free_path(hdr_->nodeid,&fusepath); } static @@ -2443,7 +2443,7 @@ fuse_lib_opendir(fuse_req_t *req_, struct fuse_in_header *hdr_) { int err; - char *fusepath; + fs::relpath fusepath; struct fuse_dh *dh; fuse_file_info_t llffi = {}; fuse_file_info_t ffi = {}; @@ -2476,7 +2476,7 @@ fuse_lib_opendir(fuse_req_t *req_, if(!err) { err = f.ops.opendir(&req_->ctx, - &fusepath[1], + fusepath, &ffi); dh->fh = ffi.fh; llffi.keep_cache = ffi.keep_cache; @@ -2504,7 +2504,7 @@ fuse_lib_opendir(fuse_req_t *req_, free(dh); } - free_path(hdr_->nodeid,fusepath); + free_path(hdr_->nodeid,&fusepath); } static @@ -2705,19 +2705,18 @@ fuse_lib_statfs(fuse_req_t *req_, struct fuse_in_header *hdr_) { int err = 0; - char *fusepath; + fs::relpath fusepath; struct statvfs buf = {}; - fusepath = NULL; if(hdr_->nodeid) err = get_path(hdr_->nodeid,&fusepath); if(!err) { err = f.ops.statfs(&req_->ctx, - fusepath ? &fusepath[1] : "", + fusepath, &buf); - free_path(hdr_->nodeid,fusepath); + free_path(hdr_->nodeid,&fusepath); } if(!err) @@ -2732,7 +2731,7 @@ fuse_lib_setxattr(fuse_req_t *req_, struct fuse_in_header *hdr_) { int err; - char *fusepath; + fs::relpath fusepath; const char *name; const char *value; struct fuse_setxattr_in *arg; @@ -2750,12 +2749,12 @@ fuse_lib_setxattr(fuse_req_t *req_, if(!err) { err = f.ops.setxattr(&req_->ctx, - &fusepath[1], + fusepath, name, value, arg->size, arg->flags); - free_path(hdr_->nodeid,fusepath); + free_path(hdr_->nodeid,&fusepath); } fuse_reply_err(req_,err); @@ -2770,18 +2769,18 @@ common_getxattr(fuse_req_t *req_, size_t size) { int err; - char *fusepath; + fs::relpath fusepath; err = get_path(ino,&fusepath); if(!err) { err = f.ops.getxattr(&req_->ctx, - &fusepath[1], + fusepath, name, value, size); - free_path(ino,fusepath); + free_path(ino,&fusepath); } return err; @@ -2833,16 +2832,16 @@ common_listxattr(fuse_req_t *req_, size_t size) { int err; - char *fusepath; + fs::relpath fusepath; err = get_path(ino,&fusepath); if(!err) { err = f.ops.listxattr(&req_->ctx, - &fusepath[1], + fusepath, list, size); - free_path(ino,fusepath); + free_path(ino,&fusepath); } return err; @@ -2890,7 +2889,7 @@ fuse_lib_removexattr(fuse_req_t *req_, const struct fuse_in_header *hdr_) { int err; - char *fusepath; + fs::relpath fusepath; const char *name; name = (const char*)fuse_hdr_arg(hdr_); @@ -2899,9 +2898,9 @@ fuse_lib_removexattr(fuse_req_t *req_, if(!err) { err = f.ops.removexattr(&req_->ctx, - &fusepath[1], + fusepath, name); - free_path(hdr_->nodeid,fusepath); + free_path(hdr_->nodeid,&fusepath); } fuse_reply_err(req_,err); @@ -2981,7 +2980,7 @@ fuse_lib_tmpfile(fuse_req_t *req_, const struct fuse_in_header *hdr_) { int err; - char *fusepath; + fs::relpath fusepath; const char *name; uint64_t new_nodeid; fuse_file_info_t ffi = {}; @@ -3018,7 +3017,7 @@ fuse_lib_tmpfile(fuse_req_t *req_, if(!err) { err = f.ops.tmpfile(&req_->ctx, - &fusepath[1], + fusepath, arg->mode, &ffi); if(!err) @@ -3041,8 +3040,8 @@ fuse_lib_tmpfile(fuse_req_t *req_, if(!err) { - free_path_and_inc_open(hdr_->nodeid,fusepath,e.ino,true); - fusepath = NULL; + free_path_and_inc_open(hdr_->nodeid,e.ino,true); + fusepath.clear(); if(fuse_reply_create(req_,&e,&ffi) == -ENOENT) { @@ -3058,7 +3057,7 @@ fuse_lib_tmpfile(fuse_req_t *req_, fuse_reply_err(req_,err); } - free_path(hdr_->nodeid,fusepath); + free_path(hdr_->nodeid,&fusepath); } static @@ -3134,7 +3133,7 @@ fuse_lib_bmap(fuse_req_t *req_, const struct fuse_in_header *hdr_) { int err; - char *fusepath; + fs::relpath fusepath; uint64_t block; const struct fuse_bmap_in *arg; @@ -3145,10 +3144,10 @@ fuse_lib_bmap(fuse_req_t *req_, if(!err) { err = f.ops.bmap(&req_->ctx, - &fusepath[1], + fusepath, arg->blocksize, &block); - free_path(hdr_->nodeid,fusepath); + free_path(hdr_->nodeid,&fusepath); } if(!err)