diff --git a/README.md b/README.md index d968191e..f3a72d2c 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ mergerfs - another FUSE union filesystem # SYNOPSIS -mergerfs -ocreate=epmfs,search=ff <srcpoints> <mountpoint> +mergerfs -o<options> <srcpoints> <mountpoint> # DESCRIPTION @@ -20,10 +20,11 @@ Why create mergerfs when those exist? mhddfs isn't really maintained or flexible ###options### -| Option | Default | -|--------|--------| -| search | ff | -| create | epmfs | +All [FUSE](http://fuse.sourceforge.net) functions which have a category (see below) are option keys. The syntax being `<func>=<policy>`. + +To set all function policies in a category use `category.<category>=<policy>` such as `category.create=mfs`. + +They are evaluated in the order listed so if the options are `rmdir=rand,category.action=ff` the `action` category setting will override the `rmdir` setting. ###srcpoints### @@ -46,14 +47,15 @@ In /etc/fstab it'd look like the following: # POLICIES -Filesystem calls are broken up into 2 categories: search and create. There are also some calls which have no policy attached due to state being kept between calls. These categories can be assigned a policy which dictates how [mergerfs](http://github.com/trapexit/mergerfs) behaves. Any policy can be assigned to a category though some aren't terribly practical. For instance: rand (Random) may be useful for **create** but could lead to very odd behavior if used for **search**. +Filesystem calls are broken up into 3 categories: action, create, search. There are also some calls which have no policy attached due to state being kept between calls. These categories can be assigned a policy which dictates how [mergerfs](http://github.com/trapexit/mergerfs) behaves. Any policy can be assigned to a category though some aren't terribly practical. For instance: rand (Random) may be useful for **create** but could lead to very odd behavior if used for **search**. #### Functional classifications #### | Class | FUSE calls | |-------|------------| -| search | access, getattr, getxattr, listxattr, open, readlink, chmod, link, removexattr, rmdir, setxattr, truncate, unlink, utimens | +| action | chmod, chown, link, removexattr, rename, rmdir, setxattr, truncate, unlink, utimens | +| search | access, getattr, getxattr, listxattr, open, readlink, symlink | | create | create, mkdir, mknod | -| none | fallocate, fgetattr, fsync, ftruncate, ioctl, read, readdir, rename, statfs, symlink, write, release | +| N/A | fallocate, fgetattr, fsync, ftruncate, ioctl, read, readdir, statfs, symlink, write, release | #### Policy descriptions #### | Policy | Description | @@ -100,22 +102,66 @@ There is a pseudo file available at the mountpoint which allows for the runtime Even if xattrs are disabled the [{list,get,set}xattrs](http://linux.die.net/man/2/listxattr) calls will still work. -The keys are: +##### Keys ##### * user.mergerfs.srcmounts -* user.mergerfs.create -* user.mergerfs.search +* user.mergerfs.category.action +* user.mergerfs.category.create +* user.mergerfs.category.search +* user.mergerfs.func.access +* user.mergerfs.func.chmod +* user.mergerfs.func.chown +* user.mergerfs.func.create +* user.mergerfs.func.getattr +* user.mergerfs.func.getxattr +* user.mergerfs.func.link +* user.mergerfs.func.listxattr +* user.mergerfs.func.mkdir +* user.mergerfs.func.mknod +* user.mergerfs.func.open +* user.mergerfs.func.readlink +* user.mergerfs.func.removexattr +* user.mergerfs.func.rename +* user.mergerfs.func.rmdir +* user.mergerfs.func.setxattr +* user.mergerfs.func.symlink +* user.mergerfs.func.truncate +* user.mergerfs.func.unlink +* user.mergerfs.func.utimens + +##### Example ##### ``` [trapexit:/tmp/mount] $ xattr -l .mergerfs user.mergerfs.srcmounts: /tmp/a:/tmp/b -user.mergerfs.create: epmfs -user.mergerfs.search: ff +user.mergerfs.category.action: ff +user.mergerfs.category.create: epmfs +user.mergerfs.category.search: ff +user.mergerfs.func.access: ff +user.mergerfs.func.chmod: ff +user.mergerfs.func.chown: ff +user.mergerfs.func.create: epmfs +user.mergerfs.func.getattr: ff +user.mergerfs.func.getxattr: ff +user.mergerfs.func.link: ff +user.mergerfs.func.listxattr: ff +user.mergerfs.func.mkdir: epmfs +user.mergerfs.func.mknod: epmfs +user.mergerfs.func.open: ff +user.mergerfs.func.readlink: ff +user.mergerfs.func.removexattr: ff +user.mergerfs.func.rename: ff +user.mergerfs.func.rmdir: ff +user.mergerfs.func.setxattr: ff +user.mergerfs.func.symlink: ff +user.mergerfs.func.truncate: ff +user.mergerfs.func.unlink: ff +user.mergerfs.func.utimens: ff -[trapexit:/tmp/mount] $ xattr -p user.mergerfs.search .mergerfs +[trapexit:/tmp/mount] $ xattr -p user.mergerfs.category.search .mergerfs ff -[trapexit:/tmp/mount] $ xattr -w user.mergerfs.search ffwp .mergerfs -[trapexit:/tmp/mount] $ xattr -p user.mergerfs.search .mergerfs +[trapexit:/tmp/mount] $ xattr -w user.mergerfs.category.search ffwp .mergerfs +[trapexit:/tmp/mount] $ xattr -p user.mergerfs.category.search .mergerfs ffwp [trapexit:/tmp/mount] $ xattr -w user.mergerfs.srcmounts +/tmp/c .mergerfs @@ -131,6 +177,8 @@ ffwp /tmp/a:/tmp/b:/tmp/c ``` +##### Extra Details ##### + For **user.mergerfs.srcmounts** there are several instructions available for manipulating the list. The value provided is just as the value used at mount time. A colon (':') delimited list of full path globs. | Instruction | Description | @@ -144,6 +192,8 @@ For **user.mergerfs.srcmounts** there are several instructions available for man | =[list] | set | | [list] | set | +Categories and funcs take a policy as described in the previous section. When reading funcs you'll get the policy string. However, with categories you'll get a coma separated list of policies for each type found. For example: if all search functions are `ff` except for `access` which is `ffwp` the value for `user.mergerfs.category.search` will be `ff,ffwp`. + #### 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: diff --git a/src/access.cpp b/src/access.cpp index aa41402a..724bdce1 100644 --- a/src/access.cpp +++ b/src/access.cpp @@ -75,7 +75,7 @@ namespace mergerfs const ugid::SetResetGuard ugid(fc->uid,fc->gid); const rwlock::ReadGuard readlock(&config.srcmountslock); - return _access(*config.search, + return _access(*config.access, config.srcmounts, fusepath, mask); diff --git a/src/category.cpp b/src/category.cpp index df1e2d40..2c068345 100644 --- a/src/category.cpp +++ b/src/category.cpp @@ -35,12 +35,14 @@ namespace mergerfs const std::vector Category::_categories_ = buildvector (CATEGORY(invalid)) + (CATEGORY(action)) (CATEGORY(create)) (CATEGORY(search)); const Category * const Category::categories = &_categories_[1]; const Category &Category::invalid = Category::categories[Category::Enum::invalid]; + const Category &Category::action = Category::categories[Category::Enum::action]; const Category &Category::create = Category::categories[Category::Enum::create]; const Category &Category::search = Category::categories[Category::Enum::search]; diff --git a/src/category.hpp b/src/category.hpp index 3bca5bc6..9b014e72 100644 --- a/src/category.hpp +++ b/src/category.hpp @@ -39,7 +39,8 @@ namespace mergerfs { invalid = -1, BEGIN = 0, - create = BEGIN, + action = BEGIN, + create, search, END }; @@ -88,6 +89,7 @@ namespace mergerfs static const std::vector _categories_; static const Category * const categories; static const Category &invalid; + static const Category &action; static const Category &create; static const Category &search; }; diff --git a/src/chmod.cpp b/src/chmod.cpp index 7c9d9797..26867383 100644 --- a/src/chmod.cpp +++ b/src/chmod.cpp @@ -69,7 +69,7 @@ namespace mergerfs const config::Config &config = config::get(); const rwlock::ReadGuard readlock(&config.srcmountslock); - return _chmod(*config.search, + return _chmod(*config.chmod, config.srcmounts, fusepath, mode); diff --git a/src/chown.cpp b/src/chown.cpp index 2609a1d3..41dd12c4 100644 --- a/src/chown.cpp +++ b/src/chown.cpp @@ -72,7 +72,7 @@ namespace mergerfs const ugid::SetResetGuard ugid(fc->uid,fc->gid); const rwlock::ReadGuard readlock(&config.srcmountslock); - return _chown(*config.search, + return _chown(*config.chown, config.srcmounts, fusepath, uid, diff --git a/src/config.cpp b/src/config.cpp index 580c696c..0b20812a 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -32,6 +32,8 @@ #include "rwlock.hpp" #include "fs.hpp" +#define POLICYINIT(X) X(policies[FuseFunc::Enum::X]) + using std::string; using std::vector; @@ -43,14 +45,46 @@ namespace mergerfs : destmount(), srcmounts(), srcmountslock(), - create(policies[Category::Enum::create]), - search(policies[Category::Enum::search]), + POLICYINIT(access), + POLICYINIT(chmod), + POLICYINIT(chown), + POLICYINIT(create), + POLICYINIT(getattr), + POLICYINIT(getxattr), + POLICYINIT(link), + POLICYINIT(listxattr), + POLICYINIT(mkdir), + POLICYINIT(mknod), + POLICYINIT(open), + POLICYINIT(readlink), + POLICYINIT(removexattr), + POLICYINIT(rename), + POLICYINIT(rmdir), + POLICYINIT(setxattr), + POLICYINIT(symlink), + POLICYINIT(truncate), + POLICYINIT(unlink), + POLICYINIT(utimens), controlfile("/.mergerfs") { pthread_rwlock_init(&srcmountslock,NULL); - create = &Policy::epmfs; - search = &Policy::ff; + setpolicy(Category::Enum::action,Policy::Enum::ff); + setpolicy(Category::Enum::create,Policy::Enum::epmfs); + setpolicy(Category::Enum::search,Policy::Enum::ff); + } + + void + Config::setpolicy(const Category::Enum::Type category, + const Policy::Enum::Type policy_) + { + const Policy *policy = Policy::find(policy_); + + for(int i = 0; i < FuseFunc::Enum::END; i++) + { + if(FuseFunc::fusefuncs[i] == category) + policies[(FuseFunc::Enum::Type)FuseFunc::fusefuncs[i]] = policy; + } } const Config& diff --git a/src/config.hpp b/src/config.hpp index 36aa1490..2c544b4f 100644 --- a/src/config.hpp +++ b/src/config.hpp @@ -33,7 +33,7 @@ #include #include "policy.hpp" -#include "category.hpp" +#include "fusefunc.hpp" namespace mergerfs { @@ -44,14 +44,37 @@ namespace mergerfs public: Config(); + public: + void setpolicy(const Category::Enum::Type category, + const Policy::Enum::Type policy); + public: std::string destmount; std::vector srcmounts; mutable pthread_rwlock_t srcmountslock; - const Policy *policies[Category::Enum::END]; + public: + const Policy *policies[FuseFunc::Enum::END]; + const Policy *&access; + const Policy *&chmod; + const Policy *&chown; const Policy *&create; - const Policy *&search; + const Policy *&getattr; + const Policy *&getxattr; + const Policy *&link; + const Policy *&listxattr; + const Policy *&mkdir; + const Policy *&mknod; + const Policy *&open; + const Policy *&readlink; + const Policy *&removexattr; + const Policy *&rename; + const Policy *&rmdir; + const Policy *&setxattr; + const Policy *&symlink; + const Policy *&truncate; + const Policy *&unlink; + const Policy *&utimens; public: const std::string controlfile; diff --git a/src/create.cpp b/src/create.cpp index 35818fb8..e6988b5f 100644 --- a/src/create.cpp +++ b/src/create.cpp @@ -100,7 +100,7 @@ namespace mergerfs const ugid::SetResetGuard ugid(fc->uid,fc->gid); const rwlock::ReadGuard readlock(&config.srcmountslock); - return _create(*config.search, + return _create(*config.create, *config.create, config.srcmounts, fusepath, diff --git a/src/fs.cpp b/src/fs.cpp index e449509c..b8d31b4c 100644 --- a/src/fs.cpp +++ b/src/fs.cpp @@ -39,7 +39,6 @@ #include #include #include -#include #include "fs.hpp" #include "xattr.hpp" @@ -492,7 +491,7 @@ namespace fs if(rv == -1 && errno != ENOTTY) return -1; - return (errno = 0); + return 0; } void @@ -515,32 +514,6 @@ namespace fs 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 { int diff --git a/src/fs.hpp b/src/fs.hpp index 588b960a..ac3d071e 100644 --- a/src/fs.hpp +++ b/src/fs.hpp @@ -110,9 +110,6 @@ namespace fs void glob(const vector &patterns, vector &strs); - void erase_fnmatches(const vector &patterns, - vector &strs); - namespace find { int invalid(const vector &basepaths, diff --git a/src/fusefunc.cpp b/src/fusefunc.cpp new file mode 100644 index 00000000..06fbb90b --- /dev/null +++ b/src/fusefunc.cpp @@ -0,0 +1,105 @@ +/* + 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 "fusefunc.hpp" +#include "category.hpp" +#include "buildvector.hpp" + +#define FUSEFUNC(X,Y) FuseFunc(FuseFunc::Enum::X,#X,Category::Enum::Y) + +namespace mergerfs +{ + const std::vector FuseFunc::_fusefuncs_ = + buildvector + (FUSEFUNC(invalid,invalid)) + (FUSEFUNC(access,search)) + (FUSEFUNC(chmod,action)) + (FUSEFUNC(chown,action)) + (FUSEFUNC(create,create)) + (FUSEFUNC(getattr,search)) + (FUSEFUNC(getxattr,search)) + (FUSEFUNC(link,action)) + (FUSEFUNC(listxattr,search)) + (FUSEFUNC(mkdir,create)) + (FUSEFUNC(mknod,create)) + (FUSEFUNC(open,search)) + (FUSEFUNC(readlink,search)) + (FUSEFUNC(removexattr,action)) + (FUSEFUNC(rename,action)) + (FUSEFUNC(rmdir,action)) + (FUSEFUNC(setxattr,action)) + (FUSEFUNC(symlink,search)) + (FUSEFUNC(truncate,action)) + (FUSEFUNC(unlink,action)) + (FUSEFUNC(utimens,action)) + ; + + const FuseFunc * const FuseFunc::fusefuncs = &_fusefuncs_[1]; + + const FuseFunc &FuseFunc::invalid = FuseFunc::fusefuncs[FuseFunc::Enum::invalid]; + const FuseFunc &FuseFunc::access = FuseFunc::fusefuncs[FuseFunc::Enum::access]; + const FuseFunc &FuseFunc::chmod = FuseFunc::fusefuncs[FuseFunc::Enum::chmod]; + const FuseFunc &FuseFunc::chown = FuseFunc::fusefuncs[FuseFunc::Enum::chown]; + const FuseFunc &FuseFunc::create = FuseFunc::fusefuncs[FuseFunc::Enum::create]; + const FuseFunc &FuseFunc::getattr = FuseFunc::fusefuncs[FuseFunc::Enum::getattr]; + const FuseFunc &FuseFunc::getxattr = FuseFunc::fusefuncs[FuseFunc::Enum::getxattr]; + const FuseFunc &FuseFunc::link = FuseFunc::fusefuncs[FuseFunc::Enum::link]; + const FuseFunc &FuseFunc::listxattr = FuseFunc::fusefuncs[FuseFunc::Enum::listxattr]; + const FuseFunc &FuseFunc::mkdir = FuseFunc::fusefuncs[FuseFunc::Enum::mkdir]; + const FuseFunc &FuseFunc::mknod = FuseFunc::fusefuncs[FuseFunc::Enum::mknod]; + const FuseFunc &FuseFunc::open = FuseFunc::fusefuncs[FuseFunc::Enum::open]; + const FuseFunc &FuseFunc::readlink = FuseFunc::fusefuncs[FuseFunc::Enum::readlink]; + const FuseFunc &FuseFunc::removexattr = FuseFunc::fusefuncs[FuseFunc::Enum::removexattr]; + const FuseFunc &FuseFunc::rmdir = FuseFunc::fusefuncs[FuseFunc::Enum::rmdir]; + const FuseFunc &FuseFunc::setxattr = FuseFunc::fusefuncs[FuseFunc::Enum::setxattr]; + const FuseFunc &FuseFunc::symlink = FuseFunc::fusefuncs[FuseFunc::Enum::symlink]; + const FuseFunc &FuseFunc::truncate = FuseFunc::fusefuncs[FuseFunc::Enum::truncate]; + const FuseFunc &FuseFunc::unlink = FuseFunc::fusefuncs[FuseFunc::Enum::unlink]; + const FuseFunc &FuseFunc::utimens = FuseFunc::fusefuncs[FuseFunc::Enum::utimens]; + + const FuseFunc& + FuseFunc::find(const std::string &str) + { + for(int i = Enum::BEGIN; i != Enum::END; ++i) + { + if(fusefuncs[i] == str) + return fusefuncs[i]; + } + + return invalid; + } + + const FuseFunc& + FuseFunc::find(const FuseFunc::Enum::Type i) + { + if(i >= FuseFunc::Enum::BEGIN && + i < FuseFunc::Enum::END) + return fusefuncs[i]; + + return invalid; + } +} diff --git a/src/fusefunc.hpp b/src/fusefunc.hpp new file mode 100644 index 00000000..432b19b2 --- /dev/null +++ b/src/fusefunc.hpp @@ -0,0 +1,139 @@ +/* + 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. +*/ + +#ifndef __FUSEFUNC_HPP__ +#define __FUSEFUNC_HPP__ + +#include +#include + +#include "category.hpp" + +namespace mergerfs +{ + class FuseFunc + { + public: + struct Enum + { + enum Type + { + invalid = -1, + BEGIN = 0, + access = BEGIN, + chmod, + chown, + create, + getattr, + getxattr, + link, + listxattr, + mkdir, + mknod, + open, + readlink, + removexattr, + rename, + rmdir, + setxattr, + symlink, + truncate, + unlink, + utimens, + END + }; + }; + + private: + Enum::Type _enum; + std::string _str; + Category::Enum::Type _category; + + public: + FuseFunc() + : _enum(invalid), + _str(invalid), + _category(Category::Enum::invalid) + { + } + + FuseFunc(const Enum::Type enum_, + const std::string &str_, + const Category::Enum::Type category_) + : _enum(enum_), + _str(str_), + _category(category_) + { + } + + public: + operator const Enum::Type() const { return _enum; } + operator const std::string&() const { return _str; } + operator const Category::Enum::Type() const { return _category; } + operator const FuseFunc*() const { return this; } + + bool operator==(const std::string &str_) const + { return _str == str_; } + + bool operator==(const Enum::Type enum_) const + { return _enum == enum_; } + + bool operator!=(const FuseFunc &r) const + { return _enum != r._enum; } + + bool operator<(const FuseFunc &r) const + { return _enum < r._enum; } + + public: + static const FuseFunc &find(const std::string&); + static const FuseFunc &find(const Enum::Type); + + public: + static const std::vector _fusefuncs_; + static const FuseFunc * const fusefuncs; + static const FuseFunc &invalid; + static const FuseFunc &access; + static const FuseFunc &chmod; + static const FuseFunc &chown; + static const FuseFunc &create; + static const FuseFunc &getattr; + static const FuseFunc &getxattr; + static const FuseFunc &link; + static const FuseFunc &listxattr; + static const FuseFunc &mkdir; + static const FuseFunc &mknod; + static const FuseFunc &open; + static const FuseFunc &readlink; + static const FuseFunc &removexattr; + static const FuseFunc &rename; + static const FuseFunc &rmdir; + static const FuseFunc &setxattr; + static const FuseFunc &symlink; + static const FuseFunc &truncate; + static const FuseFunc &unlink; + static const FuseFunc &utimens; + }; +} + +#endif diff --git a/src/getattr.cpp b/src/getattr.cpp index bfb05a83..baadfd38 100644 --- a/src/getattr.cpp +++ b/src/getattr.cpp @@ -99,7 +99,7 @@ namespace mergerfs const ugid::SetResetGuard ugid(fc->uid,fc->gid); const rwlock::ReadGuard readlock(&config.srcmountslock); - return _getattr(*config.search, + return _getattr(*config.getattr, config.srcmounts, fusepath, *st); diff --git a/src/getxattr.cpp b/src/getxattr.cpp index a67529c4..46047889 100644 --- a/src/getxattr.cpp +++ b/src/getxattr.cpp @@ -26,6 +26,8 @@ #include #include +#include +#include #include #include @@ -40,24 +42,76 @@ using std::string; using std::vector; +using std::set; +using namespace mergerfs; using namespace mergerfs::config; +static +void +_getxattr_controlfile_fusefunc_policy(const Config &config, + const char *attrbasename, + string &attrvalue) +{ + FuseFunc fusefunc; + + fusefunc = FuseFunc::find(attrbasename); + if(fusefunc != FuseFunc::invalid) + attrvalue = (std::string)*config.policies[(FuseFunc::Enum::Type)*fusefunc]; +} + +static +void +_getxattr_controlfile_category_policy(const Config &config, + const char *attrbasename, + string &attrvalue) +{ + Category cat; + + cat = Category::find(attrbasename); + if(cat != Category::invalid) + { + vector policies; + for(int i = FuseFunc::Enum::BEGIN; i < FuseFunc::Enum::END; i++) + { + if(cat == (Category::Enum::Type)*FuseFunc::fusefuncs[i]) + policies.push_back(*config.policies[i]); + } + + std::sort(policies.begin(),policies.end()); + policies.erase(std::unique(policies.begin(),policies.end()), + policies.end()); + attrvalue = str::join(policies,','); + } +} + +static +void +_getxattr_controlfile_srcmounts(const Config &config, + string &attrvalue) +{ + attrvalue = str::join(config.srcmounts,':'); +} + static int _getxattr_controlfile(const Config &config, - const string &attrname, + const char *attrname, char *buf, const size_t count) { size_t len; string attrvalue; + const char *attrbasename = &attrname[sizeof("user.mergerfs.")-1]; - if(attrname == "user.mergerfs.create") - 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(strncmp("user.mergerfs.",attrname,sizeof("user.mergerfs.")-1)) + return -ENOATTR; + + if(!strcmp("srcmounts",attrbasename)) + _getxattr_controlfile_srcmounts(config,attrvalue); + else if(!strncmp("category.",attrbasename,sizeof("category.")-1)) + _getxattr_controlfile_category_policy(config,&attrbasename[sizeof("category.")-1],attrvalue); + else if(!strncmp("func.",attrbasename,sizeof("func.")-1)) + _getxattr_controlfile_fusefunc_policy(config,&attrbasename[sizeof("func.")-1],attrvalue); if(attrvalue.empty()) return -ENOATTR; @@ -185,7 +239,7 @@ namespace mergerfs const ugid::SetResetGuard ugid(fc->uid,fc->gid); const rwlock::ReadGuard readlock(&config.srcmountslock); - return _getxattr(*config.search, + return _getxattr(*config.getxattr, config.srcmounts, fusepath, attrname, diff --git a/src/ioctl.cpp b/src/ioctl.cpp index c4449d5f..a93fd772 100644 --- a/src/ioctl.cpp +++ b/src/ioctl.cpp @@ -123,7 +123,7 @@ _ioctl_dir(const string &fusepath, const ugid::SetResetGuard ugid(fc->uid,fc->gid); const rwlock::ReadGuard readlock(&config.srcmountslock); - return _ioctl_dir_base(*config.search, + return _ioctl_dir_base(*config.getattr, config.srcmounts, fusepath, cmd, diff --git a/src/link.cpp b/src/link.cpp index e19c2226..41e3daa6 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -91,7 +91,7 @@ namespace mergerfs const ugid::SetResetGuard ugid(fc->uid,fc->gid); const rwlock::ReadGuard readlock(&config.srcmountslock); - return _link(*config.search, + return _link(*config.link, config.srcmounts, from, to); diff --git a/src/listxattr.cpp b/src/listxattr.cpp index e6b7d2d0..06dc2ae4 100644 --- a/src/listxattr.cpp +++ b/src/listxattr.cpp @@ -47,21 +47,24 @@ int _listxattr_controlfile(char *list, const size_t size) { - const char xattrs[] = - "user.mergerfs.srcmounts\0" - "user.mergerfs.create\0" - "user.mergerfs.search" - ; + string xattrs; + + xattrs.reserve(512); + xattrs.append("user.mergerfs.srcmounts",sizeof("user.mergerfs.srcmounts")); + for(int i = Category::Enum::BEGIN; i < Category::Enum::END; i++) + xattrs += ("user.mergerfs.category." + (std::string)*Category::categories[i] + '\0'); + for(int i = FuseFunc::Enum::BEGIN; i < FuseFunc::Enum::END; i++) + xattrs += ("user.mergerfs.func." + (std::string)*FuseFunc::fusefuncs[i] + '\0'); if(size == 0) - return sizeof(xattrs); + return xattrs.size(); - if(size < sizeof(xattrs)) + if(size < xattrs.size()) return -ERANGE; - memcpy(list,xattrs,sizeof(xattrs)); + memcpy(list,xattrs.c_str(),xattrs.size()); - return sizeof(xattrs); + return xattrs.size(); } static @@ -107,7 +110,7 @@ namespace mergerfs const ugid::SetResetGuard ugid(fc->uid,fc->gid); const rwlock::ReadGuard readlock(&config.srcmountslock); - return _listxattr(*config.search, + return _listxattr(*config.listxattr, config.srcmounts, fusepath, list, diff --git a/src/mkdir.cpp b/src/mkdir.cpp index 3c5c61ed..693e8613 100644 --- a/src/mkdir.cpp +++ b/src/mkdir.cpp @@ -92,8 +92,8 @@ namespace mergerfs const ugid::SetResetGuard ugid(fc->uid,fc->gid); const rwlock::ReadGuard readlock(&config.srcmountslock); - return _mkdir(*config.search, - *config.create, + return _mkdir(*config.getattr, + *config.mkdir, config.srcmounts, fusepath, (mode & ~fc->umask)); diff --git a/src/mknod.cpp b/src/mknod.cpp index 2df40b61..f99d7a6d 100644 --- a/src/mknod.cpp +++ b/src/mknod.cpp @@ -95,8 +95,8 @@ namespace mergerfs const ugid::SetResetGuard ugid(fc->uid,fc->gid); const rwlock::ReadGuard readlock(&config.srcmountslock); - return _mknod(*config.search, - *config.create, + return _mknod(*config.getattr, + *config.mknod, config.srcmounts, fusepath, (mode & ~fc->umask), diff --git a/src/open.cpp b/src/open.cpp index fe43608f..19f519c2 100644 --- a/src/open.cpp +++ b/src/open.cpp @@ -79,7 +79,7 @@ namespace mergerfs const ugid::SetResetGuard ugid(fc->uid,fc->gid); const rwlock::ReadGuard readlock(&config.srcmountslock); - return _open(*config.search, + return _open(*config.open, config.srcmounts, fusepath, fileinfo->flags, diff --git a/src/option_parser.cpp b/src/option_parser.cpp index 861a2c7a..9d83b5d7 100644 --- a/src/option_parser.cpp +++ b/src/option_parser.cpp @@ -53,12 +53,13 @@ process_opt(config::Config &config, switch(argvalue.size()) { case 2: - if(argvalue[0] == "create") - config.create = Policy::find(argvalue[1]); - else if(argvalue[0] == "search") - config.search = Policy::find(argvalue[1]); - else - rv = 1; + { + FuseFunc fusefunc = FuseFunc::find(argvalue[0]); + if(fusefunc != FuseFunc::invalid) + config.policies[(FuseFunc::Enum::Type)*fusefunc] = Policy::find(argvalue[1]); + else + rv = 1; + } break; default: diff --git a/src/readlink.cpp b/src/readlink.cpp index 575feed5..680b6f12 100644 --- a/src/readlink.cpp +++ b/src/readlink.cpp @@ -77,7 +77,7 @@ namespace mergerfs const ugid::SetResetGuard ugid(fc->uid,fc->gid); const rwlock::ReadGuard readlock(&config.srcmountslock); - return _readlink(*config.search, + return _readlink(*config.readlink, config.srcmounts, fusepath, buf, diff --git a/src/removexattr.cpp b/src/removexattr.cpp index b4dd98a1..5295498b 100644 --- a/src/removexattr.cpp +++ b/src/removexattr.cpp @@ -79,7 +79,7 @@ namespace mergerfs const ugid::SetResetGuard ugid(fc->uid,fc->gid); const rwlock::ReadGuard readlock(&config.srcmountslock); - return _removexattr(*config.search, + return _removexattr(*config.removexattr, config.srcmounts, fusepath, attrname); diff --git a/src/rename.cpp b/src/rename.cpp index 7a9e50ab..3ddd5afe 100644 --- a/src/rename.cpp +++ b/src/rename.cpp @@ -76,7 +76,7 @@ namespace mergerfs const ugid::SetResetGuard ugid(fc->uid,fc->gid); const rwlock::ReadGuard readlock(&config.srcmountslock); - return _rename(*config.search, + return _rename(*config.rename, config.srcmounts, from, to); diff --git a/src/rmdir.cpp b/src/rmdir.cpp index c87e65a4..4a8ab163 100644 --- a/src/rmdir.cpp +++ b/src/rmdir.cpp @@ -68,7 +68,7 @@ namespace mergerfs const ugid::SetResetGuard ugid(fc->uid,fc->gid); const rwlock::ReadGuard readguard(&config.srcmountslock); - return _rmdir(*config.search, + return _rmdir(*config.rmdir, config.srcmounts, fusepath); } diff --git a/src/setxattr.cpp b/src/setxattr.cpp index 4e1ca9cb..e4439619 100644 --- a/src/setxattr.cpp +++ b/src/setxattr.cpp @@ -30,6 +30,7 @@ #include #include +#include #include "config.hpp" #include "fs.hpp" @@ -41,7 +42,7 @@ using std::string; using std::vector; using mergerfs::Policy; -using mergerfs::Category; +using mergerfs::FuseFunc; using namespace mergerfs; static @@ -86,7 +87,7 @@ _erase_srcmounts(vector &srcmounts, { const rwlock::WriteGuard wrg(&srcmountslock); - fs::erase_fnmatches(patterns,srcmounts); + str::erase_fnmatches(patterns,srcmounts); } return 0; @@ -166,26 +167,54 @@ _setxattr_srcmounts(vector &srcmounts, static int -_setxattr_policy(const Policy *policies[], - const string &attrname, - const string &attrval, - const int flags) +_setxattr_controlfile_func_policy(const Policy *policies[], + const char *funcname, + const string &attrval, + const int flags) { - const Category *cat; + const FuseFunc *fusefunc; const Policy *policy; - cat = Category::find(attrname); - if(cat == Category::invalid) + fusefunc = FuseFunc::find(funcname); + if(fusefunc == FuseFunc::invalid) return -ENOATTR; if((flags & XATTR_CREATE) == XATTR_CREATE) return -EEXIST; policy = Policy::find(attrval); - if(policy == Policy::invalid) + if((policy == Policy::invalid) && + (attrval != "invalid")) return -EINVAL; - policies[*cat] = policy; + policies[(FuseFunc::Enum::Type)*fusefunc] = policy; + + return 0; +} + +static +int +_setxattr_controlfile_category_policy(config::Config &config, + const char *categoryname, + const string &attrval, + const int flags) +{ + const Category *category; + const Policy *policy; + + category = Category::find(categoryname); + if(category == Category::invalid) + return -ENOATTR; + + if((flags & XATTR_CREATE) == XATTR_CREATE) + return -EEXIST; + + policy = Policy::find(attrval); + if((policy == Policy::invalid) && + (attrval != "invalid")) + return -EINVAL; + + config.setpolicy(*category,*policy); return 0; } @@ -193,33 +222,36 @@ _setxattr_policy(const Policy *policies[], static int _setxattr_controlfile(config::Config &config, - const string &attrname, + const char *attrname, const string &attrval, const int flags) { - vector nameparts; + const char *attrbasename = &attrname[sizeof("user.mergerfs.")-1]; - str::split(nameparts,attrname,'.'); - - if(nameparts.size() != 3 || - nameparts[0] != "user" || - nameparts[1] != "mergerfs") + if(strncmp("user.mergerfs.",attrname,sizeof("user.mergerfs.")-1)) return -ENOATTR; if(attrval.empty()) return -EINVAL; - if(nameparts[2] == "srcmounts") + if(!strcmp("srcmounts",attrbasename)) return _setxattr_srcmounts(config.srcmounts, config.srcmountslock, config.destmount, attrval, flags); + else if(!strncmp("category.",attrbasename,sizeof("category.")-1)) + return _setxattr_controlfile_category_policy(config, + &attrbasename[sizeof("category.")-1], + attrval, + flags); + else if(!strncmp("func.",attrbasename,sizeof("func.")-1)) + return _setxattr_controlfile_func_policy(config.policies, + &attrbasename[sizeof("func.")-1], + attrval, + flags); - return _setxattr_policy(config.policies, - nameparts[2], - attrval, - flags); + return -EINVAL; } static @@ -271,7 +303,7 @@ namespace mergerfs const ugid::SetResetGuard ugid(fc->uid,fc->gid); const rwlock::ReadGuard readlock(&config.srcmountslock); - return _setxattr(*config.search, + return _setxattr(*config.setxattr, config.srcmounts, fusepath, attrname, diff --git a/src/str.cpp b/src/str.cpp index 69458d52..75416ba4 100644 --- a/src/str.cpp +++ b/src/str.cpp @@ -26,6 +26,8 @@ #include #include +#include + using std::string; using std::vector; using std::istringstream; @@ -111,4 +113,30 @@ namespace str return str::join(vec,idx,sep); } + + 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; + } + } } diff --git a/src/str.hpp b/src/str.hpp index e768bdc7..530e6422 100644 --- a/src/str.hpp +++ b/src/str.hpp @@ -50,4 +50,8 @@ namespace str std::string remove_common_prefix_and_join(const std::vector &vec, const char sep); + + void + erase_fnmatches(const std::vector &pattern, + std::vector &strs); } diff --git a/src/truncate.cpp b/src/truncate.cpp index 4bd28280..64268c7c 100644 --- a/src/truncate.cpp +++ b/src/truncate.cpp @@ -71,7 +71,7 @@ namespace mergerfs const ugid::SetResetGuard ugid(fc->uid,fc->gid); const rwlock::ReadGuard readlock(&config.srcmountslock); - return _truncate(*config.search, + return _truncate(*config.truncate, config.srcmounts, fusepath, size); diff --git a/src/unlink.cpp b/src/unlink.cpp index adf9c869..f72835a2 100644 --- a/src/unlink.cpp +++ b/src/unlink.cpp @@ -68,7 +68,7 @@ namespace mergerfs const ugid::SetResetGuard ugid(fc->uid,fc->gid); const rwlock::ReadGuard readlock(&config.srcmountslock); - return _unlink(*config.search, + return _unlink(*config.unlink, config.srcmounts, fusepath); } diff --git a/src/utimens.cpp b/src/utimens.cpp index a9b3409b..26a1db46 100644 --- a/src/utimens.cpp +++ b/src/utimens.cpp @@ -71,7 +71,7 @@ namespace mergerfs const ugid::SetResetGuard ugid(fc->uid,fc->gid); const rwlock::ReadGuard readlock(&config.srcmountslock); - return _utimens(*config.search, + return _utimens(*config.utimens, config.srcmounts, fusepath, ts);