diff --git a/README.md b/README.md index c98921fa..cd6bc957 100644 --- a/README.md +++ b/README.md @@ -93,14 +93,19 @@ make XATTR_AVAILABLE=0 - to build program without xattrs functionality (auto dis /.mergerfs ``` -There is a pseudo file available at the mountpoint which allows for the runtime modification of certain mergerfs options. The file will not show up in readdirs but can be stat'ed and manipulated via [{list,get,set}xattrs](http://linux.die.net/man/2/listxattr) calls. +There is a pseudo file available at the mountpoint which allows for the runtime modification of certain mergerfs options. The file will not show up in readdirs but can be stat'ed and manipulated via [{list,get,set}xattrs](http://linux.die.net/man/2/listxattr) calls. Even if xattrs are disabled the [{list,get,set}xattrs](http://linux.die.net/man/2/listxattr) calls will still work. -The keys are **user.mergerfs.action**, **user.mergerfs.create**, and **user.mergerfs.search**. +The keys are: +* user.mergerfs.srcmounts +* user.mergerfs.action +* user.mergerfs.create +* user.mergerfs.search ``` [trapexit:/tmp/mount] $ xattr -l .mergerfs +user.mergerfs.srcmounts: /tmp/a:/tmp/b user.mergerfs.action: ff user.mergerfs.create: epmfs user.mergerfs.search: ff @@ -111,9 +116,34 @@ ff [trapexit:/tmp/mount] $ xattr -w user.mergerfs.action ffwp .mergerfs [trapexit:/tmp/mount] $ xattr -p user.mergerfs.action .mergerfs ffwp + +[trapexit:/tmp/mount] $ xattr -w user.mergerfs.srcmounts +/tmp/c .mergerfs +[trapexit:/tmp/mount] $ xattr -p user.mergerfs.srcmounts .mergerfs +/tmp/a:/tmp/b:/tmp/c + +[trapexit:/tmp/mount] $ xattr -w user.mergerfs.srcmounts =/tmp/c .mergerfs +[trapexit:/tmp/mount] $ xattr -p user.mergerfs.srcmounts .mergerfs +/tmp/c + +[trapexit:/tmp/mount] $ xattr -w user.mergerfs.srcmounts '+[list] | append | +| -[list] | remove all values provided | +| -< | remove first in list | +| -> | remove last in list | +| =[list] | set | +| [list] | set | + +#### mergerfs file xattrs #### While they won't show up when using [listxattr](http://linux.die.net/man/2/listxattr) mergerfs offers a number of special xattrs to query information about the files served. To access the values you will need to issue a [getxattr](http://linux.die.net/man/2/getxattr) for one of the following: @@ -124,5 +154,7 @@ While they won't show up when using [listxattr](http://linux.die.net/man/2/listx [trapexit:/tmp/mount] $ ls A B C [trapexit:/tmp/mount] $ xattr -p user.mergerfs.fullpath A -/mnt/full/path/to/A +/mnt/a/full/path/to/A +[trapexit:/tmp/mount] $ xattr -p user.mergerfs.basepath A +/mnt/a ``` diff --git a/src/access.cpp b/src/access.cpp index eaf71ab4..a727654c 100644 --- a/src/access.cpp +++ b/src/access.cpp @@ -37,7 +37,7 @@ #include "ugid.hpp" #include "fs.hpp" #include "config.hpp" -#include "assert.hpp" +#include "rwlock.hpp" using std::string; using std::vector; @@ -73,6 +73,7 @@ namespace mergerfs const struct fuse_context *fc = fuse_get_context(); const config::Config &config = config::get(); const ugid::SetResetGuard ugid(fc->uid,fc->gid); + const rwlock::ReadGuard readlock(&config.srcmountslock); return _access(*config.search, config.srcmounts, diff --git a/src/chmod.cpp b/src/chmod.cpp index 39e1a04c..e523aaf8 100644 --- a/src/chmod.cpp +++ b/src/chmod.cpp @@ -31,7 +31,7 @@ #include "ugid.hpp" #include "fs.hpp" #include "config.hpp" -#include "assert.hpp" +#include "rwlock.hpp" using std::string; using std::vector; @@ -76,6 +76,7 @@ namespace mergerfs const struct fuse_context *fc = fuse_get_context(); const ugid::SetResetGuard ugid(fc->uid,fc->gid); const config::Config &config = config::get(); + const rwlock::ReadGuard readlock(&config.srcmountslock); return _chmod(*config.action, config.srcmounts, diff --git a/src/chown.cpp b/src/chown.cpp index abeca738..638c231b 100644 --- a/src/chown.cpp +++ b/src/chown.cpp @@ -32,7 +32,7 @@ #include "ugid.hpp" #include "fs.hpp" #include "config.hpp" -#include "assert.hpp" +#include "rwlock.hpp" using std::string; using std::vector; @@ -79,6 +79,7 @@ namespace mergerfs const struct fuse_context *fc = fuse_get_context(); const config::Config &config = config::get(); const ugid::SetResetGuard ugid(fc->uid,fc->gid); + const rwlock::ReadGuard readlock(&config.srcmountslock); return _chown(*config.action, config.srcmounts, diff --git a/src/config.cpp b/src/config.cpp index a171bedf..ce7695ec 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -24,23 +24,34 @@ #include -#include +#include +#include #include #include #include "config.hpp" +#include "rwlock.hpp" +#include "fs.hpp" + +using std::string; +using std::vector; namespace mergerfs { namespace config { Config::Config() - : action(policies[0]), - create(policies[1]), - search(policies[2]), + : destmount(), + srcmounts(), + srcmountslock(), + action(policies[Category::Enum::action]), + create(policies[Category::Enum::create]), + search(policies[Category::Enum::search]), controlfile("/.mergerfs") { + pthread_rwlock_init(&srcmountslock,NULL); + action = &Policy::ff; create = &Policy::epmfs; search = &Policy::ff; diff --git a/src/config.hpp b/src/config.hpp index c1622c01..7646b46c 100644 --- a/src/config.hpp +++ b/src/config.hpp @@ -45,6 +45,7 @@ namespace mergerfs public: std::string destmount; std::vector srcmounts; + mutable pthread_rwlock_t srcmountslock; const Policy *policies[Category::Enum::END]; const Policy *&action; diff --git a/src/create.cpp b/src/create.cpp index b20ca269..9623ed4f 100644 --- a/src/create.cpp +++ b/src/create.cpp @@ -36,7 +36,7 @@ #include "ugid.hpp" #include "fileinfo.hpp" #include "fs.hpp" -#include "assert.hpp" +#include "rwlock.hpp" using std::string; using std::vector; @@ -94,6 +94,7 @@ namespace mergerfs const struct fuse_context *fc = fuse_get_context(); const config::Config &config = config::get(); const ugid::SetResetGuard ugid(fc->uid,fc->gid); + const rwlock::ReadGuard readlock(&config.srcmountslock); return _create(*config.search, *config.create, diff --git a/src/fs.cpp b/src/fs.cpp index 6bf69196..63b1a183 100644 --- a/src/fs.cpp +++ b/src/fs.cpp @@ -38,30 +38,18 @@ #include #include #include +#include +#include #include "fs.hpp" #include "xattr.hpp" +#include "str.hpp" using std::string; using std::vector; using std::map; using std::istringstream; -template -Container& -split(Container &result, - const typename Container::value_type &s, - typename Container::value_type::value_type delimiter) -{ - string str; - istringstream ss(s); - - while(std::getline(ss,str,delimiter)) - result.push_back(str); - - return result; -} - template Iter random_element(Iter begin, @@ -208,7 +196,7 @@ namespace fs if(rv != -1) { string tmp(attrs.begin(),attrs.end()); - split(attrvector,tmp,'\0'); + str::split(attrvector,tmp,'\0'); } return rv; @@ -453,6 +441,52 @@ namespace fs return (errno = 0); } + void + glob(const vector &patterns, + vector &strs) + { + int flags; + glob_t gbuf; + + flags = 0; + for(size_t i = 0; i < patterns.size(); i++) + { + glob(patterns[i].c_str(),flags,NULL,&gbuf); + flags = GLOB_APPEND; + } + + for(size_t i = 0; i < gbuf.gl_pathc; ++i) + strs.push_back(gbuf.gl_pathv[i]); + + globfree(&gbuf); + } + + void + erase_fnmatches(const vector &patterns, + vector &strs) + { + vector::iterator si; + vector::const_iterator pi; + + si = strs.begin(); + while(si != strs.end()) + { + int match = FNM_NOMATCH; + + for(pi = patterns.begin(); + pi != patterns.end() && match != 0; + ++pi) + { + match = fnmatch(pi->c_str(),si->c_str(),0); + } + + if(match == 0) + si = strs.erase(si); + else + ++si; + } + } + namespace find { void diff --git a/src/fs.hpp b/src/fs.hpp index a4385e3b..4dc4a6b3 100644 --- a/src/fs.hpp +++ b/src/fs.hpp @@ -104,6 +104,12 @@ namespace fs int copyattr(const string from, const string to); + void glob(const vector &patterns, + vector &strs); + + void erase_fnmatches(const vector &patterns, + vector &strs); + namespace find { void invalid(const vector &basepaths, diff --git a/src/getattr.cpp b/src/getattr.cpp index 8a0ab16b..648f79c8 100644 --- a/src/getattr.cpp +++ b/src/getattr.cpp @@ -35,7 +35,7 @@ #include "config.hpp" #include "fs.hpp" #include "ugid.hpp" -#include "assert.hpp" +#include "rwlock.hpp" using std::string; using std::vector; @@ -90,13 +90,15 @@ namespace mergerfs getattr(const char *fusepath, struct stat *st) { - const struct fuse_context *fc = fuse_get_context(); - const config::Config &config = config::get(); - const ugid::SetResetGuard ugid(fc->uid,fc->gid); + const config::Config &config = config::get(); if(fusepath == config.controlfile) return _getattr_controlfile(*st); + const struct fuse_context *fc = fuse_get_context(); + const ugid::SetResetGuard ugid(fc->uid,fc->gid); + const rwlock::ReadGuard readlock(&config.srcmountslock); + return _getattr(*config.search, config.srcmounts, fusepath, diff --git a/src/getxattr.cpp b/src/getxattr.cpp index fd5ce766..ccaee00b 100644 --- a/src/getxattr.cpp +++ b/src/getxattr.cpp @@ -34,8 +34,9 @@ #include "config.hpp" #include "fs.hpp" #include "ugid.hpp" -#include "assert.hpp" +#include "rwlock.hpp" #include "xattr.hpp" +#include "str.hpp" using std::string; using std::vector; @@ -57,6 +58,8 @@ _getxattr_controlfile(const Config &config, attrvalue = (std::string)*config.create; else if(attrname == "user.mergerfs.search") attrvalue = (std::string)*config.search; + else if(attrname == "user.mergerfs.srcmounts") + attrvalue = str::join(config.srcmounts,':'); if(attrvalue.empty()) return -ENOATTR; @@ -133,9 +136,7 @@ namespace mergerfs char *buf, size_t count) { - const struct fuse_context *fc = fuse_get_context(); - const config::Config &config = config::get(); - const ugid::SetResetGuard ugid(fc->uid,fc->gid); + const config::Config &config = config::get(); if(fusepath == config.controlfile) return _getxattr_controlfile(config, @@ -143,6 +144,10 @@ namespace mergerfs buf, count); + const struct fuse_context *fc = fuse_get_context(); + const ugid::SetResetGuard ugid(fc->uid,fc->gid); + const rwlock::ReadGuard readlock(&config.srcmountslock); + return _getxattr(*config.search, config.srcmounts, fusepath, diff --git a/src/link.cpp b/src/link.cpp index 8189c1b8..d41c963b 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -33,7 +33,7 @@ #include "config.hpp" #include "fs.hpp" #include "ugid.hpp" -#include "assert.hpp" +#include "rwlock.hpp" using std::string; using std::vector; @@ -97,6 +97,7 @@ namespace mergerfs const struct fuse_context *fc = fuse_get_context(); const config::Config &config = config::get(); const ugid::SetResetGuard ugid(fc->uid,fc->gid); + const rwlock::ReadGuard readlock(&config.srcmountslock); return _link(*config.action, config.srcmounts, diff --git a/src/listxattr.cpp b/src/listxattr.cpp index 469d2753..463c8f27 100644 --- a/src/listxattr.cpp +++ b/src/listxattr.cpp @@ -35,7 +35,7 @@ #include "category.hpp" #include "fs.hpp" #include "ugid.hpp" -#include "assert.hpp" +#include "rwlock.hpp" #include "xattr.hpp" using std::string; @@ -47,23 +47,22 @@ int _listxattr_controlfile(char *list, const size_t size) { - size_t xattrssize; - string xattrs; - - for(int i = Category::Enum::BEGIN; i < Category::Enum::END; ++i) - xattrs += "user.mergerfs." + (string)Category::categories[i] + '\0'; - - xattrssize = xattrs.size(); + const char xattrs[] = + "user.mergerfs.srcmounts\0" + "user.mergerfs.action\0" + "user.mergerfs.create\0" + "user.mergerfs.search" + ; if(size == 0) - return xattrssize; + return sizeof(xattrs); - if(size < xattrssize) + if(size < sizeof(xattrs)) return -ERANGE; - memcpy(list,xattrs.data(),xattrssize); + memcpy(list,xattrs,sizeof(xattrs)); - return xattrssize; + return sizeof(xattrs); } static @@ -99,14 +98,16 @@ namespace mergerfs char *list, size_t size) { - const struct fuse_context *fc = fuse_get_context(); - const config::Config &config = config::get(); - const ugid::SetResetGuard ugid(fc->uid,fc->gid); + const config::Config &config = config::get(); if(fusepath == config.controlfile) return _listxattr_controlfile(list, size); + const struct fuse_context *fc = fuse_get_context(); + const ugid::SetResetGuard ugid(fc->uid,fc->gid); + const rwlock::ReadGuard readlock(&config.srcmountslock); + return _listxattr(*config.search, config.srcmounts, fusepath, diff --git a/src/mkdir.cpp b/src/mkdir.cpp index e583a7a6..065b9e00 100644 --- a/src/mkdir.cpp +++ b/src/mkdir.cpp @@ -35,7 +35,7 @@ #include "fileinfo.hpp" #include "fs.hpp" #include "config.hpp" -#include "assert.hpp" +#include "rwlock.hpp" using std::string; using std::vector; @@ -84,6 +84,7 @@ namespace mergerfs const struct fuse_context *fc = fuse_get_context(); const config::Config &config = config::get(); const ugid::SetResetGuard ugid(fc->uid,fc->gid); + const rwlock::ReadGuard readlock(&config.srcmountslock); return _mkdir(*config.search, *config.create, diff --git a/src/mknod.cpp b/src/mknod.cpp index b8888f05..ccca0ba2 100644 --- a/src/mknod.cpp +++ b/src/mknod.cpp @@ -36,7 +36,7 @@ #include "ugid.hpp" #include "fs.hpp" #include "config.hpp" -#include "assert.hpp" +#include "rwlock.hpp" using std::string; using std::vector; @@ -87,6 +87,7 @@ namespace mergerfs const struct fuse_context *fc = fuse_get_context(); const config::Config &config = config::get(); const ugid::SetResetGuard ugid(fc->uid,fc->gid); + const rwlock::ReadGuard readlock(&config.srcmountslock); return _mknod(*config.search, *config.create, diff --git a/src/open.cpp b/src/open.cpp index 58c5b21f..3bd4ec92 100644 --- a/src/open.cpp +++ b/src/open.cpp @@ -35,7 +35,7 @@ #include "fileinfo.hpp" #include "fs.hpp" #include "config.hpp" -#include "assert.hpp" +#include "rwlock.hpp" using std::string; using std::vector; @@ -75,7 +75,8 @@ namespace mergerfs { const struct fuse_context *fc = fuse_get_context(); const config::Config &config = config::get(); - const ugid::SetResetGuard ugid(fc->uid,fc->gid);; + const ugid::SetResetGuard ugid(fc->uid,fc->gid); + const rwlock::ReadGuard readlock(&config.srcmountslock); return _open(*config.search, config.srcmounts, diff --git a/src/option_parser.cpp b/src/option_parser.cpp index 5be03890..76d6865b 100644 --- a/src/option_parser.cpp +++ b/src/option_parser.cpp @@ -27,13 +27,13 @@ #include #include #include -#include #include #include #include #include +#include "str.hpp" #include "config.hpp" #include "policy.hpp" @@ -41,25 +41,6 @@ using std::string; using std::vector; using namespace mergerfs; -template -Container& -split(Container &result, - const typename Container::value_type &s, - typename Container::value_type::value_type delimiter) -{ - std::istringstream ss(s); - while(!ss.eof()) - { - typename Container::value_type field; - std::getline(ss,field,delimiter); - if(field.empty()) - continue; - result.push_back(field); - } - - return result; -} - static int process_opt(config::Config &config, @@ -68,7 +49,7 @@ process_opt(config::Config &config, int rv = 0; std::vector argvalue; - split(argvalue,arg,'='); + str::split(argvalue,arg,'='); switch(argvalue.size()) { case 2: @@ -91,26 +72,27 @@ process_opt(config::Config &config, } static -void +int process_srcmounts(const char *arg, config::Config &config) { - int flags; - glob_t gbuf; vector paths; - flags = 0; - split(paths,arg,':'); - for(size_t i = 0; i < paths.size(); i++) - { - glob(paths[i].c_str(),flags,NULL,&gbuf); - flags = GLOB_APPEND; - } + str::split(paths,arg,':'); + + fs::glob(paths,config.srcmounts); + + return 0; +} - for(size_t i = 0; i < gbuf.gl_pathc; ++i) - config.srcmounts.push_back(gbuf.gl_pathv[i]); +static +int +process_destmounts(const char *arg, + config::Config &config) +{ + config.destmount = arg; - globfree(&gbuf); + return 1; } static @@ -130,10 +112,9 @@ option_processor(void *data, break; case FUSE_OPT_KEY_NONOPT: - if(config.srcmounts.empty()) - process_srcmounts(arg,config); - else - rv = (config.destmount = arg,1); + rv = config.srcmounts.empty() ? + process_srcmounts(arg,config) : + process_destmounts(arg,config); break; default: @@ -152,10 +133,7 @@ set_fsname(struct fuse_args &args, { std::string fsname; - fsname = "-ofsname="; - fsname += config.srcmounts[0]; - for(size_t i = 1; i < config.srcmounts.size(); i++) - fsname += ';' + config.srcmounts[i]; + fsname = "-ofsname=" + str::join(config.srcmounts,':'); fuse_opt_insert_arg(&args,1,fsname.c_str()); } diff --git a/src/read.cpp b/src/read.cpp index f655aa37..351f92c1 100644 --- a/src/read.cpp +++ b/src/read.cpp @@ -29,7 +29,6 @@ #include #include -#include #include "fileinfo.hpp" diff --git a/src/readdir.cpp b/src/readdir.cpp index a88ffc1e..f84dc54a 100644 --- a/src/readdir.cpp +++ b/src/readdir.cpp @@ -35,6 +35,7 @@ #include "config.hpp" #include "ugid.hpp" #include "fs.hpp" +#include "rwlock.hpp" using std::string; using std::vector; @@ -125,6 +126,7 @@ namespace mergerfs const struct fuse_context *fc = fuse_get_context(); const config::Config &config = config::get(); const ugid::SetResetGuard ugid(fc->uid,fc->gid); + const rwlock::ReadGuard readlock(&config.srcmountslock); return _readdir(config.srcmounts, fusepath, diff --git a/src/readlink.cpp b/src/readlink.cpp index 69714fa7..cfc1d646 100644 --- a/src/readlink.cpp +++ b/src/readlink.cpp @@ -33,7 +33,7 @@ #include "config.hpp" #include "ugid.hpp" #include "fs.hpp" -#include "assert.hpp" +#include "rwlock.hpp" using std::string; using std::vector; @@ -75,6 +75,7 @@ namespace mergerfs const struct fuse_context *fc = fuse_get_context(); const config::Config &config = config::get(); const ugid::SetResetGuard ugid(fc->uid,fc->gid); + const rwlock::ReadGuard readlock(&config.srcmountslock); return _readlink(*config.search, config.srcmounts, diff --git a/src/release.cpp b/src/release.cpp index 940c7f8c..26253c35 100644 --- a/src/release.cpp +++ b/src/release.cpp @@ -54,9 +54,9 @@ namespace mergerfs { int release(const char *fusepath, - struct fuse_file_info *fi) + struct fuse_file_info *ffi) { - return _release(fi->fh); + return _release(ffi->fh); } } } diff --git a/src/removexattr.cpp b/src/removexattr.cpp index 0dd9ec7c..b5d3ce40 100644 --- a/src/removexattr.cpp +++ b/src/removexattr.cpp @@ -33,7 +33,7 @@ #include "config.hpp" #include "ugid.hpp" #include "fs.hpp" -#include "assert.hpp" +#include "rwlock.hpp" #include "xattr.hpp" using std::string; @@ -79,13 +79,15 @@ namespace mergerfs removexattr(const char *fusepath, const char *attrname) { - const struct fuse_context *fc = fuse_get_context(); - const config::Config &config = config::get(); - const ugid::SetResetGuard ugid(fc->uid,fc->gid); + const config::Config &config = config::get(); if(fusepath == config.controlfile) return -ENOTSUP; + const struct fuse_context *fc = fuse_get_context(); + const ugid::SetResetGuard ugid(fc->uid,fc->gid); + const rwlock::ReadGuard readlock(&config.srcmountslock); + return _removexattr(*config.action, config.srcmounts, fusepath, diff --git a/src/rename.cpp b/src/rename.cpp index 16c14813..a8aaee4d 100644 --- a/src/rename.cpp +++ b/src/rename.cpp @@ -34,6 +34,7 @@ #include "ugid.hpp" #include "fs.hpp" #include "config.hpp" +#include "rwlock.hpp" using std::string; using std::vector; @@ -73,6 +74,7 @@ namespace mergerfs const struct fuse_context *fc = fuse_get_context(); const config::Config &config = config::get(); const ugid::SetResetGuard ugid(fc->uid,fc->gid); + const rwlock::ReadGuard readlock(&config.srcmountslock); return _rename(*config.search, config.srcmounts, diff --git a/src/rmdir.cpp b/src/rmdir.cpp index 0311194b..dff8603f 100644 --- a/src/rmdir.cpp +++ b/src/rmdir.cpp @@ -32,6 +32,7 @@ #include "ugid.hpp" #include "fs.hpp" #include "config.hpp" +#include "rwlock.hpp" using std::string; using std::vector; @@ -74,6 +75,7 @@ namespace mergerfs const struct fuse_context *fc = fuse_get_context(); const config::Config &config = config::get(); const ugid::SetResetGuard ugid(fc->uid,fc->gid); + const rwlock::ReadGuard readguard(&config.srcmountslock); return _rmdir(*config.action, config.srcmounts, diff --git a/src/rwlock.hpp b/src/rwlock.hpp new file mode 100644 index 00000000..082d7d41 --- /dev/null +++ b/src/rwlock.hpp @@ -0,0 +1,71 @@ +/* + The MIT License (MIT) + + Copyright (c) 2014 Antonio SJ Musumeci + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include + +namespace mergerfs +{ + namespace rwlock + { + class ReadGuard + { + public: + ReadGuard(pthread_rwlock_t *lock) + { + _lock = lock; + pthread_rwlock_rdlock(_lock); + } + + ~ReadGuard() + { + pthread_rwlock_unlock(_lock); + } + + pthread_rwlock_t *_lock; + + private: + ReadGuard(); + }; + + class WriteGuard + { + public: + WriteGuard(pthread_rwlock_t *lock) + { + _lock = lock; + pthread_rwlock_wrlock(_lock); + } + + ~WriteGuard() + { + pthread_rwlock_unlock(_lock); + } + + pthread_rwlock_t *_lock; + + private: + WriteGuard(); + }; + } +} diff --git a/src/setxattr.cpp b/src/setxattr.cpp index 3e91baeb..04a63edf 100644 --- a/src/setxattr.cpp +++ b/src/setxattr.cpp @@ -34,8 +34,9 @@ #include "config.hpp" #include "fs.hpp" #include "ugid.hpp" -#include "assert.hpp" +#include "rwlock.hpp" #include "xattr.hpp" +#include "str.hpp" using std::string; using std::vector; @@ -43,45 +44,135 @@ using mergerfs::Policy; using mergerfs::Category; using namespace mergerfs; -template -Container& -split(Container &result, - const typename Container::value_type &s, - typename Container::value_type::value_type delimiter) +static +int +_add_srcmounts(vector &srcmounts, + pthread_rwlock_t &srcmountslock, + const string destmount, + const string values, + vector::iterator pos) { - std::string str; - std::istringstream ss(s); + vector patterns; + vector additions; + + str::split(patterns,values,':'); + fs::glob(patterns,additions); + + if(!additions.empty()) + { + const rwlock::WriteGuard wrg(&srcmountslock); - while(std::getline(ss,str,delimiter)) - result.push_back(str); + srcmounts.insert(pos, + additions.begin(), + additions.end()); + } - return result; + return 0; } static int -_setxattr_controlfile(config::Config &config, - const string attrname, - const string attrval, - const size_t attrvalsize, - const int flags) +_erase_srcmounts(vector &srcmounts, + pthread_rwlock_t srcmountslock, + const string &values) { - const Category *cat; - const Policy *policy; - vector nameparts; + if(srcmounts.empty()) + return 0; - split(nameparts,attrname,'.'); + vector patterns; - if(nameparts.size() != 3) - return -EINVAL; - - if(nameparts[0] != "user") - return -ENOATTR; - - if(nameparts[1] != "mergerfs") - return -ENOATTR; + str::split(patterns,values,':'); + + const rwlock::WriteGuard wrg(&srcmountslock); + + fs::erase_fnmatches(patterns,srcmounts); + + return 0; +} + +static +int +_replace_srcmounts(vector &srcmounts, + pthread_rwlock_t srcmountslock, + const string destmount, + const string values) +{ + vector patterns; + vector newmounts; + + str::split(patterns,values,':'); + fs::glob(patterns,newmounts); + + { + const rwlock::WriteGuard wrg(&srcmountslock); + + srcmounts.swap(newmounts); + } + + return 0; +} + +static +void +_split_attrval(const string attrval, + string &instruction, + string &values) +{ + size_t offset; - cat = Category::find(nameparts[2]); + offset = attrval.find_first_of('/'); + instruction = attrval.substr(0,offset); + if(offset != string::npos) + values = attrval.substr(offset); +} + +static +int +_setxattr_srcmounts(vector &srcmounts, + pthread_rwlock_t &srcmountslock, + const string destmount, + const string attrval, + const int flags) +{ + string instruction; + string values; + + if((flags & XATTR_CREATE) == XATTR_CREATE) + return -EEXIST; + + _split_attrval(attrval,instruction,values); + + if(instruction == "+") + return _add_srcmounts(srcmounts,srcmountslock,destmount,values,srcmounts.end()); + else if(instruction == "+<") + return _add_srcmounts(srcmounts,srcmountslock,destmount,values,srcmounts.begin()); + else if(instruction == "+>") + return _add_srcmounts(srcmounts,srcmountslock,destmount,values,srcmounts.end()); + else if(instruction == "-") + return _erase_srcmounts(srcmounts,srcmountslock,values); + else if(instruction == "-<") + return _erase_srcmounts(srcmounts,srcmountslock,srcmounts.front()); + else if(instruction == "->") + return _erase_srcmounts(srcmounts,srcmountslock,srcmounts.back()); + else if(instruction == "=") + return _replace_srcmounts(srcmounts,srcmountslock,destmount,values); + else if(instruction.empty()) + return _replace_srcmounts(srcmounts,srcmountslock,destmount,values); + + return -EINVAL; +} + +static +int +_setxattr_policy(const Policy *policies[], + const string attrname, + const string attrval, + const int flags) +{ + const Category *cat; + const Policy *policy; + + cat = Category::find(attrname); if(cat == Category::invalid) return -ENOATTR; @@ -92,11 +183,43 @@ _setxattr_controlfile(config::Config &config, if(policy == Policy::invalid) return -EINVAL; - config.policies[*cat] = policy; + policies[*cat] = policy; return 0; } +static +int +_setxattr_controlfile(config::Config &config, + const string attrname, + const string attrval, + const int flags) +{ + vector nameparts; + + str::split(nameparts,attrname,'.'); + + if(nameparts.size() != 3 || + nameparts[0] != "user" || + nameparts[1] != "mergerfs") + return -ENOATTR; + + if(attrval.empty()) + return -EINVAL; + + if(nameparts[2] == "srcmounts") + return _setxattr_srcmounts(config.srcmounts, + config.srcmountslock, + config.destmount, + attrval, + flags); + + return _setxattr_policy(config.policies, + nameparts[2], + attrval, + flags); +} + static int _setxattr(const fs::SearchFunc searchFunc, @@ -142,24 +265,27 @@ namespace mergerfs size_t attrvalsize, int flags) { - const struct fuse_context *fc = fuse_get_context(); - const config::Config &config = config::get(); - const ugid::SetResetGuard ugid(fc->uid,fc->gid); + const config::Config &config = config::get(); if(fusepath == config.controlfile) return _setxattr_controlfile(config::get_writable(), attrname, string(attrval,attrvalsize), - attrvalsize, flags); - return _setxattr(*config.action, - config.srcmounts, - fusepath, - attrname, - attrval, - attrvalsize, - flags); + { + const struct fuse_context *fc = fuse_get_context(); + const ugid::SetResetGuard ugid(fc->uid,fc->gid); + const rwlock::ReadGuard readlock(&config.srcmountslock); + + return _setxattr(*config.action, + config.srcmounts, + fusepath, + attrname, + attrval, + attrvalsize, + flags); + } } } } diff --git a/src/statfs.cpp b/src/statfs.cpp index 223a5428..3ae39c35 100644 --- a/src/statfs.cpp +++ b/src/statfs.cpp @@ -34,6 +34,7 @@ #include "ugid.hpp" #include "config.hpp" +#include "rwlock.hpp" using std::string; using std::vector; @@ -126,6 +127,7 @@ namespace mergerfs const struct fuse_context *fc = fuse_get_context(); const config::Config &config = config::get(); const ugid::SetResetGuard ugid(fc->uid,fc->gid); + const rwlock::ReadGuard readlock(&config.srcmountslock); return _statfs(config.srcmounts, *stat); diff --git a/src/str.cpp b/src/str.cpp new file mode 100644 index 00000000..be71882b --- /dev/null +++ b/src/str.cpp @@ -0,0 +1,60 @@ +/* + The MIT License (MIT) + + Copyright (c) 2014 Antonio SJ Musumeci + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include +#include +#include + +using std::string; +using std::vector; +using std::istringstream; + +namespace str +{ + void + split(vector &result, + const string str, + const char delimiter) + { + string part; + istringstream ss(str); + + while(std::getline(ss,part,delimiter)) + result.push_back(part); + } + + string + join(const vector &vec, + const char sep) + { + if(vec.empty()) + return string(); + + string rv = vec[0]; + for(size_t i = 1; i < vec.size(); i++) + rv += sep + vec[i]; + + return rv; + } +} diff --git a/src/str.hpp b/src/str.hpp new file mode 100644 index 00000000..aa02e2b0 --- /dev/null +++ b/src/str.hpp @@ -0,0 +1,36 @@ +/* + The MIT License (MIT) + + Copyright (c) 2014 Antonio SJ Musumeci + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include +#include + +namespace str +{ + void split(std::vector &result, + const std::string str, + const char delimiter); + + std::string join(const std::vector &vec, + const char sep); +} diff --git a/src/symlink.cpp b/src/symlink.cpp index dba44001..56fdd40a 100644 --- a/src/symlink.cpp +++ b/src/symlink.cpp @@ -32,6 +32,7 @@ #include "fs.hpp" #include "config.hpp" #include "ugid.hpp" +#include "rwlock.hpp" using std::string; using std::vector; @@ -67,6 +68,7 @@ namespace mergerfs const struct fuse_context *fc = fuse_get_context(); const config::Config &config = config::get(); const ugid::SetResetGuard ugid(fc->uid,fc->gid); + const rwlock::ReadGuard readlock(&config.srcmountslock); return _symlink(config.srcmounts, oldpath, diff --git a/src/truncate.cpp b/src/truncate.cpp index 7f6b00c1..da5a297a 100644 --- a/src/truncate.cpp +++ b/src/truncate.cpp @@ -34,6 +34,7 @@ #include "ugid.hpp" #include "fs.hpp" #include "config.hpp" +#include "rwlock.hpp" using std::string; using std::vector; @@ -77,6 +78,7 @@ namespace mergerfs const struct fuse_context *fc = fuse_get_context(); const config::Config &config = config::get(); const ugid::SetResetGuard ugid(fc->uid,fc->gid); + const rwlock::ReadGuard readlock(&config.srcmountslock); return _truncate(*config.action, config.srcmounts, diff --git a/src/unlink.cpp b/src/unlink.cpp index e9f33d96..da7460c5 100644 --- a/src/unlink.cpp +++ b/src/unlink.cpp @@ -33,6 +33,7 @@ #include "ugid.hpp" #include "fs.hpp" #include "config.hpp" +#include "rwlock.hpp" using std::string; using std::vector; @@ -74,6 +75,7 @@ namespace mergerfs const struct fuse_context *fc = fuse_get_context(); const config::Config &config = config::get(); const ugid::SetResetGuard ugid(fc->uid,fc->gid); + const rwlock::ReadGuard readlock(&config.srcmountslock); return _unlink(*config.action, config.srcmounts, diff --git a/src/utimens.cpp b/src/utimens.cpp index 46f554e7..356b5f20 100644 --- a/src/utimens.cpp +++ b/src/utimens.cpp @@ -34,6 +34,7 @@ #include "ugid.hpp" #include "fs.hpp" #include "config.hpp" +#include "rwlock.hpp" using std::string; using std::vector; @@ -77,6 +78,7 @@ namespace mergerfs const struct fuse_context *fc = fuse_get_context(); const config::Config &config = config::get(); const ugid::SetResetGuard ugid(fc->uid,fc->gid); + const rwlock::ReadGuard readlock(&config.srcmountslock); return _utimens(*config.action, config.srcmounts, diff --git a/src/write.cpp b/src/write.cpp index e979d2fc..a6e8d9a3 100644 --- a/src/write.cpp +++ b/src/write.cpp @@ -52,9 +52,9 @@ namespace mergerfs const char *buf, size_t count, off_t offset, - struct fuse_file_info *fi) + struct fuse_file_info *ffi) { - return _write(((FileInfo*)fi->fh)->fd, + return _write(((FileInfo*)ffi->fh)->fd, buf, count, offset);