Browse Source

support runtime setting of srcmounts. closes #12

pull/37/merge 1.4.0
Antonio SJ Musumeci 10 years ago
parent
commit
7e9ccd0317
  1. 38
      README.md
  2. 3
      src/access.cpp
  3. 3
      src/chmod.cpp
  4. 3
      src/chown.cpp
  5. 19
      src/config.cpp
  6. 1
      src/config.hpp
  7. 3
      src/create.cpp
  8. 66
      src/fs.cpp
  9. 6
      src/fs.hpp
  10. 10
      src/getattr.cpp
  11. 13
      src/getxattr.cpp
  12. 3
      src/link.cpp
  13. 31
      src/listxattr.cpp
  14. 3
      src/mkdir.cpp
  15. 3
      src/mknod.cpp
  16. 5
      src/open.cpp
  17. 62
      src/option_parser.cpp
  18. 1
      src/read.cpp
  19. 2
      src/readdir.cpp
  20. 3
      src/readlink.cpp
  21. 4
      src/release.cpp
  22. 10
      src/removexattr.cpp
  23. 2
      src/rename.cpp
  24. 2
      src/rmdir.cpp
  25. 71
      src/rwlock.hpp
  26. 204
      src/setxattr.cpp
  27. 2
      src/statfs.cpp
  28. 60
      src/str.cpp
  29. 36
      src/str.hpp
  30. 2
      src/symlink.cpp
  31. 2
      src/truncate.cpp
  32. 2
      src/unlink.cpp
  33. 2
      src/utimens.cpp
  34. 4
      src/write.cpp

38
README.md

@ -97,10 +97,15 @@ 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 **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 [trapexit:/tmp/mount] $ xattr -l .mergerfs
user.mergerfs.srcmounts: /tmp/a:/tmp/b
user.mergerfs.action: ff user.mergerfs.action: ff
user.mergerfs.create: epmfs user.mergerfs.create: epmfs
user.mergerfs.search: ff user.mergerfs.search: ff
@ -111,9 +116,34 @@ ff
[trapexit:/tmp/mount] $ xattr -w user.mergerfs.action ffwp .mergerfs [trapexit:/tmp/mount] $ xattr -w user.mergerfs.action ffwp .mergerfs
[trapexit:/tmp/mount] $ xattr -p user.mergerfs.action .mergerfs [trapexit:/tmp/mount] $ xattr -p user.mergerfs.action .mergerfs
ffwp 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: 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 [trapexit:/tmp/mount] $ ls
A B C A B C
[trapexit:/tmp/mount] $ xattr -p user.mergerfs.fullpath A [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
``` ```

3
src/access.cpp

@ -37,7 +37,7 @@
#include "ugid.hpp" #include "ugid.hpp"
#include "fs.hpp" #include "fs.hpp"
#include "config.hpp" #include "config.hpp"
#include "assert.hpp"
#include "rwlock.hpp"
using std::string; using std::string;
using std::vector; using std::vector;
@ -73,6 +73,7 @@ namespace mergerfs
const struct fuse_context *fc = fuse_get_context(); const struct fuse_context *fc = fuse_get_context();
const config::Config &config = config::get(); 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 _access(*config.search, return _access(*config.search,
config.srcmounts, config.srcmounts,

3
src/chmod.cpp

@ -31,7 +31,7 @@
#include "ugid.hpp" #include "ugid.hpp"
#include "fs.hpp" #include "fs.hpp"
#include "config.hpp" #include "config.hpp"
#include "assert.hpp"
#include "rwlock.hpp"
using std::string; using std::string;
using std::vector; using std::vector;
@ -76,6 +76,7 @@ namespace mergerfs
const struct fuse_context *fc = fuse_get_context(); const struct fuse_context *fc = fuse_get_context();
const ugid::SetResetGuard ugid(fc->uid,fc->gid); const ugid::SetResetGuard ugid(fc->uid,fc->gid);
const config::Config &config = config::get(); const config::Config &config = config::get();
const rwlock::ReadGuard readlock(&config.srcmountslock);
return _chmod(*config.action, return _chmod(*config.action,
config.srcmounts, config.srcmounts,

3
src/chown.cpp

@ -32,7 +32,7 @@
#include "ugid.hpp" #include "ugid.hpp"
#include "fs.hpp" #include "fs.hpp"
#include "config.hpp" #include "config.hpp"
#include "assert.hpp"
#include "rwlock.hpp"
using std::string; using std::string;
using std::vector; using std::vector;
@ -79,6 +79,7 @@ namespace mergerfs
const struct fuse_context *fc = fuse_get_context(); const struct fuse_context *fc = fuse_get_context();
const config::Config &config = config::get(); 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 _chown(*config.action, return _chown(*config.action,
config.srcmounts, config.srcmounts,

19
src/config.cpp

@ -24,23 +24,34 @@
#include <fuse.h> #include <fuse.h>
#include <sstream>
#include <string>
#include <vector>
#include <unistd.h> #include <unistd.h>
#include <sys/stat.h> #include <sys/stat.h>
#include "config.hpp" #include "config.hpp"
#include "rwlock.hpp"
#include "fs.hpp"
using std::string;
using std::vector;
namespace mergerfs namespace mergerfs
{ {
namespace config namespace config
{ {
Config::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") controlfile("/.mergerfs")
{ {
pthread_rwlock_init(&srcmountslock,NULL);
action = &Policy::ff; action = &Policy::ff;
create = &Policy::epmfs; create = &Policy::epmfs;
search = &Policy::ff; search = &Policy::ff;

1
src/config.hpp

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

3
src/create.cpp

@ -36,7 +36,7 @@
#include "ugid.hpp" #include "ugid.hpp"
#include "fileinfo.hpp" #include "fileinfo.hpp"
#include "fs.hpp" #include "fs.hpp"
#include "assert.hpp"
#include "rwlock.hpp"
using std::string; using std::string;
using std::vector; using std::vector;
@ -94,6 +94,7 @@ namespace mergerfs
const struct fuse_context *fc = fuse_get_context(); const struct fuse_context *fc = fuse_get_context();
const config::Config &config = config::get(); 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 _create(*config.search, return _create(*config.search,
*config.create, *config.create,

66
src/fs.cpp

@ -38,30 +38,18 @@
#include <sys/statvfs.h> #include <sys/statvfs.h>
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> #include <unistd.h>
#include <glob.h>
#include <fnmatch.h>
#include "fs.hpp" #include "fs.hpp"
#include "xattr.hpp" #include "xattr.hpp"
#include "str.hpp"
using std::string; using std::string;
using std::vector; using std::vector;
using std::map; using std::map;
using std::istringstream; 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> template <typename Iter>
Iter Iter
random_element(Iter begin, random_element(Iter begin,
@ -208,7 +196,7 @@ namespace fs
if(rv != -1) if(rv != -1)
{ {
string tmp(attrs.begin(),attrs.end()); string tmp(attrs.begin(),attrs.end());
split(attrvector,tmp,'\0');
str::split(attrvector,tmp,'\0');
} }
return rv; return rv;
@ -453,6 +441,52 @@ namespace fs
return (errno = 0); 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 namespace find
{ {
void void

6
src/fs.hpp

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

10
src/getattr.cpp

@ -35,7 +35,7 @@
#include "config.hpp" #include "config.hpp"
#include "fs.hpp" #include "fs.hpp"
#include "ugid.hpp" #include "ugid.hpp"
#include "assert.hpp"
#include "rwlock.hpp"
using std::string; using std::string;
using std::vector; using std::vector;
@ -90,13 +90,15 @@ namespace mergerfs
getattr(const char *fusepath, getattr(const char *fusepath,
struct stat *st) 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) if(fusepath == config.controlfile)
return _getattr_controlfile(*st); 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, return _getattr(*config.search,
config.srcmounts, config.srcmounts,
fusepath, fusepath,

13
src/getxattr.cpp

@ -34,8 +34,9 @@
#include "config.hpp" #include "config.hpp"
#include "fs.hpp" #include "fs.hpp"
#include "ugid.hpp" #include "ugid.hpp"
#include "assert.hpp"
#include "rwlock.hpp"
#include "xattr.hpp" #include "xattr.hpp"
#include "str.hpp"
using std::string; using std::string;
using std::vector; using std::vector;
@ -57,6 +58,8 @@ _getxattr_controlfile(const Config &config,
attrvalue = (std::string)*config.create; attrvalue = (std::string)*config.create;
else if(attrname == "user.mergerfs.search") else if(attrname == "user.mergerfs.search")
attrvalue = (std::string)*config.search; attrvalue = (std::string)*config.search;
else if(attrname == "user.mergerfs.srcmounts")
attrvalue = str::join(config.srcmounts,':');
if(attrvalue.empty()) if(attrvalue.empty())
return -ENOATTR; return -ENOATTR;
@ -133,9 +136,7 @@ namespace mergerfs
char *buf, char *buf,
size_t count) 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) if(fusepath == config.controlfile)
return _getxattr_controlfile(config, return _getxattr_controlfile(config,
@ -143,6 +144,10 @@ namespace mergerfs
buf, buf,
count); 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, return _getxattr(*config.search,
config.srcmounts, config.srcmounts,
fusepath, fusepath,

3
src/link.cpp

@ -33,7 +33,7 @@
#include "config.hpp" #include "config.hpp"
#include "fs.hpp" #include "fs.hpp"
#include "ugid.hpp" #include "ugid.hpp"
#include "assert.hpp"
#include "rwlock.hpp"
using std::string; using std::string;
using std::vector; using std::vector;
@ -97,6 +97,7 @@ namespace mergerfs
const struct fuse_context *fc = fuse_get_context(); const struct fuse_context *fc = fuse_get_context();
const config::Config &config = config::get(); 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 _link(*config.action, return _link(*config.action,
config.srcmounts, config.srcmounts,

31
src/listxattr.cpp

@ -35,7 +35,7 @@
#include "category.hpp" #include "category.hpp"
#include "fs.hpp" #include "fs.hpp"
#include "ugid.hpp" #include "ugid.hpp"
#include "assert.hpp"
#include "rwlock.hpp"
#include "xattr.hpp" #include "xattr.hpp"
using std::string; using std::string;
@ -47,23 +47,22 @@ int
_listxattr_controlfile(char *list, _listxattr_controlfile(char *list,
const size_t size) 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) if(size == 0)
return xattrssize;
return sizeof(xattrs);
if(size < xattrssize)
if(size < sizeof(xattrs))
return -ERANGE; return -ERANGE;
memcpy(list,xattrs.data(),xattrssize);
memcpy(list,xattrs,sizeof(xattrs));
return xattrssize;
return sizeof(xattrs);
} }
static static
@ -99,14 +98,16 @@ namespace mergerfs
char *list, char *list,
size_t size) 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) if(fusepath == config.controlfile)
return _listxattr_controlfile(list, return _listxattr_controlfile(list,
size); 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, return _listxattr(*config.search,
config.srcmounts, config.srcmounts,
fusepath, fusepath,

3
src/mkdir.cpp

@ -35,7 +35,7 @@
#include "fileinfo.hpp" #include "fileinfo.hpp"
#include "fs.hpp" #include "fs.hpp"
#include "config.hpp" #include "config.hpp"
#include "assert.hpp"
#include "rwlock.hpp"
using std::string; using std::string;
using std::vector; using std::vector;
@ -84,6 +84,7 @@ namespace mergerfs
const struct fuse_context *fc = fuse_get_context(); const struct fuse_context *fc = fuse_get_context();
const config::Config &config = config::get(); 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 _mkdir(*config.search, return _mkdir(*config.search,
*config.create, *config.create,

3
src/mknod.cpp

@ -36,7 +36,7 @@
#include "ugid.hpp" #include "ugid.hpp"
#include "fs.hpp" #include "fs.hpp"
#include "config.hpp" #include "config.hpp"
#include "assert.hpp"
#include "rwlock.hpp"
using std::string; using std::string;
using std::vector; using std::vector;
@ -87,6 +87,7 @@ namespace mergerfs
const struct fuse_context *fc = fuse_get_context(); const struct fuse_context *fc = fuse_get_context();
const config::Config &config = config::get(); 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 _mknod(*config.search, return _mknod(*config.search,
*config.create, *config.create,

5
src/open.cpp

@ -35,7 +35,7 @@
#include "fileinfo.hpp" #include "fileinfo.hpp"
#include "fs.hpp" #include "fs.hpp"
#include "config.hpp" #include "config.hpp"
#include "assert.hpp"
#include "rwlock.hpp"
using std::string; using std::string;
using std::vector; using std::vector;
@ -75,7 +75,8 @@ namespace mergerfs
{ {
const struct fuse_context *fc = fuse_get_context(); const struct fuse_context *fc = fuse_get_context();
const config::Config &config = config::get(); 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, return _open(*config.search,
config.srcmounts, config.srcmounts,

62
src/option_parser.cpp

@ -27,13 +27,13 @@
#include <stddef.h> #include <stddef.h>
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include <glob.h>
#include <string> #include <string>
#include <vector> #include <vector>
#include <sstream> #include <sstream>
#include <iostream> #include <iostream>
#include "str.hpp"
#include "config.hpp" #include "config.hpp"
#include "policy.hpp" #include "policy.hpp"
@ -41,25 +41,6 @@ using std::string;
using std::vector; using std::vector;
using namespace mergerfs; 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 static
int int
process_opt(config::Config &config, process_opt(config::Config &config,
@ -68,7 +49,7 @@ process_opt(config::Config &config,
int rv = 0; int rv = 0;
std::vector<std::string> argvalue; std::vector<std::string> argvalue;
split(argvalue,arg,'=');
str::split(argvalue,arg,'=');
switch(argvalue.size()) switch(argvalue.size())
{ {
case 2: case 2:
@ -91,26 +72,27 @@ process_opt(config::Config &config,
} }
static static
void
int
process_srcmounts(const char *arg, process_srcmounts(const char *arg,
config::Config &config) config::Config &config)
{ {
int flags;
glob_t gbuf;
vector<string> paths; 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,':');
fs::glob(paths,config.srcmounts);
return 0;
}
for(size_t i = 0; i < gbuf.gl_pathc; ++i)
config.srcmounts.push_back(gbuf.gl_pathv[i]);
static
int
process_destmounts(const char *arg,
config::Config &config)
{
config.destmount = arg;
globfree(&gbuf);
return 1;
} }
static static
@ -130,10 +112,9 @@ option_processor(void *data,
break; break;
case FUSE_OPT_KEY_NONOPT: 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; break;
default: default:
@ -152,10 +133,7 @@ set_fsname(struct fuse_args &args,
{ {
std::string fsname; 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()); fuse_opt_insert_arg(&args,1,fsname.c_str());
} }

1
src/read.cpp

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

2
src/readdir.cpp

@ -35,6 +35,7 @@
#include "config.hpp" #include "config.hpp"
#include "ugid.hpp" #include "ugid.hpp"
#include "fs.hpp" #include "fs.hpp"
#include "rwlock.hpp"
using std::string; using std::string;
using std::vector; using std::vector;
@ -125,6 +126,7 @@ namespace mergerfs
const struct fuse_context *fc = fuse_get_context(); const struct fuse_context *fc = fuse_get_context();
const config::Config &config = config::get(); 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 _readdir(config.srcmounts, return _readdir(config.srcmounts,
fusepath, fusepath,

3
src/readlink.cpp

@ -33,7 +33,7 @@
#include "config.hpp" #include "config.hpp"
#include "ugid.hpp" #include "ugid.hpp"
#include "fs.hpp" #include "fs.hpp"
#include "assert.hpp"
#include "rwlock.hpp"
using std::string; using std::string;
using std::vector; using std::vector;
@ -75,6 +75,7 @@ namespace mergerfs
const struct fuse_context *fc = fuse_get_context(); const struct fuse_context *fc = fuse_get_context();
const config::Config &config = config::get(); 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 _readlink(*config.search, return _readlink(*config.search,
config.srcmounts, config.srcmounts,

4
src/release.cpp

@ -54,9 +54,9 @@ namespace mergerfs
{ {
int int
release(const char *fusepath, release(const char *fusepath,
struct fuse_file_info *fi)
struct fuse_file_info *ffi)
{ {
return _release(fi->fh);
return _release(ffi->fh);
} }
} }
} }

10
src/removexattr.cpp

@ -33,7 +33,7 @@
#include "config.hpp" #include "config.hpp"
#include "ugid.hpp" #include "ugid.hpp"
#include "fs.hpp" #include "fs.hpp"
#include "assert.hpp"
#include "rwlock.hpp"
#include "xattr.hpp" #include "xattr.hpp"
using std::string; using std::string;
@ -79,13 +79,15 @@ namespace mergerfs
removexattr(const char *fusepath, removexattr(const char *fusepath,
const char *attrname) 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) if(fusepath == config.controlfile)
return -ENOTSUP; 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, return _removexattr(*config.action,
config.srcmounts, config.srcmounts,
fusepath, fusepath,

2
src/rename.cpp

@ -34,6 +34,7 @@
#include "ugid.hpp" #include "ugid.hpp"
#include "fs.hpp" #include "fs.hpp"
#include "config.hpp" #include "config.hpp"
#include "rwlock.hpp"
using std::string; using std::string;
using std::vector; using std::vector;
@ -73,6 +74,7 @@ namespace mergerfs
const struct fuse_context *fc = fuse_get_context(); const struct fuse_context *fc = fuse_get_context();
const config::Config &config = config::get(); 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 _rename(*config.search, return _rename(*config.search,
config.srcmounts, config.srcmounts,

2
src/rmdir.cpp

@ -32,6 +32,7 @@
#include "ugid.hpp" #include "ugid.hpp"
#include "fs.hpp" #include "fs.hpp"
#include "config.hpp" #include "config.hpp"
#include "rwlock.hpp"
using std::string; using std::string;
using std::vector; using std::vector;
@ -74,6 +75,7 @@ namespace mergerfs
const struct fuse_context *fc = fuse_get_context(); const struct fuse_context *fc = fuse_get_context();
const config::Config &config = config::get(); 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 readguard(&config.srcmountslock);
return _rmdir(*config.action, return _rmdir(*config.action,
config.srcmounts, config.srcmounts,

71
src/rwlock.hpp

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

204
src/setxattr.cpp

@ -34,8 +34,9 @@
#include "config.hpp" #include "config.hpp"
#include "fs.hpp" #include "fs.hpp"
#include "ugid.hpp" #include "ugid.hpp"
#include "assert.hpp"
#include "rwlock.hpp"
#include "xattr.hpp" #include "xattr.hpp"
#include "str.hpp"
using std::string; using std::string;
using std::vector; using std::vector;
@ -43,45 +44,135 @@ using mergerfs::Policy;
using mergerfs::Category; using mergerfs::Category;
using namespace mergerfs; 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 static
int 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)
{ {
const Category *cat;
const Policy *policy;
vector<string> nameparts;
if(srcmounts.empty())
return 0;
split(nameparts,attrname,'.');
vector<string> patterns;
if(nameparts.size() != 3)
return -EINVAL;
str::split(patterns,values,':');
if(nameparts[0] != "user")
return -ENOATTR;
const rwlock::WriteGuard wrg(&srcmountslock);
if(nameparts[1] != "mergerfs")
return -ENOATTR;
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;
cat = Category::find(nameparts[2]);
cat = Category::find(attrname);
if(cat == Category::invalid) if(cat == Category::invalid)
return -ENOATTR; return -ENOATTR;
@ -92,11 +183,43 @@ _setxattr_controlfile(config::Config &config,
if(policy == Policy::invalid) if(policy == Policy::invalid)
return -EINVAL; return -EINVAL;
config.policies[*cat] = policy;
policies[*cat] = policy;
return 0; 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 static
int int
_setxattr(const fs::SearchFunc searchFunc, _setxattr(const fs::SearchFunc searchFunc,
@ -142,24 +265,27 @@ namespace mergerfs
size_t attrvalsize, size_t attrvalsize,
int flags) 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) if(fusepath == config.controlfile)
return _setxattr_controlfile(config::get_writable(), return _setxattr_controlfile(config::get_writable(),
attrname, attrname,
string(attrval,attrvalsize), string(attrval,attrvalsize),
attrvalsize,
flags); 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);
}
} }
} }
} }

2
src/statfs.cpp

@ -34,6 +34,7 @@
#include "ugid.hpp" #include "ugid.hpp"
#include "config.hpp" #include "config.hpp"
#include "rwlock.hpp"
using std::string; using std::string;
using std::vector; using std::vector;
@ -126,6 +127,7 @@ namespace mergerfs
const struct fuse_context *fc = fuse_get_context(); const struct fuse_context *fc = fuse_get_context();
const config::Config &config = config::get(); 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 _statfs(config.srcmounts, return _statfs(config.srcmounts,
*stat); *stat);

60
src/str.cpp

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

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

2
src/symlink.cpp

@ -32,6 +32,7 @@
#include "fs.hpp" #include "fs.hpp"
#include "config.hpp" #include "config.hpp"
#include "ugid.hpp" #include "ugid.hpp"
#include "rwlock.hpp"
using std::string; using std::string;
using std::vector; using std::vector;
@ -67,6 +68,7 @@ namespace mergerfs
const struct fuse_context *fc = fuse_get_context(); const struct fuse_context *fc = fuse_get_context();
const config::Config &config = config::get(); 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 _symlink(config.srcmounts, return _symlink(config.srcmounts,
oldpath, oldpath,

2
src/truncate.cpp

@ -34,6 +34,7 @@
#include "ugid.hpp" #include "ugid.hpp"
#include "fs.hpp" #include "fs.hpp"
#include "config.hpp" #include "config.hpp"
#include "rwlock.hpp"
using std::string; using std::string;
using std::vector; using std::vector;
@ -77,6 +78,7 @@ namespace mergerfs
const struct fuse_context *fc = fuse_get_context(); const struct fuse_context *fc = fuse_get_context();
const config::Config &config = config::get(); 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 _truncate(*config.action, return _truncate(*config.action,
config.srcmounts, config.srcmounts,

2
src/unlink.cpp

@ -33,6 +33,7 @@
#include "ugid.hpp" #include "ugid.hpp"
#include "fs.hpp" #include "fs.hpp"
#include "config.hpp" #include "config.hpp"
#include "rwlock.hpp"
using std::string; using std::string;
using std::vector; using std::vector;
@ -74,6 +75,7 @@ namespace mergerfs
const struct fuse_context *fc = fuse_get_context(); const struct fuse_context *fc = fuse_get_context();
const config::Config &config = config::get(); 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 _unlink(*config.action, return _unlink(*config.action,
config.srcmounts, config.srcmounts,

2
src/utimens.cpp

@ -34,6 +34,7 @@
#include "ugid.hpp" #include "ugid.hpp"
#include "fs.hpp" #include "fs.hpp"
#include "config.hpp" #include "config.hpp"
#include "rwlock.hpp"
using std::string; using std::string;
using std::vector; using std::vector;
@ -77,6 +78,7 @@ namespace mergerfs
const struct fuse_context *fc = fuse_get_context(); const struct fuse_context *fc = fuse_get_context();
const config::Config &config = config::get(); 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 _utimens(*config.action, return _utimens(*config.action,
config.srcmounts, config.srcmounts,

4
src/write.cpp

@ -52,9 +52,9 @@ namespace mergerfs
const char *buf, const char *buf,
size_t count, size_t count,
off_t offset, 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, buf,
count, count,
offset); offset);

Loading…
Cancel
Save