support runtime setting of srcmounts. closes #12

This commit is contained in:
Antonio SJ Musumeci 2014-08-03 15:50:28 -04:00
parent 992e05e897
commit 7e9ccd0317
34 changed files with 538 additions and 149 deletions

View File

@ -93,14 +93,19 @@ make XATTR_AVAILABLE=0 - to build program without xattrs functionality (auto dis
<mountpoint>/.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 '+</tmp/a:/tmp/b' .mergerfs
[trapexit:/tmp/mount] $ xattr -p user.mergerfs.srcmounts .mergerfs
/tmp/a:/tmp/b:/tmp/c
```
#### mergerfs xattrs ####
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 |
|--------------|-------------|
| +[list] | append |
| +<[list] | prepend |
| +>[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
```

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -24,23 +24,34 @@
#include <fuse.h>
#include <sstream>
#include <string>
#include <vector>
#include <unistd.h>
#include <sys/stat.h>
#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;

View File

@ -45,6 +45,7 @@ namespace mergerfs
public:
std::string destmount;
std::vector<std::string> srcmounts;
mutable pthread_rwlock_t srcmountslock;
const Policy *policies[Category::Enum::END];
const Policy *&action;

View File

@ -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,

View File

@ -38,30 +38,18 @@
#include <sys/statvfs.h>
#include <sys/types.h>
#include <unistd.h>
#include <glob.h>
#include <fnmatch.h>
#include "fs.hpp"
#include "xattr.hpp"
#include "str.hpp"
using std::string;
using std::vector;
using std::map;
using std::istringstream;
template<typename Container>
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 <typename Iter>
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<string> &patterns,
vector<string> &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<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
{
void

View File

@ -104,6 +104,12 @@ namespace fs
int copyattr(const string from,
const string to);
void glob(const vector<string> &patterns,
vector<string> &strs);
void erase_fnmatches(const vector<string> &patterns,
vector<string> &strs);
namespace find
{
void invalid(const vector<string> &basepaths,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -27,13 +27,13 @@
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include <glob.h>
#include <string>
#include <vector>
#include <sstream>
#include <iostream>
#include "str.hpp"
#include "config.hpp"
#include "policy.hpp"
@ -41,25 +41,6 @@ using std::string;
using std::vector;
using namespace mergerfs;
template<typename Container>
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<std::string> 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<string> 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,':');
for(size_t i = 0; i < gbuf.gl_pathc; ++i)
config.srcmounts.push_back(gbuf.gl_pathv[i]);
fs::glob(paths,config.srcmounts);
globfree(&gbuf);
return 0;
}
static
int
process_destmounts(const char *arg,
config::Config &config)
{
config.destmount = arg;
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());
}

View File

@ -29,7 +29,6 @@
#include <string.h>
#include <string>
#include <algorithm>
#include "fileinfo.hpp"

View File

@ -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,

View File

@ -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,

View File

@ -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);
}
}
}

View File

@ -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,

View File

@ -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,

View File

@ -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,

71
src/rwlock.hpp Normal file
View File

@ -0,0 +1,71 @@
/*
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 <pthread.h>
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();
};
}
}

View File

@ -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<typename Container>
Container&
split(Container &result,
const typename Container::value_type &s,
typename Container::value_type::value_type delimiter)
static
int
_add_srcmounts(vector<string> &srcmounts,
pthread_rwlock_t &srcmountslock,
const string destmount,
const string values,
vector<string>::iterator pos)
{
std::string str;
std::istringstream ss(s);
vector<string> patterns;
vector<string> additions;
while(std::getline(ss,str,delimiter))
result.push_back(str);
str::split(patterns,values,':');
fs::glob(patterns,additions);
return result;
if(!additions.empty())
{
const rwlock::WriteGuard wrg(&srcmountslock);
srcmounts.insert(pos,
additions.begin(),
additions.end());
}
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<string> &srcmounts,
pthread_rwlock_t srcmountslock,
const string &values)
{
if(srcmounts.empty())
return 0;
vector<string> patterns;
str::split(patterns,values,':');
const rwlock::WriteGuard wrg(&srcmountslock);
fs::erase_fnmatches(patterns,srcmounts);
return 0;
}
static
int
_replace_srcmounts(vector<string> &srcmounts,
pthread_rwlock_t srcmountslock,
const string destmount,
const string values)
{
vector<string> patterns;
vector<string> 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;
offset = attrval.find_first_of('/');
instruction = attrval.substr(0,offset);
if(offset != string::npos)
values = attrval.substr(offset);
}
static
int
_setxattr_srcmounts(vector<string> &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;
vector<string> nameparts;
split(nameparts,attrname,'.');
if(nameparts.size() != 3)
return -EINVAL;
if(nameparts[0] != "user")
return -ENOATTR;
if(nameparts[1] != "mergerfs")
return -ENOATTR;
cat = Category::find(nameparts[2]);
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<string> 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);
}
}
}
}

View File

@ -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);

60
src/str.cpp Normal file
View File

@ -0,0 +1,60 @@
/*
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 <sstream>
using std::string;
using std::vector;
using std::istringstream;
namespace str
{
void
split(vector<string> &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<string> &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;
}
}

36
src/str.hpp Normal file
View File

@ -0,0 +1,36 @@
/*
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>
namespace str
{
void split(std::vector<std::string> &result,
const std::string str,
const char delimiter);
std::string join(const std::vector<std::string> &vec,
const char sep);
}

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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);