per FUSE function policies. closes #52, #53

This commit is contained in:
Antonio SJ Musumeci 2015-02-19 09:09:24 -05:00
parent e0f0bb53f7
commit 12f393a55e
32 changed files with 569 additions and 122 deletions

View File

@ -8,7 +8,7 @@ mergerfs - another FUSE union filesystem
# SYNOPSIS # SYNOPSIS
mergerfs -ocreate=epmfs,search=ff <srcpoints> <mountpoint> mergerfs -o<options> <srcpoints> <mountpoint>
# DESCRIPTION # DESCRIPTION
@ -20,10 +20,11 @@ Why create mergerfs when those exist? mhddfs isn't really maintained or flexible
###options### ###options###
| Option | Default | All [FUSE](http://fuse.sourceforge.net) functions which have a category (see below) are option keys. The syntax being `<func>=<policy>`.
|--------|--------|
| search | ff | To set all function policies in a category use `category.<category>=<policy>` such as `category.create=mfs`.
| create | epmfs |
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### ###srcpoints###
@ -46,14 +47,15 @@ In /etc/fstab it'd look like the following:
# POLICIES # 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 #### #### Functional classifications ####
| Class | FUSE calls | | 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 | | 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 descriptions ####
| Policy | Description | | 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. 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.srcmounts
* user.mergerfs.create * user.mergerfs.category.action
* user.mergerfs.search * 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 [trapexit:/tmp/mount] $ xattr -l .mergerfs
user.mergerfs.srcmounts: /tmp/a:/tmp/b user.mergerfs.srcmounts: /tmp/a:/tmp/b
user.mergerfs.create: epmfs user.mergerfs.category.action: ff
user.mergerfs.search: 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 ff
[trapexit:/tmp/mount] $ xattr -w user.mergerfs.search ffwp .mergerfs [trapexit:/tmp/mount] $ xattr -w user.mergerfs.category.search ffwp .mergerfs
[trapexit:/tmp/mount] $ xattr -p user.mergerfs.search .mergerfs [trapexit:/tmp/mount] $ xattr -p user.mergerfs.category.search .mergerfs
ffwp ffwp
[trapexit:/tmp/mount] $ xattr -w user.mergerfs.srcmounts +/tmp/c .mergerfs [trapexit:/tmp/mount] $ xattr -w user.mergerfs.srcmounts +/tmp/c .mergerfs
@ -131,6 +177,8 @@ ffwp
/tmp/a:/tmp/b:/tmp/c /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. 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 | | Instruction | Description |
@ -144,6 +192,8 @@ For **user.mergerfs.srcmounts** there are several instructions available for man
| =[list] | set | | =[list] | set |
| [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 #### #### 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: 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:

View File

@ -75,7 +75,7 @@ namespace mergerfs
const ugid::SetResetGuard ugid(fc->uid,fc->gid); const ugid::SetResetGuard ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock); const rwlock::ReadGuard readlock(&config.srcmountslock);
return _access(*config.search, return _access(*config.access,
config.srcmounts, config.srcmounts,
fusepath, fusepath,
mask); mask);

View File

@ -35,12 +35,14 @@ namespace mergerfs
const std::vector<Category> Category::_categories_ = const std::vector<Category> Category::_categories_ =
buildvector<Category,true> buildvector<Category,true>
(CATEGORY(invalid)) (CATEGORY(invalid))
(CATEGORY(action))
(CATEGORY(create)) (CATEGORY(create))
(CATEGORY(search)); (CATEGORY(search));
const Category * const Category::categories = &_categories_[1]; const Category * const Category::categories = &_categories_[1];
const Category &Category::invalid = Category::categories[Category::Enum::invalid]; 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::create = Category::categories[Category::Enum::create];
const Category &Category::search = Category::categories[Category::Enum::search]; const Category &Category::search = Category::categories[Category::Enum::search];

View File

@ -39,7 +39,8 @@ namespace mergerfs
{ {
invalid = -1, invalid = -1,
BEGIN = 0, BEGIN = 0,
create = BEGIN, action = BEGIN,
create,
search, search,
END END
}; };
@ -88,6 +89,7 @@ namespace mergerfs
static const std::vector<Category> _categories_; static const std::vector<Category> _categories_;
static const Category * const categories; static const Category * const categories;
static const Category &invalid; static const Category &invalid;
static const Category &action;
static const Category &create; static const Category &create;
static const Category &search; static const Category &search;
}; };

View File

@ -69,7 +69,7 @@ namespace mergerfs
const config::Config &config = config::get(); const config::Config &config = config::get();
const rwlock::ReadGuard readlock(&config.srcmountslock); const rwlock::ReadGuard readlock(&config.srcmountslock);
return _chmod(*config.search, return _chmod(*config.chmod,
config.srcmounts, config.srcmounts,
fusepath, fusepath,
mode); mode);

View File

@ -72,7 +72,7 @@ namespace mergerfs
const ugid::SetResetGuard ugid(fc->uid,fc->gid); const ugid::SetResetGuard ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock); const rwlock::ReadGuard readlock(&config.srcmountslock);
return _chown(*config.search, return _chown(*config.chown,
config.srcmounts, config.srcmounts,
fusepath, fusepath,
uid, uid,

View File

@ -32,6 +32,8 @@
#include "rwlock.hpp" #include "rwlock.hpp"
#include "fs.hpp" #include "fs.hpp"
#define POLICYINIT(X) X(policies[FuseFunc::Enum::X])
using std::string; using std::string;
using std::vector; using std::vector;
@ -43,14 +45,46 @@ namespace mergerfs
: destmount(), : destmount(),
srcmounts(), srcmounts(),
srcmountslock(), srcmountslock(),
create(policies[Category::Enum::create]), POLICYINIT(access),
search(policies[Category::Enum::search]), 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") controlfile("/.mergerfs")
{ {
pthread_rwlock_init(&srcmountslock,NULL); pthread_rwlock_init(&srcmountslock,NULL);
create = &Policy::epmfs; setpolicy(Category::Enum::action,Policy::Enum::ff);
search = &Policy::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& const Config&

View File

@ -33,7 +33,7 @@
#include <vector> #include <vector>
#include "policy.hpp" #include "policy.hpp"
#include "category.hpp" #include "fusefunc.hpp"
namespace mergerfs namespace mergerfs
{ {
@ -44,14 +44,37 @@ namespace mergerfs
public: public:
Config(); Config();
public:
void setpolicy(const Category::Enum::Type category,
const Policy::Enum::Type policy);
public: public:
std::string destmount; std::string destmount;
std::vector<std::string> srcmounts; std::vector<std::string> srcmounts;
mutable pthread_rwlock_t srcmountslock; 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 *&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: public:
const std::string controlfile; const std::string controlfile;

View File

@ -100,7 +100,7 @@ namespace mergerfs
const ugid::SetResetGuard ugid(fc->uid,fc->gid); const ugid::SetResetGuard ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock); const rwlock::ReadGuard readlock(&config.srcmountslock);
return _create(*config.search, return _create(*config.create,
*config.create, *config.create,
config.srcmounts, config.srcmounts,
fusepath, fusepath,

View File

@ -39,7 +39,6 @@
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> #include <unistd.h>
#include <glob.h> #include <glob.h>
#include <fnmatch.h>
#include "fs.hpp" #include "fs.hpp"
#include "xattr.hpp" #include "xattr.hpp"
@ -492,7 +491,7 @@ namespace fs
if(rv == -1 && errno != ENOTTY) if(rv == -1 && errno != ENOTTY)
return -1; return -1;
return (errno = 0); return 0;
} }
void void
@ -515,32 +514,6 @@ namespace fs
globfree(&gbuf); globfree(&gbuf);
} }
void
erase_fnmatches(const vector<string> &patterns,
vector<string> &strs)
{
vector<string>::iterator si;
vector<string>::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 namespace find
{ {
int int

View File

@ -110,9 +110,6 @@ namespace fs
void glob(const vector<string> &patterns, void glob(const vector<string> &patterns,
vector<string> &strs); vector<string> &strs);
void erase_fnmatches(const vector<string> &patterns,
vector<string> &strs);
namespace find namespace find
{ {
int invalid(const vector<string> &basepaths, int invalid(const vector<string> &basepaths,

105
src/fusefunc.cpp Normal file
View File

@ -0,0 +1,105 @@
/*
The MIT License (MIT)
Copyright (c) 2014 Antonio SJ Musumeci <trapexit@spawn.link>
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 <string>
#include <vector>
#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> FuseFunc::_fusefuncs_ =
buildvector<FuseFunc,true>
(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;
}
}

139
src/fusefunc.hpp Normal file
View File

@ -0,0 +1,139 @@
/*
The MIT License (MIT)
Copyright (c) 2014 Antonio SJ Musumeci <trapexit@spawn.link>
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 <string>
#include <vector>
#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<FuseFunc> _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

View File

@ -99,7 +99,7 @@ namespace mergerfs
const ugid::SetResetGuard ugid(fc->uid,fc->gid); const ugid::SetResetGuard ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock); const rwlock::ReadGuard readlock(&config.srcmountslock);
return _getattr(*config.search, return _getattr(*config.getattr,
config.srcmounts, config.srcmounts,
fusepath, fusepath,
*st); *st);

View File

@ -26,6 +26,8 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <set>
#include <algorithm>
#include <errno.h> #include <errno.h>
#include <sys/types.h> #include <sys/types.h>
@ -40,24 +42,76 @@
using std::string; using std::string;
using std::vector; using std::vector;
using std::set;
using namespace mergerfs;
using namespace mergerfs::config; 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<string> 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 static
int int
_getxattr_controlfile(const Config &config, _getxattr_controlfile(const Config &config,
const string &attrname, const char *attrname,
char *buf, char *buf,
const size_t count) const size_t count)
{ {
size_t len; size_t len;
string attrvalue; string attrvalue;
const char *attrbasename = &attrname[sizeof("user.mergerfs.")-1];
if(attrname == "user.mergerfs.create") if(strncmp("user.mergerfs.",attrname,sizeof("user.mergerfs.")-1))
attrvalue = (std::string)*config.create; return -ENOATTR;
else if(attrname == "user.mergerfs.search")
attrvalue = (std::string)*config.search; if(!strcmp("srcmounts",attrbasename))
else if(attrname == "user.mergerfs.srcmounts") _getxattr_controlfile_srcmounts(config,attrvalue);
attrvalue = str::join(config.srcmounts,':'); 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()) if(attrvalue.empty())
return -ENOATTR; return -ENOATTR;
@ -185,7 +239,7 @@ namespace mergerfs
const ugid::SetResetGuard ugid(fc->uid,fc->gid); const ugid::SetResetGuard ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock); const rwlock::ReadGuard readlock(&config.srcmountslock);
return _getxattr(*config.search, return _getxattr(*config.getxattr,
config.srcmounts, config.srcmounts,
fusepath, fusepath,
attrname, attrname,

View File

@ -123,7 +123,7 @@ _ioctl_dir(const string &fusepath,
const ugid::SetResetGuard ugid(fc->uid,fc->gid); const ugid::SetResetGuard ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock); const rwlock::ReadGuard readlock(&config.srcmountslock);
return _ioctl_dir_base(*config.search, return _ioctl_dir_base(*config.getattr,
config.srcmounts, config.srcmounts,
fusepath, fusepath,
cmd, cmd,

View File

@ -91,7 +91,7 @@ namespace mergerfs
const ugid::SetResetGuard ugid(fc->uid,fc->gid); const ugid::SetResetGuard ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock); const rwlock::ReadGuard readlock(&config.srcmountslock);
return _link(*config.search, return _link(*config.link,
config.srcmounts, config.srcmounts,
from, from,
to); to);

View File

@ -47,21 +47,24 @@ int
_listxattr_controlfile(char *list, _listxattr_controlfile(char *list,
const size_t size) const size_t size)
{ {
const char xattrs[] = string xattrs;
"user.mergerfs.srcmounts\0"
"user.mergerfs.create\0" xattrs.reserve(512);
"user.mergerfs.search" 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) if(size == 0)
return sizeof(xattrs); return xattrs.size();
if(size < sizeof(xattrs)) if(size < xattrs.size())
return -ERANGE; return -ERANGE;
memcpy(list,xattrs,sizeof(xattrs)); memcpy(list,xattrs.c_str(),xattrs.size());
return sizeof(xattrs); return xattrs.size();
} }
static static
@ -107,7 +110,7 @@ namespace mergerfs
const ugid::SetResetGuard ugid(fc->uid,fc->gid); const ugid::SetResetGuard ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock); const rwlock::ReadGuard readlock(&config.srcmountslock);
return _listxattr(*config.search, return _listxattr(*config.listxattr,
config.srcmounts, config.srcmounts,
fusepath, fusepath,
list, list,

View File

@ -92,8 +92,8 @@ namespace mergerfs
const ugid::SetResetGuard ugid(fc->uid,fc->gid); const ugid::SetResetGuard ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock); const rwlock::ReadGuard readlock(&config.srcmountslock);
return _mkdir(*config.search, return _mkdir(*config.getattr,
*config.create, *config.mkdir,
config.srcmounts, config.srcmounts,
fusepath, fusepath,
(mode & ~fc->umask)); (mode & ~fc->umask));

View File

@ -95,8 +95,8 @@ namespace mergerfs
const ugid::SetResetGuard ugid(fc->uid,fc->gid); const ugid::SetResetGuard ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock); const rwlock::ReadGuard readlock(&config.srcmountslock);
return _mknod(*config.search, return _mknod(*config.getattr,
*config.create, *config.mknod,
config.srcmounts, config.srcmounts,
fusepath, fusepath,
(mode & ~fc->umask), (mode & ~fc->umask),

View File

@ -79,7 +79,7 @@ namespace mergerfs
const ugid::SetResetGuard ugid(fc->uid,fc->gid); const ugid::SetResetGuard ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock); const rwlock::ReadGuard readlock(&config.srcmountslock);
return _open(*config.search, return _open(*config.open,
config.srcmounts, config.srcmounts,
fusepath, fusepath,
fileinfo->flags, fileinfo->flags,

View File

@ -53,12 +53,13 @@ process_opt(config::Config &config,
switch(argvalue.size()) switch(argvalue.size())
{ {
case 2: case 2:
if(argvalue[0] == "create") {
config.create = Policy::find(argvalue[1]); FuseFunc fusefunc = FuseFunc::find(argvalue[0]);
else if(argvalue[0] == "search") if(fusefunc != FuseFunc::invalid)
config.search = Policy::find(argvalue[1]); config.policies[(FuseFunc::Enum::Type)*fusefunc] = Policy::find(argvalue[1]);
else else
rv = 1; rv = 1;
}
break; break;
default: default:

View File

@ -77,7 +77,7 @@ namespace mergerfs
const ugid::SetResetGuard ugid(fc->uid,fc->gid); const ugid::SetResetGuard ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock); const rwlock::ReadGuard readlock(&config.srcmountslock);
return _readlink(*config.search, return _readlink(*config.readlink,
config.srcmounts, config.srcmounts,
fusepath, fusepath,
buf, buf,

View File

@ -79,7 +79,7 @@ namespace mergerfs
const ugid::SetResetGuard ugid(fc->uid,fc->gid); const ugid::SetResetGuard ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock); const rwlock::ReadGuard readlock(&config.srcmountslock);
return _removexattr(*config.search, return _removexattr(*config.removexattr,
config.srcmounts, config.srcmounts,
fusepath, fusepath,
attrname); attrname);

View File

@ -76,7 +76,7 @@ namespace mergerfs
const ugid::SetResetGuard ugid(fc->uid,fc->gid); const ugid::SetResetGuard ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock); const rwlock::ReadGuard readlock(&config.srcmountslock);
return _rename(*config.search, return _rename(*config.rename,
config.srcmounts, config.srcmounts,
from, from,
to); to);

View File

@ -68,7 +68,7 @@ namespace mergerfs
const ugid::SetResetGuard ugid(fc->uid,fc->gid); const ugid::SetResetGuard ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readguard(&config.srcmountslock); const rwlock::ReadGuard readguard(&config.srcmountslock);
return _rmdir(*config.search, return _rmdir(*config.rmdir,
config.srcmounts, config.srcmounts,
fusepath); fusepath);
} }

View File

@ -30,6 +30,7 @@
#include <errno.h> #include <errno.h>
#include <sys/types.h> #include <sys/types.h>
#include <string.h>
#include "config.hpp" #include "config.hpp"
#include "fs.hpp" #include "fs.hpp"
@ -41,7 +42,7 @@
using std::string; using std::string;
using std::vector; using std::vector;
using mergerfs::Policy; using mergerfs::Policy;
using mergerfs::Category; using mergerfs::FuseFunc;
using namespace mergerfs; using namespace mergerfs;
static static
@ -86,7 +87,7 @@ _erase_srcmounts(vector<string> &srcmounts,
{ {
const rwlock::WriteGuard wrg(&srcmountslock); const rwlock::WriteGuard wrg(&srcmountslock);
fs::erase_fnmatches(patterns,srcmounts); str::erase_fnmatches(patterns,srcmounts);
} }
return 0; return 0;
@ -166,26 +167,54 @@ _setxattr_srcmounts(vector<string> &srcmounts,
static static
int int
_setxattr_policy(const Policy *policies[], _setxattr_controlfile_func_policy(const Policy *policies[],
const string &attrname, const char *funcname,
const string &attrval, const string &attrval,
const int flags) const int flags)
{ {
const Category *cat; const FuseFunc *fusefunc;
const Policy *policy; const Policy *policy;
cat = Category::find(attrname); fusefunc = FuseFunc::find(funcname);
if(cat == Category::invalid) if(fusefunc == FuseFunc::invalid)
return -ENOATTR; return -ENOATTR;
if((flags & XATTR_CREATE) == XATTR_CREATE) if((flags & XATTR_CREATE) == XATTR_CREATE)
return -EEXIST; return -EEXIST;
policy = Policy::find(attrval); policy = Policy::find(attrval);
if(policy == Policy::invalid) if((policy == Policy::invalid) &&
(attrval != "invalid"))
return -EINVAL; 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; return 0;
} }
@ -193,33 +222,36 @@ _setxattr_policy(const Policy *policies[],
static static
int int
_setxattr_controlfile(config::Config &config, _setxattr_controlfile(config::Config &config,
const string &attrname, const char *attrname,
const string &attrval, const string &attrval,
const int flags) const int flags)
{ {
vector<string> nameparts; const char *attrbasename = &attrname[sizeof("user.mergerfs.")-1];
str::split(nameparts,attrname,'.'); if(strncmp("user.mergerfs.",attrname,sizeof("user.mergerfs.")-1))
if(nameparts.size() != 3 ||
nameparts[0] != "user" ||
nameparts[1] != "mergerfs")
return -ENOATTR; return -ENOATTR;
if(attrval.empty()) if(attrval.empty())
return -EINVAL; return -EINVAL;
if(nameparts[2] == "srcmounts") if(!strcmp("srcmounts",attrbasename))
return _setxattr_srcmounts(config.srcmounts, return _setxattr_srcmounts(config.srcmounts,
config.srcmountslock, config.srcmountslock,
config.destmount, config.destmount,
attrval, attrval,
flags); 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, return -EINVAL;
nameparts[2],
attrval,
flags);
} }
static static
@ -271,7 +303,7 @@ namespace mergerfs
const ugid::SetResetGuard ugid(fc->uid,fc->gid); const ugid::SetResetGuard ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock); const rwlock::ReadGuard readlock(&config.srcmountslock);
return _setxattr(*config.search, return _setxattr(*config.setxattr,
config.srcmounts, config.srcmounts,
fusepath, fusepath,
attrname, attrname,

View File

@ -26,6 +26,8 @@
#include <vector> #include <vector>
#include <sstream> #include <sstream>
#include <fnmatch.h>
using std::string; using std::string;
using std::vector; using std::vector;
using std::istringstream; using std::istringstream;
@ -111,4 +113,30 @@ namespace str
return str::join(vec,idx,sep); return str::join(vec,idx,sep);
} }
void
erase_fnmatches(const vector<string> &patterns,
vector<string> &strs)
{
vector<string>::iterator si;
vector<string>::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;
}
}
} }

View File

@ -50,4 +50,8 @@ namespace str
std::string std::string
remove_common_prefix_and_join(const std::vector<std::string> &vec, remove_common_prefix_and_join(const std::vector<std::string> &vec,
const char sep); const char sep);
void
erase_fnmatches(const std::vector<std::string> &pattern,
std::vector<std::string> &strs);
} }

View File

@ -71,7 +71,7 @@ namespace mergerfs
const ugid::SetResetGuard ugid(fc->uid,fc->gid); const ugid::SetResetGuard ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock); const rwlock::ReadGuard readlock(&config.srcmountslock);
return _truncate(*config.search, return _truncate(*config.truncate,
config.srcmounts, config.srcmounts,
fusepath, fusepath,
size); size);

View File

@ -68,7 +68,7 @@ namespace mergerfs
const ugid::SetResetGuard ugid(fc->uid,fc->gid); const ugid::SetResetGuard ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock); const rwlock::ReadGuard readlock(&config.srcmountslock);
return _unlink(*config.search, return _unlink(*config.unlink,
config.srcmounts, config.srcmounts,
fusepath); fusepath);
} }

View File

@ -71,7 +71,7 @@ namespace mergerfs
const ugid::SetResetGuard ugid(fc->uid,fc->gid); const ugid::SetResetGuard ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.srcmountslock); const rwlock::ReadGuard readlock(&config.srcmountslock);
return _utimens(*config.search, return _utimens(*config.utimens,
config.srcmounts, config.srcmounts,
fusepath, fusepath,
ts); ts);