Browse Source

rework config management

* Add standard way to do str2val and val2str conversion
* Add support for a config file
* Add support for reading and writing settings via ioctl
pull/766/head
Antonio SJ Musumeci 5 years ago
parent
commit
54c41c4994
  1. 10
      Makefile
  2. 38
      README.md
  3. 7
      libfuse/Makefile
  4. 22
      libfuse/include/fuse.h
  5. 10
      libfuse/include/fuse_common.h
  6. 13
      libfuse/include/fuse_lowlevel.h
  7. 424
      libfuse/lib/fuse.c
  8. 69
      libfuse/lib/fuse_lowlevel.c
  9. 69
      man/mergerfs.1
  10. 239
      src/branch.cpp
  11. 39
      src/branch.hpp
  12. 444
      src/config.cpp
  13. 220
      src/config.hpp
  14. 9
      src/dirinfo.hpp
  15. 21
      src/ef.hpp
  16. 78
      src/enum.hpp
  17. 31
      src/fh.hpp
  18. 9
      src/fileinfo.hpp
  19. 115
      src/from_string.cpp
  20. 32
      src/from_string.hpp
  21. 39
      src/func.cpp
  22. 221
      src/func.hpp
  23. 90
      src/func_category.cpp
  24. 69
      src/func_category.hpp
  25. 46
      src/funcs.hpp
  26. 20
      src/fuse_access.cpp
  27. 22
      src/fuse_chmod.cpp
  28. 24
      src/fuse_chown.cpp
  29. 34
      src/fuse_create.cpp
  30. 20
      src/fuse_fgetattr.cpp
  31. 7
      src/fuse_fgetattr.hpp
  32. 39
      src/fuse_getattr.cpp
  33. 5
      src/fuse_getattr.hpp
  34. 364
      src/fuse_getxattr.cpp
  35. 18
      src/fuse_init.cpp
  36. 239
      src/fuse_ioctl.cpp
  37. 2
      src/fuse_ioctl.hpp
  38. 76
      src/fuse_link.cpp
  39. 70
      src/fuse_listxattr.cpp
  40. 48
      src/fuse_mkdir.cpp
  41. 40
      src/fuse_mknod.cpp
  42. 20
      src/fuse_open.cpp
  43. 2
      src/fuse_opendir.cpp
  44. 4
      src/fuse_readdir_linux.icpp
  45. 12
      src/fuse_readdir_plus_linux.icpp
  46. 4
      src/fuse_readdir_plus_posix.icpp
  47. 4
      src/fuse_readdir_posix.icpp
  48. 16
      src/fuse_readlink.cpp
  49. 4
      src/fuse_release.cpp
  50. 26
      src/fuse_removexattr.cpp
  51. 112
      src/fuse_rename.cpp
  52. 20
      src/fuse_rmdir.cpp
  53. 454
      src/fuse_setxattr.cpp
  54. 30
      src/fuse_statfs.cpp
  55. 36
      src/fuse_symlink.cpp
  56. 22
      src/fuse_truncate.cpp
  57. 20
      src/fuse_unlink.cpp
  58. 23
      src/fuse_utimens.cpp
  59. 5
      src/fuse_write.cpp
  60. 45
      src/fuse_write_buf.cpp
  61. 95
      src/fusefunc.cpp
  62. 125
      src/fusefunc.hpp
  63. 35
      src/hw_cpu.cpp
  64. 27
      src/hw_cpu.hpp
  65. 18
      src/mergerfs.cpp
  66. 508
      src/option_parser.cpp
  67. 8
      src/option_parser.hpp
  68. 64
      src/policy.hpp
  69. 29
      src/policy_all.cpp
  70. 2
      src/policy_cache.cpp
  71. 41
      src/policy_epall.cpp
  72. 57
      src/policy_epff.cpp
  73. 31
      src/policy_eplfs.cpp
  74. 35
      src/policy_eplus.cpp
  75. 57
      src/policy_epmfs.cpp
  76. 6
      src/policy_eprand.cpp
  77. 8
      src/policy_erofs.cpp
  78. 13
      src/policy_ff.cpp
  79. 2
      src/policy_invalid.cpp
  80. 15
      src/policy_lfs.cpp
  81. 13
      src/policy_lus.cpp
  82. 15
      src/policy_mfs.cpp
  83. 35
      src/policy_newest.cpp
  84. 6
      src/policy_rand.cpp
  85. 47
      src/str.cpp
  86. 17
      src/str.hpp
  87. 58
      src/to_string.cpp
  88. 31
      src/to_string.hpp
  89. 47
      src/tofrom_string.hpp
  90. 131
      src/tofrom_wrapper.hpp

10
Makefile

@ -40,9 +40,9 @@ USE_XATTR = 1
UGID_USE_RWLOCK = 0
ifeq ($(DEBUG),1)
DEBUG_FLAGS := -O0 -g
OPT_FLAGS := -O0 -g
else
DEBUG_FLAGS :=
OPT_FLAGS := -O2
endif
ifeq ($(STATIC),1)
@ -62,8 +62,8 @@ OBJS = $(SRC:src/%.cpp=build/%.o)
DEPS = $(SRC:src/%.cpp=build/%.d)
MANPAGE = mergerfs.1
CXXFLAGS = \
-O2 \
$(DEBUG_FLAGS) \
-std=c++0x \
$(OPT_FLAGS) \
$(STATIC_FLAGS) \
$(LTO_FLAGS) \
-Wall \
@ -230,6 +230,6 @@ install-build-pkgs:
.PHONY: libfuse
libfuse:
$(MAKE) -C libfuse
$(MAKE) DEBUG=$(DEBUG) -C libfuse
-include $(DEPS)

38
README.md

@ -92,6 +92,8 @@ See the mergerfs [wiki for real world deployments](https://github.com/trapexit/m
### mount options
* **config**: Path to a config file. Same arguments as below in key=val format.
* **branches**: Colon delimited list of branches.
* **allow_other**: A libfuse option which allows users besides the one which ran mergerfs to see the filesystem. This is required for most use-cases.
* **minfreespace=SIZE**: The minimum space value used for creation policies. Understands 'K', 'M', and 'G' to represent kilobyte, megabyte, and gigabyte respectively. (default: 4G)
* **moveonenospc=BOOL**: When enabled if a **write** fails with **ENOSPC** (no space left on device) or **EDQUOT** (disk quota exceeded) a scan of all drives will be done looking for the drive with the most free space which is at least the size of the file plus the amount which failed to write. An attempt to move the file to that drive will occur (keeping all metadata possible) and if successful the original is unlinked and the write retried. (default: false)
@ -402,7 +404,41 @@ make LTO=1 - build with link time optimization
# RUNTIME CONFIG
#### .mergerfs pseudo file ####
#### ioctl
The original runtime config API was via xattr calls. This however became an issue when needing to disable xattr. While slightly less convenient ioctl does not have the same problems and will be the main API going forward.
The keys are the same as the command line option arguments as well as the config file.
##### requests / commands
All commands take a 4096 byte char buffer.
* read keys: get a nul '\0' delimited list of option keys
* _IOWR(0xDF,0,char[4096]) = 0xD000DF00
* on success ioctl return value is the total length
* read value: get an option value
* _IOWR(0xDF,1,char[4096]) = 0xD000DF01
* the key is passed in via the char buffer as a nul '\0' terminated string
* on success ioctl return value is the total length
* write value: set an option value
* _IOW(0xDF,2,char[4096]) = 0x5000DF02
* the key and value is passed in via the char buffer as a nul '\0' terminated string in the format of `key=value`
* on success ioctl return value is 0
* file info: get mergerfs metadata info for a file
* _IOWR(0xDF,3,char[4096]) = 0xD000DF03
* the key is passed in via the char buffer as a nul '\0' terminated string
* on success the ioctl return value is the total length
* keys:
* basepath: the base mount point for the file according to the getattr policy
* relpath: the relative path of the file from the mount point
* fullpath: the full path of the underlying file according to the getattr policy
* allpaths: a NUL '\0' delimited list of full paths to all files found
#### .mergerfs pseudo file (deprecated) ####
NOTE: this interface will be removed in mergerfs 3.0
```
<mountpoint>/.mergerfs

7
libfuse/Makefile

@ -1,9 +1,9 @@
VERSION = 2.9.7-mergerfs_2.29.0
ifeq ($(DEBUG),1)
DEBUG_FLAGS := -O0 -g
OPT_FLAGS := -O0 -g
else
DEBUG_FLAGS :=
OPT_FLAGS := -O2
endif
DESTDIR =
@ -39,8 +39,7 @@ OBJS = $(SRC:lib/%.c=build/%.o)
DEPS = $(SRC:lib/%.c=build/%.d)
CFLAGS = \
-O2 \
$(DEBUG_FLAGS) \
$(OPT_FLAGS) \
-Wall \
-pipe \
-MMD

22
libfuse/include/fuse.h

@ -77,7 +77,7 @@ struct fuse_operations {
* ignored. The 'st_ino' field is ignored except if the 'use_ino'
* mount option is given.
*/
int (*getattr) (const char *, struct stat *);
int (*getattr) (const char *, struct stat *, fuse_timeouts_t *);
/** Read the target of a symbolic link
*
@ -390,7 +390,7 @@ struct fuse_operations {
*
* Introduced in version 2.5
*/
int (*fgetattr) (const char *, struct stat *, struct fuse_file_info *);
int (*fgetattr) (const char *, struct stat *, struct fuse_file_info *, fuse_timeouts_t *);
/**
* Perform POSIX file locking operation
@ -504,7 +504,7 @@ struct fuse_operations {
* Introduced in version 2.8
*/
int (*ioctl) (const char *fusepath,
int cmd,
unsigned long cmd,
void *arg,
struct fuse_file_info *ffi,
unsigned int flags,
@ -853,9 +853,17 @@ struct fuse_fs;
* fuse_fs_releasedir and fuse_fs_statfs, which return 0.
*/
int fuse_fs_getattr(struct fuse_fs *fs, const char *path, struct stat *buf);
int fuse_fs_fgetattr(struct fuse_fs *fs, const char *path, struct stat *buf,
struct fuse_file_info *fi);
int fuse_fs_getattr(struct fuse_fs *fs,
const char *path,
struct stat *buf,
fuse_timeouts_t *timeout);
int fuse_fs_fgetattr(struct fuse_fs *fs,
const char *path,
struct stat *buf,
struct fuse_file_info *fi,
fuse_timeouts_t *timeout);
int fuse_fs_rename(struct fuse_fs *fs, const char *oldpath,
const char *newpath);
int fuse_fs_unlink(struct fuse_fs *fs, const char *path);
@ -920,7 +928,7 @@ int fuse_fs_removexattr(struct fuse_fs *fs, const char *path,
const char *name);
int fuse_fs_bmap(struct fuse_fs *fs, const char *path, size_t blocksize,
uint64_t *idx);
int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, int cmd, void *arg,
int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, unsigned long cmd, void *arg,
struct fuse_file_info *fi, unsigned int flags,
void *data, uint32_t *out_bufsz);
int fuse_fs_poll(struct fuse_fs *fs, const char *path,

10
libfuse/include/fuse_common.h

@ -40,6 +40,13 @@
extern "C" {
#endif
typedef struct fuse_timeouts_s fuse_timeouts_t;
struct fuse_timeouts_s
{
uint64_t entry;
uint64_t attr;
};
/**
* Information about open files
*
@ -83,9 +90,6 @@ fuse_file_info
uint32_t auto_cache : 1;
/** Padding. Do not use*/
uint32_t padding : 24;
/** File handle. May be filled in by filesystem in open().
Available in all other file operations */
uint64_t fh;

13
libfuse/include/fuse_lowlevel.h

@ -101,11 +101,7 @@ struct fuse_entry_param {
*/
struct stat attr;
/** Validity timeout (in seconds) for the attributes */
double attr_timeout;
/** Validity timeout (in seconds) for the name */
double entry_timeout;
fuse_timeouts_t timeout;
};
/** Additional context associated with requests */
@ -882,7 +878,7 @@ struct fuse_lowlevel_ops {
* @param in_bufsz number of fetched bytes
* @param out_bufsz maximum size of output data
*/
void (*ioctl) (fuse_req_t req, fuse_ino_t ino, int cmd, void *arg,
void (*ioctl) (fuse_req_t req, fuse_ino_t ino, unsigned long cmd, void *arg,
struct fuse_file_info *fi, unsigned flags,
const void *in_buf, uint32_t in_bufsz, uint32_t out_bufsz);
@ -1136,8 +1132,9 @@ int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e,
* @param attr_timeout validity timeout (in seconds) for the attributes
* @return zero for success, -errno for failure to send reply
*/
int fuse_reply_attr(fuse_req_t req, const struct stat *attr,
double attr_timeout);
int fuse_reply_attr(fuse_req_t req,
const struct stat *attr,
const uint64_t timeout);
/**
* Reply with the contents of a symbolic link

424
libfuse/lib/fuse.c

@ -58,9 +58,6 @@ struct fuse_config {
unsigned int uid;
unsigned int gid;
unsigned int umask;
double entry_timeout;
double negative_timeout;
double attr_timeout;
int remember;
int nopath;
int debug;
@ -1392,8 +1389,7 @@ static int fuse_compat_open(struct fuse_fs *fs, const char *path,
else if (fs->compat == 22) {
struct fuse_file_info_compat tmp;
memcpy(&tmp, fi, sizeof(tmp));
err = ((struct fuse_operations_compat22 *) &fs->op)->open(path,
&tmp);
err = ((struct fuse_operations_compat22 *) &fs->op)->open(path,&tmp);
memcpy(fi, &tmp, sizeof(tmp));
fi->fh = tmp.fh;
} else
@ -1504,37 +1500,49 @@ static inline int fuse_compat_statfs(struct fuse_fs *fs, const char *path,
#endif /* __FreeBSD__ || __NetBSD__ */
int fuse_fs_getattr(struct fuse_fs *fs, const char *path, struct stat *buf)
int
fuse_fs_getattr(struct fuse_fs *fs,
const char *path,
struct stat *buf,
fuse_timeouts_t *timeout)
{
fuse_get_context()->private_data = fs->user_data;
if (fs->op.getattr) {
if (fs->debug)
fprintf(stderr, "getattr %s\n", path);
if(fs->op.getattr == NULL)
return -ENOSYS;
return fs->op.getattr(path, buf);
} else {
return -ENOSYS;
}
if(fs->debug)
fprintf(stderr,"getattr %s\n",path);
fuse_get_context()->private_data = fs->user_data;
return fs->op.getattr(path,buf,timeout);
}
int fuse_fs_fgetattr(struct fuse_fs *fs, const char *path, struct stat *buf,
struct fuse_file_info *fi)
int
fuse_fs_fgetattr(struct fuse_fs *fs,
const char *path,
struct stat *buf,
struct fuse_file_info *fi,
fuse_timeouts_t *timeout)
{
fuse_get_context()->private_data = fs->user_data;
if (fs->op.fgetattr) {
if (fs->debug)
fprintf(stderr, "fgetattr[%llu] %s\n",
(unsigned long long) fi->fh, path);
fuse_get_context()->private_data = fs->user_data;
if(fs->op.fgetattr)
{
if(fs->debug)
fprintf(stderr,"fgetattr[%llu] %s\n",(unsigned long long)fi->fh,path);
return fs->op.fgetattr(path, buf, fi);
} else if (path && fs->op.getattr) {
if (fs->debug)
fprintf(stderr, "getattr %s\n", path);
return fs->op.fgetattr(path,buf,fi,timeout);
}
else if(path && fs->op.getattr)
{
if(fs->debug)
fprintf(stderr,"getattr %s\n",path);
return fs->op.getattr(path, buf);
} else {
return -ENOSYS;
}
return fs->op.getattr(path,buf,timeout);
}
else
{
return -ENOSYS;
}
}
int
@ -2254,14 +2262,14 @@ int fuse_fs_removexattr(struct fuse_fs *fs, const char *path, const char *name)
}
}
int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, int cmd, void *arg,
int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, unsigned long cmd, void *arg,
struct fuse_file_info *fi, unsigned int flags,
void *data, uint32_t *out_size)
{
fuse_get_context()->private_data = fs->user_data;
if (fs->op.ioctl) {
if (fs->debug)
fprintf(stderr, "ioctl[%llu] 0x%x flags: 0x%x\n",
fprintf(stderr, "ioctl[%llu] 0x%lx flags: 0x%x\n",
(unsigned long long) fi->fh, cmd, flags);
return fs->op.ioctl(path, cmd, arg, fi, flags, data, out_size);
@ -2377,41 +2385,53 @@ update_stat(struct node *node_,
*stold = *stnew_;
}
static int lookup_path(struct fuse *f, fuse_ino_t nodeid,
const char *name, const char *path,
struct fuse_entry_param *e, struct fuse_file_info *fi)
static
int
lookup_path(struct fuse *f,
fuse_ino_t nodeid,
const char *name,
const char *path,
struct fuse_entry_param *e,
struct fuse_file_info *fi)
{
int res;
int res;
memset(e, 0, sizeof(struct fuse_entry_param));
if (fi)
res = fuse_fs_fgetattr(f->fs, path, &e->attr, fi);
else
res = fuse_fs_getattr(f->fs, path, &e->attr);
if (res == 0) {
struct node *node;
memset(e,0,sizeof(struct fuse_entry_param));
node = find_node(f, nodeid, name);
if (node == NULL)
res = -ENOMEM;
else {
e->ino = node->nodeid;
e->generation = node->generation;
e->entry_timeout = f->conf.entry_timeout;
e->attr_timeout = f->conf.attr_timeout;
pthread_mutex_lock(&f->lock);
update_stat(node, &e->attr);
pthread_mutex_unlock(&f->lock);
set_stat(f, e->ino, &e->attr);
if(f->conf.debug)
fprintf(stderr,
" NODEID: %llu\n"
" GEN: %llu\n",
(unsigned long long)e->ino,
(unsigned long long)e->generation);
}
}
return res;
if(fi)
res = fuse_fs_fgetattr(f->fs,path,&e->attr,fi,&e->timeout);
else
res = fuse_fs_getattr(f->fs,path,&e->attr,&e->timeout);
if(res == 0)
{
struct node *node;
node = find_node(f,nodeid,name);
if(node == NULL)
{
res = -ENOMEM;
}
else
{
e->ino = node->nodeid;
e->generation = node->generation;
pthread_mutex_lock(&f->lock);
update_stat(node,&e->attr);
pthread_mutex_unlock(&f->lock);
set_stat(f,e->ino,&e->attr);
if(f->conf.debug)
fprintf(stderr,
" NODEID: %llu\n"
" GEN: %llu\n",
(unsigned long long)e->ino,
(unsigned long long)e->generation);
}
}
return res;
}
static struct fuse_context_i *fuse_get_context_internal(void)
@ -2545,61 +2565,63 @@ static void fuse_lib_destroy(void *data)
f->fs = NULL;
}
static void fuse_lib_lookup(fuse_req_t req, fuse_ino_t parent,
const char *name)
static
void
fuse_lib_lookup(fuse_req_t req,
fuse_ino_t parent,
const char *name)
{
struct fuse *f = req_fuse_prepare(req);
struct fuse_entry_param e;
char *path;
int err;
struct node *dot = NULL;
if (name[0] == '.') {
int len = strlen(name);
if (len == 1 || (name[1] == '.' && len == 2)) {
pthread_mutex_lock(&f->lock);
if (len == 1) {
if (f->conf.debug)
fprintf(stderr, "LOOKUP-DOT\n");
dot = get_node_nocheck(f, parent);
if (dot == NULL) {
pthread_mutex_unlock(&f->lock);
reply_entry(req, &e, -ESTALE);
return;
}
dot->refctr++;
} else {
if (f->conf.debug)
fprintf(stderr, "LOOKUP-DOTDOT\n");
parent = get_node(f, parent)->parent->nodeid;
}
pthread_mutex_unlock(&f->lock);
name = NULL;
}
}
struct fuse *f = req_fuse_prepare(req);
struct fuse_entry_param e;
char *path;
int err;
struct node *dot = NULL;
err = get_path_name(f, parent, name, &path);
if (!err) {
struct fuse_intr_data d;
if (f->conf.debug)
fprintf(stderr, "LOOKUP %s\n", path);
fuse_prepare_interrupt(f, req, &d);
err = lookup_path(f, parent, name, path, &e, NULL);
if (err == -ENOENT && f->conf.negative_timeout != 0.0) {
e.ino = 0;
e.entry_timeout = f->conf.negative_timeout;
err = 0;
}
fuse_finish_interrupt(f, req, &d);
free_path(f, parent, path);
}
if (dot) {
pthread_mutex_lock(&f->lock);
unref_node(f, dot);
pthread_mutex_unlock(&f->lock);
}
reply_entry(req, &e, err);
if (name[0] == '.') {
int len = strlen(name);
if (len == 1 || (name[1] == '.' && len == 2)) {
pthread_mutex_lock(&f->lock);
if (len == 1) {
if (f->conf.debug)
fprintf(stderr, "LOOKUP-DOT\n");
dot = get_node_nocheck(f, parent);
if (dot == NULL) {
pthread_mutex_unlock(&f->lock);
reply_entry(req, &e, -ESTALE);
return;
}
dot->refctr++;
} else {
if (f->conf.debug)
fprintf(stderr, "LOOKUP-DOTDOT\n");
parent = get_node(f, parent)->parent->nodeid;
}
pthread_mutex_unlock(&f->lock);
name = NULL;
}
}
err = get_path_name(f, parent, name, &path);
if (!err) {
struct fuse_intr_data d;
if (f->conf.debug)
fprintf(stderr, "LOOKUP %s\n", path);
fuse_prepare_interrupt(f, req, &d);
err = lookup_path(f, parent, name, path, &e, NULL);
if (err == -ENOENT) {
e.ino = 0;
err = 0;
}
fuse_finish_interrupt(f, req, &d);
free_path(f, parent, path);
}
if (dot) {
pthread_mutex_lock(&f->lock);
unref_node(f, dot);
pthread_mutex_unlock(&f->lock);
}
reply_entry(req, &e, err);
}
static
@ -2639,56 +2661,69 @@ static void fuse_lib_forget_multi(fuse_req_t req, size_t count,
}
static void fuse_lib_getattr(fuse_req_t req, fuse_ino_t ino,
struct fuse_file_info *fi)
static
void
fuse_lib_getattr(fuse_req_t req,
fuse_ino_t ino,
struct fuse_file_info *fi)
{
struct fuse *f = req_fuse_prepare(req);
struct stat buf;
char *path;
int err;
struct node *node;
struct fuse_file_info ffi = {0};
if(fi == NULL)
{
pthread_mutex_lock(&f->lock);
node = get_node(f,ino);
if(node->is_hidden)
{
fi = &ffi;
fi->fh = node->hidden_fh;
}
pthread_mutex_unlock(&f->lock);
}
int err;
char *path;
struct fuse *f;
struct stat buf;
struct node *node;
fuse_timeouts_t timeout;
struct fuse_file_info ffi = {0};
memset(&buf, 0, sizeof(buf));
f = req_fuse_prepare(req);
if(fi == NULL)
{
pthread_mutex_lock(&f->lock);
node = get_node(f,ino);
if(node->is_hidden)
{
fi = &ffi;
fi->fh = node->hidden_fh;
}
pthread_mutex_unlock(&f->lock);
}
path = NULL;
err = (((fi == NULL) || (f->fs->op.fgetattr == NULL)) ?
get_path(f,ino,&path) :
get_path_nullok(f,ino,&path));
memset(&buf, 0, sizeof(buf));
if (!err) {
struct fuse_intr_data d;
fuse_prepare_interrupt(f, req, &d);
path = NULL;
err = (((fi == NULL) || (f->fs->op.fgetattr == NULL)) ?
get_path(f,ino,&path) :
get_path_nullok(f,ino,&path));
err = ((fi == NULL) ?
fuse_fs_getattr(f->fs,path,&buf) :
fuse_fs_fgetattr(f->fs,path,&buf,fi));
if(!err)
{
struct fuse_intr_data d;
fuse_finish_interrupt(f, req, &d);
free_path(f, ino, path);
}
fuse_prepare_interrupt(f,req,&d);
if (!err) {
pthread_mutex_lock(&f->lock);
node = get_node(f, ino);
update_stat(node, &buf);
pthread_mutex_unlock(&f->lock);
set_stat(f, ino, &buf);
fuse_reply_attr(req, &buf, f->conf.attr_timeout);
} else
reply_err(req, err);
err = ((fi == NULL) ?
fuse_fs_getattr(f->fs,path,&buf,&timeout) :
fuse_fs_fgetattr(f->fs,path,&buf,fi,&timeout));
fuse_finish_interrupt(f,req,&d);
free_path(f,ino,path);
}
if(!err)
{
pthread_mutex_lock(&f->lock);
node = get_node(f,ino);
update_stat(node,&buf);
pthread_mutex_unlock(&f->lock);
set_stat(f,ino,&buf);
fuse_reply_attr(req,&buf,timeout.attr);
}
else
{
reply_err(req, err);
}
}
int fuse_fs_chmod(struct fuse_fs *fs, const char *path, mode_t mode)
@ -2715,10 +2750,10 @@ fuse_fs_fchmod(struct fuse_fs *fs_,
static
void
fuse_lib_setattr(fuse_req_t req,
fuse_ino_t ino,
struct stat *attr,
int valid,
fuse_lib_setattr(fuse_req_t req,
fuse_ino_t ino,
struct stat *attr,
int valid,
struct fuse_file_info *fi)
{
struct fuse *f = req_fuse_prepare(req);
@ -2726,6 +2761,7 @@ fuse_lib_setattr(fuse_req_t req,
char *path;
int err;
struct node *node;
fuse_timeouts_t timeout;
struct fuse_file_info ffi = {0};
if(fi == NULL)
@ -2812,22 +2848,23 @@ fuse_lib_setattr(fuse_req_t req,
fuse_fs_futimens(f->fs,fi,tv));
}
if(!err)
if (!err)
err = ((fi == NULL) ?
fuse_fs_getattr(f->fs,path,&buf) :
fuse_fs_fgetattr(f->fs,path,&buf,fi));
fuse_fs_getattr(f->fs,path,&buf,&timeout) :
fuse_fs_fgetattr(f->fs,path,&buf,fi,&timeout));
fuse_finish_interrupt(f,req,&d);
free_path(f,ino,path);
}
if(!err)
{
pthread_mutex_lock(&f->lock);
update_stat(get_node(f,ino),&buf);
pthread_mutex_unlock(&f->lock);
set_stat(f,ino,&buf);
fuse_reply_attr(req,&buf,f->conf.attr_timeout);
fuse_reply_attr(req,&buf,timeout.attr);
}
else
{
@ -3191,6 +3228,7 @@ open_auto_cache(struct fuse *f,
struct fuse_file_info *fi)
{
struct node *node;
fuse_timeouts_t timeout;
pthread_mutex_lock(&f->lock);
@ -3201,7 +3239,7 @@ open_auto_cache(struct fuse *f,
struct stat stbuf;
pthread_mutex_unlock(&f->lock);
err = fuse_fs_fgetattr(f->fs,path,&stbuf,fi);
err = fuse_fs_fgetattr(f->fs,path,&stbuf,fi,&timeout);
pthread_mutex_lock(&f->lock);
if(!err)
@ -4078,7 +4116,7 @@ static void fuse_lib_bmap(fuse_req_t req, fuse_ino_t ino, size_t blocksize,
reply_err(req, err);
}
static void fuse_lib_ioctl(fuse_req_t req, fuse_ino_t ino, int cmd, void *arg,
static void fuse_lib_ioctl(fuse_req_t req, fuse_ino_t ino, unsigned long cmd, void *arg,
struct fuse_file_info *llfi, unsigned int flags,
const void *in_buf, uint32_t in_bufsz,
uint32_t out_bufsz_)
@ -4228,7 +4266,7 @@ int fuse_clean_cache(struct fuse *f)
static struct fuse_lowlevel_ops fuse_path_ops = {
.init = fuse_lib_init,
.destroy = fuse_lib_destroy,
.lookup = fuse_lib_lookup,
.lookup = fuse_lib_lookup,
.forget = fuse_lib_forget,
.forget_multi = fuse_lib_forget_multi,
.getattr = fuse_lib_getattr,
@ -4469,9 +4507,6 @@ static const struct fuse_opt fuse_lib_opts[] = {
FUSE_LIB_OPT("uid=%d", uid, 0),
FUSE_LIB_OPT("gid=", set_gid, 1),
FUSE_LIB_OPT("gid=%d", gid, 0),
FUSE_LIB_OPT("entry_timeout=%lf", entry_timeout, 0),
FUSE_LIB_OPT("attr_timeout=%lf", attr_timeout, 0),
FUSE_LIB_OPT("negative_timeout=%lf", negative_timeout, 0),
FUSE_LIB_OPT("noforget", remember, -1),
FUSE_LIB_OPT("remember=%u", remember, 0),
FUSE_LIB_OPT("nopath", nopath, 1),
@ -4488,9 +4523,6 @@ static void fuse_lib_help(void)
" -o umask=M set file permissions (octal)\n"
" -o uid=N set file owner\n"
" -o gid=N set file group\n"
" -o entry_timeout=T cache timeout for names (1.0s)\n"
" -o negative_timeout=T cache timeout for deleted names (0.0s)\n"
" -o attr_timeout=T cache timeout for attributes (1.0s)\n"
" -o noforget never forget cached inodes\n"
" -o remember=T remember cached inodes for T seconds (0s)\n"
" -o nopath don't supply path if not necessary\n"
@ -4657,9 +4689,6 @@ struct fuse *fuse_new_common(struct fuse_chan *ch, struct fuse_args *args,
llop.setlk = NULL;
}
f->conf.entry_timeout = 1.0;
f->conf.attr_timeout = 1.0;
f->conf.negative_timeout = 0.0;
f->conf.intr_signal = FUSE_DEFAULT_INTR_SIGNAL;
f->pagesize = getpagesize();
@ -4889,42 +4918,3 @@ fuse_config_num_threads(const struct fuse *fuse_)
{
return fuse_->conf.threads;
}
void
fuse_config_set_entry_timeout(struct fuse *fuse_,
const double entry_timeout_)
{
fuse_->conf.entry_timeout = entry_timeout_;
}
double
fuse_config_get_entry_timeout(const struct fuse *fuse_)
{
return fuse_->conf.entry_timeout;
}
void
fuse_config_set_negative_entry_timeout(struct fuse *fuse_,
const double entry_timeout_)
{
fuse_->conf.negative_timeout = entry_timeout_;
}
double
fuse_config_get_negative_entry_timeout(const struct fuse *fuse_)
{
return fuse_->conf.negative_timeout;
}
void
fuse_config_set_attr_timeout(struct fuse *fuse_,
const double attr_timeout_)
{
fuse_->conf.attr_timeout = attr_timeout_;
}
double
fuse_config_get_attr_timeout(const struct fuse *fuse_)
{
return fuse_->conf.attr_timeout;
}

69
libfuse/lib/fuse_lowlevel.c

@ -319,37 +319,18 @@ void fuse_reply_none(fuse_req_t req)
fuse_free_req(req);
}
static unsigned long calc_timeout_sec(double t)
{
if (t > (double) ULONG_MAX)
return ULONG_MAX;
else if (t < 0.0)
return 0;
else
return (unsigned long) t;
}
static unsigned int calc_timeout_nsec(double t)
{
double f = t - (double) calc_timeout_sec(t);
if (f < 0.0)
return 0;
else if (f >= 0.999999999)
return 999999999;
else
return (unsigned int) (f * 1.0e9);
}
static void fill_entry(struct fuse_entry_out *arg,
const struct fuse_entry_param *e)
static
void
fill_entry(struct fuse_entry_out *arg,
const struct fuse_entry_param *e)
{
arg->nodeid = e->ino;
arg->generation = e->generation;
arg->entry_valid = calc_timeout_sec(e->entry_timeout);
arg->entry_valid_nsec = calc_timeout_nsec(e->entry_timeout);
arg->attr_valid = calc_timeout_sec(e->attr_timeout);
arg->attr_valid_nsec = calc_timeout_nsec(e->attr_timeout);
convert_stat(&e->attr, &arg->attr);
arg->nodeid = e->ino;
arg->generation = e->generation;
arg->entry_valid = e->timeout.entry;
arg->entry_valid_nsec = 0;
arg->attr_valid = e->timeout.attr;
arg->attr_valid_nsec = 0;
convert_stat(&e->attr,&arg->attr);
}
static void fill_open(struct fuse_open_out *arg,
@ -398,19 +379,21 @@ int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e,
entrysize + sizeof(struct fuse_open_out));
}
int fuse_reply_attr(fuse_req_t req, const struct stat *attr,
double attr_timeout)
int
fuse_reply_attr(fuse_req_t req,
const struct stat *attr,
const uint64_t timeout)
{
struct fuse_attr_out arg;
size_t size = req->f->conn.proto_minor < 9 ?
FUSE_COMPAT_ATTR_OUT_SIZE : sizeof(arg);
struct fuse_attr_out arg;
size_t size = req->f->conn.proto_minor < 9 ?
FUSE_COMPAT_ATTR_OUT_SIZE : sizeof(arg);
memset(&arg, 0, sizeof(arg));
arg.attr_valid = calc_timeout_sec(attr_timeout);
arg.attr_valid_nsec = calc_timeout_nsec(attr_timeout);
convert_stat(attr, &arg.attr);
memset(&arg,0,sizeof(arg));
arg.attr_valid = timeout;
arg.attr_valid_nsec = 0;
convert_stat(attr,&arg.attr);
return send_reply_ok(req, &arg, size);
return send_reply_ok(req,&arg,size);
}
int fuse_reply_readlink(fuse_req_t req, const char *linkname)
@ -1712,9 +1695,9 @@ static void do_ioctl(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
}
if (req->f->op.ioctl)
req->f->op.ioctl(req, nodeid, arg->cmd,
(void *)(uintptr_t)arg->arg, &fi, flags,
in_buf, arg->in_size, arg->out_size);
req->f->op.ioctl(req, nodeid, (unsigned long)arg->cmd,
(void *)(uintptr_t)arg->arg, &fi, flags,
in_buf, arg->in_size, arg->out_size);
else
fuse_reply_err(req, ENOSYS);
}

69
man/mergerfs.1

@ -116,6 +116,11 @@ for comparisons / ideas.
.SH OPTIONS
.SS mount options
.IP \[bu] 2
\f[B]config\f[]: Path to a config file.
Same arguments as below in key=val format.
.IP \[bu] 2
\f[B]branches\f[]: Colon delimited list of branches.
.IP \[bu] 2
\f[B]allow_other\f[]: A libfuse option which allows users besides the
one which ran mergerfs to see the filesystem.
This is required for most use\-cases.
@ -940,7 +945,69 @@ make\ LTO=1\ \ \ \ \ \ \ \ \ \ \ \ \-\ build\ with\ link\ time\ optimization
\f[]
.fi
.SH RUNTIME CONFIG
.SS .mergerfs pseudo file
.SS ioctl
.PP
The original runtime config API was via xattr calls.
This however became an issue when needing to disable xattr.
While slightly less convenient ioctl does not have the same problems and
will be the main API going forward.
.PP
The keys are the same as the command line option arguments as well as
the config file.
.SS requests / commands
.PP
All commands take a 4096 byte char buffer.
.IP \[bu] 2
read keys: get a nul \[aq]\[aq] delimited list of option keys
.IP \[bu] 2
_IOWR(0xDF,0,char[4096]) = 0xD000DF00
.IP \[bu] 2
on success ioctl return value is the total length
.IP \[bu] 2
read value: get an option value
.IP \[bu] 2
_IOWR(0xDF,1,char[4096]) = 0xD000DF01
.IP \[bu] 2
the key is passed in via the char buffer as a nul \[aq]\[aq] terminated
string
.IP \[bu] 2
on success ioctl return value is the total length
.IP \[bu] 2
write value: set an option value
.IP \[bu] 2
_IOW(0xDF,2,char[4096]) = 0x5000DF02
.IP \[bu] 2
the key and value is passed in via the char buffer as a nul \[aq]\[aq]
terminated string in the format of \f[C]key=value\f[]
.IP \[bu] 2
on success ioctl return value is 0
.IP \[bu] 2
file info: get mergerfs metadata info for a file
.IP \[bu] 2
_IOWR(0xDF,3,char[4096]) = 0xD000DF03
.IP \[bu] 2
the key is passed in via the char buffer as a nul \[aq]\[aq] terminated
string
.IP \[bu] 2
on success the ioctl return value is the total length
.IP \[bu] 2
keys:
.RS 2
.IP \[bu] 2
basepath: the base mount point for the file according to the getattr
policy
.IP \[bu] 2
relpath: the relative path of the file from the mount point
.IP \[bu] 2
fullpath: the full path of the underlying file according to the getattr
policy
.IP \[bu] 2
allpaths: a NUL \[aq]\[aq] delimited list of full paths to all files
found
.RE
.SS .mergerfs pseudo file (deprecated)
.PP
NOTE: this interface will be removed in mergerfs 3.0
.IP
.nf
\f[C]

239
src/branch.cpp

@ -17,14 +17,16 @@
*/
#include "branch.hpp"
#include "ef.hpp"
#include "fs.hpp"
#include "fs_glob.hpp"
#include "str.hpp"
#include <fnmatch.h>
#include <string>
#include <errno.h>
#include <fnmatch.h>
using std::string;
using std::vector;
@ -47,53 +49,23 @@ Branch::ro_or_nc(void) const
(mode == Branch::NC));
}
string
Branches::to_string(const bool mode_) const
static
void
split(const std::string &s_,
std::string *instr_,
std::string *values_)
{
string tmp;
for(size_t i = 0; i < size(); i++)
{
const Branch &branch = (*this)[i];
tmp += branch.path;
if(mode_)
{
tmp += '=';
switch(branch.mode)
{
default:
case Branch::RW:
tmp += "RW";
break;
case Branch::RO:
tmp += "RO";
break;
case Branch::NC:
tmp += "NC";
break;
}
}
tmp += ':';
}
if(*tmp.rbegin() == ':')
tmp.erase(tmp.size() - 1);
uint64_t offset;
return tmp;
offset = s_.find_first_of('/');
*instr_ = s_.substr(0,offset);
if(offset != std::string::npos)
*values_ = s_.substr(offset);
}
void
Branches::to_paths(vector<string> &vec_) const
Branches::Branches()
{
for(size_t i = 0; i < size(); i++)
{
const Branch &branch = (*this)[i];
vec_.push_back(branch.path);
}
pthread_rwlock_init(&lock,NULL);
}
static
@ -107,11 +79,11 @@ parse(const string &str_,
str = str_;
branch.mode = Branch::INVALID;
if(str::ends_with(str,"=RO"))
if(str::endswith(str,"=RO"))
branch.mode = Branch::RO;
else if(str::ends_with(str,"=RW"))
ef(str::endswith(str,"=RW"))
branch.mode = Branch::RW;
else if(str::ends_with(str,"=NC"))
ef(str::endswith(str,"=NC"))
branch.mode = Branch::NC;
if(branch.mode != Branch::INVALID)
@ -128,29 +100,33 @@ parse(const string &str_,
}
}
static
void
Branches::set(const std::string &str_)
set(Branches &branches_,
const std::string &str_)
{
vector<string> paths;
clear();
branches_.clear();
str::split(paths,str_,':');
for(size_t i = 0; i < paths.size(); i++)
{
Branches branches;
Branches tmp;
parse(paths[i],branches);
parse(paths[i],tmp);
insert(end(),
branches.begin(),
branches.end());
branches_.insert(branches_.end(),
tmp.begin(),
tmp.end());
}
}
static
void
Branches::add_begin(const std::string &str_)
add_begin(Branches &branches_,
const std::string &str_)
{
vector<string> paths;
@ -158,18 +134,20 @@ Branches::add_begin(const std::string &str_)
for(size_t i = 0; i < paths.size(); i++)
{
Branches branches;
Branches tmp;
parse(paths[i],branches);
parse(paths[i],tmp);
insert(begin(),
branches.begin(),
branches.end());
branches_.insert(branches_.begin(),
tmp.begin(),
tmp.end());
}
}
static
void
Branches::add_end(const std::string &str_)
add_end(Branches &branches_,
const std::string &str_)
{
vector<string> paths;
@ -177,36 +155,41 @@ Branches::add_end(const std::string &str_)
for(size_t i = 0; i < paths.size(); i++)
{
Branches branches;
Branches tmp;
parse(paths[i],branches);
parse(paths[i],tmp);
insert(end(),
branches.begin(),
branches.end());
branches_.insert(branches_.end(),
tmp.begin(),
tmp.end());
}
}
static
void
Branches::erase_begin(void)
erase_begin(Branches &branches_)
{
erase(begin());
branches_.erase(branches_.begin());
}
static
void
Branches::erase_end(void)
erase_end(Branches &branches_)
{
pop_back();
branches_.pop_back();
}
static
void
Branches::erase_fnmatch(const std::string &str_)
erase_fnmatch(Branches &branches_,
const std::string &str_)
{
vector<string> patterns;
str::split(patterns,str_,':');
for(iterator i = begin(); i != end();)
for(Branches::iterator i = branches_.begin();
i != branches_.end();)
{
int match = FNM_NOMATCH;
@ -217,6 +200,116 @@ Branches::erase_fnmatch(const std::string &str_)
match = ::fnmatch(pi->c_str(),i->path.c_str(),0);
}
i = ((match == 0) ? erase(i) : (i+1));
i = ((match == 0) ? branches_.erase(i) : (i+1));
}
}
int
Branches::from_string(const std::string &s_)
{
rwlock::WriteGuard guard(&lock);
std::string instr;
std::string values;
::split(s_,&instr,&values);
if(instr == "+")
::add_end(*this,values);
ef(instr == "+<")
::add_begin(*this,values);
ef(instr == "+>")
::add_end(*this,values);
ef(instr == "-")
::erase_fnmatch(*this,values);
ef(instr == "-<")
::erase_begin(*this);
ef(instr == "->")
::erase_end(*this);
ef(instr == "=")
::set(*this,values);
ef(instr.empty())
::set(*this,values);
else
return -EINVAL;
return 0;
}
string
Branches::to_string(void) const
{
rwlock::ReadGuard guard(&lock);
string tmp;
for(size_t i = 0; i < size(); i++)
{
const Branch &branch = (*this)[i];
tmp += branch.path;
tmp += '=';
switch(branch.mode)
{
default:
case Branch::RW:
tmp += "RW";
break;
case Branch::RO:
tmp += "RO";
break;
case Branch::NC:
tmp += "NC";
break;
}
tmp += ':';
}
if(*tmp.rbegin() == ':')
tmp.erase(tmp.size() - 1);
return tmp;
}
void
Branches::to_paths(vector<string> &vec_) const
{
rwlock::ReadGuard guard(&lock);
for(size_t i = 0; i < size(); i++)
{
const Branch &branch = (*this)[i];
vec_.push_back(branch.path);
}
}
SrcMounts::SrcMounts(Branches &b_)
: _branches(b_)
{
}
int
SrcMounts::from_string(const std::string &s_)
{
return _branches.from_string(s_);
}
std::string
SrcMounts::to_string(void) const
{
rwlock::ReadGuard guard(&_branches.lock);
std::string rv;
for(uint64_t i = 0; i < _branches.size(); i++)
{
rv += _branches[i].path;
rv += ':';
}
if(*rv.rbegin() == ':')
rv.erase(rv.size() - 1);
return rv;
}

39
src/branch.hpp

@ -18,11 +18,17 @@
#pragma once
#include "rwlock.hpp"
#include "tofrom_string.hpp"
#include <string>
#include <vector>
struct Branch
#include <pthread.h>
class Branch
{
public:
enum Mode
{
INVALID,
@ -39,18 +45,31 @@ struct Branch
bool ro_or_nc(void) const;
};
class Branches : public std::vector<Branch>
class Branches : public std::vector<Branch>, public ToFromString
{
public:
std::string to_string(const bool mode_ = false) const;
Branches();
void to_paths(std::vector<std::string> &vec_) const;
public:
int from_string(const std::string &str);
std::string to_string(void) const;
public:
void set(const std::string &str_);
void add_begin(const std::string &str_);
void add_end(const std::string &str_);
void erase_begin(void);
void erase_end(void);
void erase_fnmatch(const std::string &str_);
void to_paths(std::vector<std::string> &vec) const;
public:
mutable pthread_rwlock_t lock;
};
class SrcMounts : public ToFromString
{
public:
SrcMounts(Branches &b_);
public:
int from_string(const std::string &str);
std::string to_string(void) const;
private:
Branches &_branches;
};

444
src/config.cpp

@ -15,187 +15,393 @@
*/
#include "config.hpp"
#include "ef.hpp"
#include "errno.hpp"
#include "from_string.hpp"
#include "fs.hpp"
#include "num.hpp"
#include "rwlock.hpp"
#include "to_string.hpp"
#include "version.hpp"
#include <algorithm>
#include <string>
#include <vector>
#include <iostream>
#include <unistd.h>
#include <stdint.h>
#include <sys/stat.h>
#include <unistd.h>
#define MINFREESPACE_DEFAULT (4294967295ULL)
#define POLICYINIT(X) X(policies[FuseFunc::Enum::X])
using std::string;
using std::vector;
#define IFERT(S) if(S == s_) return true
namespace l
{
static
bool
readonly(const std::string &s_)
{
IFERT("async_read");
IFERT("cache.symlinks");
IFERT("cache.writeback");
IFERT("fsname");
IFERT("fuse_msg_size");
IFERT("mount");
IFERT("nullrw");
IFERT("pid");
IFERT("readdirplus");
IFERT("threads");
IFERT("version");
return false;
}
}
Config::Config()
: destmount(),
branches(),
branches_lock(),
minfreespace(MINFREESPACE_DEFAULT),
moveonenospc(false),
direct_io(false),
dropcacheonclose(false),
symlinkify(false),
symlinkify_timeout(3600),
nullrw(false),
ignorepponrename(false),
security_capability(true),
link_cow(false),
xattr(0),
statfs(StatFS::BASE),
statfs_ignore(StatFSIgnore::NONE),
posix_acl(false),
cache_symlinks(false),
cache_readdir(false),
async_read(true),
writeback_cache(false),
readdirplus(false),
cache_files(CacheFiles::LIBFUSE),
fuse_msg_size(FUSE_MAX_MAX_PAGES),
POLICYINIT(access),
POLICYINIT(chmod),
POLICYINIT(chown),
POLICYINIT(create),
POLICYINIT(getattr),
POLICYINIT(getxattr),
POLICYINIT(link),
POLICYINIT(listxattr),
POLICYINIT(mkdir),
POLICYINIT(mknod),
POLICYINIT(open),
POLICYINIT(readlink),
POLICYINIT(removexattr),
POLICYINIT(rename),
POLICYINIT(rmdir),
POLICYINIT(setxattr),
POLICYINIT(symlink),
POLICYINIT(truncate),
POLICYINIT(unlink),
POLICYINIT(utimens),
controlfile("/.mergerfs")
{
pthread_rwlock_init(&branches_lock,NULL);
set_category_policy("action","epall");
set_category_policy("create","epmfs");
set_category_policy("search","ff");
:
open_cache(),
controlfile("/.mergerfs"),
async_read(true),
auto_cache(false),
branches(),
cache_attr(1),
cache_entry(1),
cache_files(CacheFiles::ENUM::LIBFUSE),
cache_negative_entry(0),
cache_readdir(false),
cache_statfs(0),
cache_symlinks(false),
category(func),
direct_io(false),
dropcacheonclose(false),
fsname(),
func(),
fuse_msg_size(FUSE_MAX_MAX_PAGES),
ignorepponrename(false),
link_cow(false),
minfreespace(MINFREESPACE_DEFAULT),
mount(),
moveonenospc(false),
nullrw(false),
pid(::getpid()),
posix_acl(false),
readdirplus(false),
security_capability(true),
srcmounts(branches),
statfs(StatFS::ENUM::BASE),
statfs_ignore(StatFSIgnore::ENUM::NONE),
symlinkify(false),
symlinkify_timeout(3600),
threads(0),
version(MERGERFS_VERSION),
writeback_cache(false),
xattr(XAttr::ENUM::PASSTHROUGH)
{
_map["async_read"] = &async_read;
_map["auto_cache"] = &auto_cache;
_map["branches"] = &branches;
_map["cache.attr"] = &cache_attr;
_map["cache.entry"] = &cache_entry;
_map["cache.files"] = &cache_files;
_map["cache.negative_entry"] = &cache_negative_entry;
_map["cache.readdir"] = &cache_readdir;
_map["cache.statfs"] = &cache_statfs;
_map["cache.symlinks"] = &cache_symlinks;
_map["cache.writeback"] = &writeback_cache;
_map["category.action"] = &category.action;
_map["category.create"] = &category.create;
_map["category.search"] = &category.search;
_map["direct_io"] = &direct_io;
_map["dropcacheonclose"] = &dropcacheonclose;
_map["fsname"] = &fsname;
_map["func.access"] = &func.access;
_map["func.chmod"] = &func.chmod;
_map["func.chown"] = &func.chown;
_map["func.create"] = &func.create;
_map["func.getattr"] = &func.getattr;
_map["func.getxattr"] = &func.getxattr;
_map["func.link"] = &func.link;
_map["func.listxattr"] = &func.listxattr;
_map["func.mkdir"] = &func.mkdir;
_map["func.mknod"] = &func.mknod;
_map["func.open"] = &func.open;
_map["func.readlink"] = &func.readlink;
_map["func.removexattr"] = &func.removexattr;
_map["func.rename"] = &func.rename;
_map["func.rmdir"] = &func.rmdir;
_map["func.setxattr"] = &func.setxattr;
_map["func.symlink"] = &func.symlink;
_map["func.truncate"] = &func.truncate;
_map["func.unlink"] = &func.unlink;
_map["func.utimens"] = &func.utimens;
_map["fuse_msg_size"] = &fuse_msg_size;
_map["ignorepponrename"] = &ignorepponrename;
_map["kernel_cache"] = &kernel_cache;
_map["link_cow"] = &link_cow;
_map["minfreespace"] = &minfreespace;
_map["mount"] = &mount;
_map["moveonenospc"] = &moveonenospc;
_map["nullrw"] = &nullrw;
_map["pid"] = &pid;
_map["posix_acl"] = &posix_acl;
_map["readdirplus"] = &readdirplus;
_map["security_capability"] = &security_capability;
_map["srcmounts"] = &srcmounts;
_map["statfs"] = &statfs;
_map["statfs_ignore"] = &statfs_ignore;
_map["symlinkify"] = &symlinkify;
_map["symlinkify_timeout"] = &symlinkify_timeout;
_map["threads"] = &threads;
_map["version"] = &version;
_map["xattr"] = &xattr;
}
int
Config::set_func_policy(const string &fusefunc_,
const string &policy_)
const
Config&
Config::ro(void)
{
const Policy *policy;
const FuseFunc *fusefunc;
return *((Config*)fuse_get_context()->private_data);
}
Config&
Config::rw(void)
{
return *((Config*)fuse_get_context()->private_data);
}
fusefunc = FuseFunc::find(fusefunc_);
if(fusefunc == FuseFunc::invalid)
return (errno=ENODATA,-1);
bool
Config::has_key(const std::string &key_) const
{
return _map.count(key_);
}
policy = Policy::find(policy_);
if(policy == Policy::invalid)
return (errno=EINVAL,-1);
void
Config::keys(std::string &s_) const
{
Str2TFStrMap::const_iterator i;
Str2TFStrMap::const_iterator ei;
policies[(FuseFunc::Enum::Type)*fusefunc] = policy;
s_.reserve(512);
return 0;
i = _map.begin();
ei = _map.end();
for(; i != ei; ++i)
{
s_ += i->first;
s_ += '\0';
}
s_.resize(s_.size() - 1);
}
void
Config::keys_xattr(std::string &s_) const
{
Str2TFStrMap::const_iterator i;
Str2TFStrMap::const_iterator ei;
s_.reserve(1024);
for(i = _map.begin(), ei = _map.end(); i != ei; ++i)
{
s_ += "user.mergerfs.";
s_ += i->first;
s_ += '\0';
}
}
int
Config::set_category_policy(const string &category_,
const string &policy_)
Config::get(const std::string &key_,
std::string *val_) const
{
const Policy *policy;
const Category *category;
Str2TFStrMap::const_iterator i;
category = Category::find(category_);
if(category == Category::invalid)
return (errno=ENODATA,-1);
i = _map.find(key_);
if(i == _map.end())
return -ENOATTR;
policy = Policy::find(policy_);
if(policy == Policy::invalid)
return (errno=EINVAL,-1);
*val_ = i->second->to_string();
for(int i = 0; i < FuseFunc::Enum::END; i++)
return 0;
}
template<>
std::string
Config::StatFS::to_string() const
{
switch(_data)
{
if(FuseFunc::fusefuncs[i] == (Category::Enum::Type)*category)
policies[(FuseFunc::Enum::Type)FuseFunc::fusefuncs[i]] = policy;
case Config::StatFS::ENUM::BASE:
return "base";
case Config::StatFS::ENUM::FULL:
return "full";
}
return "invalid";
}
template<>
int
Config::StatFS::from_string(const std::string &s_)
{
if(s_ == "base")
_data = Config::StatFS::ENUM::BASE;
ef(s_ == "full")
_data = Config::StatFS::ENUM::FULL;
else
return -EINVAL;
return 0;
}
Config::CacheFiles::operator int() const
template<>
std::string
Config::StatFSIgnore::to_string() const
{
switch(_data)
{
case Config::StatFSIgnore::ENUM::NONE:
return "none";
case Config::StatFSIgnore::ENUM::RO:
return "ro";
case Config::StatFSIgnore::ENUM::NC:
return "nc";
}
return "invalid";
}
template<>
int
Config::StatFSIgnore::from_string(const std::string &s_)
{
return _data;
if(s_ == "none")
_data = Config::StatFSIgnore::ENUM::NONE;
ef(s_ == "ro")
_data = Config::StatFSIgnore::ENUM::RO;
ef(s_ == "nc")
_data = Config::StatFSIgnore::ENUM::NC;
else
return -EINVAL;
return 0;
}
Config::CacheFiles::operator std::string() const
template<>
std::string
Config::CacheFiles::to_string() const
{
switch(_data)
{
case OFF:
case Config::CacheFiles::ENUM::LIBFUSE:
return "libfuse";
case Config::CacheFiles::ENUM::OFF:
return "off";
case PARTIAL:
case Config::CacheFiles::ENUM::PARTIAL:
return "partial";
case FULL:
case Config::CacheFiles::ENUM::FULL:
return "full";
case AUTO_FULL:
case Config::CacheFiles::ENUM::AUTO_FULL:
return "auto-full";
case LIBFUSE:
return "libfuse";
case INVALID:
break;
}
return "";
return "invalid";
}
Config::CacheFiles::CacheFiles()
: _data(INVALID)
template<>
int
Config::CacheFiles::from_string(const std::string &s_)
{
if(s_ == "libfuse")
_data = Config::CacheFiles::ENUM::LIBFUSE;
ef(s_ == "off")
_data = Config::CacheFiles::ENUM::OFF;
ef(s_ == "partial")
_data = Config::CacheFiles::ENUM::PARTIAL;
ef(s_ == "full")
_data = Config::CacheFiles::ENUM::FULL;
ef(s_ == "auto-full")
_data = Config::CacheFiles::ENUM::AUTO_FULL;
else
return -EINVAL;
return 0;
}
Config::CacheFiles::CacheFiles(Config::CacheFiles::Enum data_)
: _data(data_)
template<>
std::string
Config::XAttr::to_string() const
{
switch(_data)
{
case Config::XAttr::ENUM::PASSTHROUGH:
return "passthrough";
case Config::XAttr::ENUM::NOSYS:
return "nosys";
case Config::XAttr::ENUM::NOATTR:
return "noattr";
}
return "invalid";
}
bool
Config::CacheFiles::valid() const
template<>
int
Config::XAttr::from_string(const std::string &s_)
{
return (_data != INVALID);
if(s_ == "passthrough")
_data = Config::XAttr::ENUM::PASSTHROUGH;
ef(s_ == "nosys")
_data = Config::XAttr::ENUM::NOSYS;
ef(s_ == "noattr")
_data = Config::XAttr::ENUM::NOATTR;
else
return -EINVAL;
return 0;
}
Config::CacheFiles&
Config::CacheFiles::operator=(const Config::CacheFiles::Enum data_)
int
Config::set_raw(const std::string &key_,
const std::string &value_)
{
_data = data_;
Str2TFStrMap::iterator i;
return *this;
i = _map.find(key_);
if(i == _map.end())
return -ENOATTR;
return i->second->from_string(value_);
}
Config::CacheFiles&
Config::CacheFiles::operator=(const std::string &data_)
int
Config::set(const std::string &key_,
const std::string &value_)
{
if(data_ == "off")
_data = OFF;
else if(data_ == "partial")
_data = PARTIAL;
else if(data_ == "full")
_data = FULL;
else if(data_ == "auto-full")
_data = AUTO_FULL;
else if(data_ == "libfuse")
_data = LIBFUSE;
else
_data = INVALID;
if(l::readonly(key_))
return -EINVAL;
return set_raw(key_,value_);
}
std::ostream&
operator<<(std::ostream &os_,
const Config &c_)
{
Str2TFStrMap::const_iterator i;
Str2TFStrMap::const_iterator ei;
for(i = c_._map.begin(), ei = c_._map.end(); i != ei; ++i)
{
os_ << i->first << '=' << i->second << '\n';
}
return *this;
return os_;
}

220
src/config.hpp

@ -17,9 +17,13 @@
#pragma once
#include "branch.hpp"
#include "fusefunc.hpp"
#include "enum.hpp"
#include "errno.hpp"
#include "func_category.hpp"
#include "funcs.hpp"
#include "policy.hpp"
#include "policy_cache.hpp"
#include "tofrom_wrapper.hpp"
#include <fuse.h>
@ -29,145 +33,113 @@
#include <stdint.h>
#include <sys/stat.h>
typedef ToFromWrapper<bool> ConfigBOOL;
typedef ToFromWrapper<uint64_t> ConfigUINT64;
typedef ToFromWrapper<int> ConfigINT;
typedef ToFromWrapper<std::string> ConfigSTR;
typedef std::map<std::string,ToFromString*> Str2TFStrMap;
class Config
{
public:
struct StatFS
{
enum Enum
{
BASE,
FULL
};
};
struct StatFSIgnore
{
enum Enum
{
NONE,
RO,
NC
};
};
class CacheFiles
{
public:
enum Enum
{
INVALID = -1,
LIBFUSE,
OFF,
PARTIAL,
FULL,
AUTO_FULL
};
CacheFiles();
CacheFiles(Enum);
operator int() const;
operator std::string() const;
CacheFiles& operator=(const Enum);
CacheFiles& operator=(const std::string&);
bool valid() const;
private:
Enum _data;
};
enum class StatFSEnum
{
BASE,
FULL
};
typedef Enum<StatFSEnum> StatFS;
enum class StatFSIgnoreEnum
{
NONE,
RO,
NC
};
typedef Enum<StatFSIgnoreEnum> StatFSIgnore;
enum class CacheFilesEnum
{
LIBFUSE,
OFF,
PARTIAL,
FULL,
AUTO_FULL
};
typedef Enum<CacheFilesEnum> CacheFiles;
enum class XAttrEnum
{
PASSTHROUGH = 0,
NOSYS = ENOSYS,
NOATTR = ENOATTR
};
typedef Enum<XAttrEnum> XAttr;
public:
Config();
public:
int set_func_policy(const std::string &fusefunc_,
const std::string &policy_);
int set_category_policy(const std::string &category_,
const std::string &policy_);
mutable PolicyCache open_cache;
public:
std::string fsname;
std::string destmount;
Branches branches;
mutable pthread_rwlock_t branches_lock;
uint64_t minfreespace;
bool moveonenospc;
bool direct_io;
bool kernel_cache;
bool auto_cache;
bool dropcacheonclose;
bool symlinkify;
time_t symlinkify_timeout;
bool nullrw;
bool ignorepponrename;
bool security_capability;
bool link_cow;
int xattr;
StatFS::Enum statfs;
StatFSIgnore::Enum statfs_ignore;
bool posix_acl;
bool cache_symlinks;
bool cache_readdir;
bool async_read;
bool writeback_cache;
bool readdirplus;
CacheFiles cache_files;
uint16_t fuse_msg_size;
const std::string controlfile;
public:
const Policy *policies[FuseFunc::Enum::END];
const Policy *&access;
const Policy *&chmod;
const Policy *&chown;
const Policy *&create;
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;
ConfigBOOL async_read;
ConfigBOOL auto_cache;
Branches branches;
ConfigUINT64 cache_attr;
ConfigUINT64 cache_entry;
CacheFiles cache_files;
ConfigUINT64 cache_negative_entry;
ConfigBOOL cache_readdir;
ConfigUINT64 cache_statfs;
ConfigBOOL cache_symlinks;
FuncCategories category;
ConfigBOOL direct_io;
ConfigBOOL dropcacheonclose;
ConfigSTR fsname;
Funcs func;
ConfigUINT64 fuse_msg_size;
ConfigBOOL ignorepponrename;
ConfigBOOL kernel_cache;
ConfigBOOL link_cow;
ConfigUINT64 minfreespace;
ConfigSTR mount;
ConfigBOOL moveonenospc;
ConfigBOOL nullrw;
ConfigUINT64 pid;
ConfigBOOL posix_acl;
ConfigBOOL readdirplus;
ConfigBOOL security_capability;
SrcMounts srcmounts;
StatFS statfs;
StatFSIgnore statfs_ignore;
ConfigBOOL symlinkify;
ConfigUINT64 symlinkify_timeout;
ConfigINT threads;
ConfigSTR version;
ConfigBOOL writeback_cache;
XAttr xattr;
public:
mutable PolicyCache open_cache;
friend std::ostream& operator<<(std::ostream &s,
const Config &c);
public:
const std::string controlfile;
bool has_key(const std::string &key) const;
void keys(std::string &s) const;
void keys_xattr(std::string &s) const;
public:
int get(const std::string &key, std::string *val) const;
int set_raw(const std::string &key, const std::string &val);
int set(const std::string &key, const std::string &val);
public:
static
const
Config &
get(void)
{
const fuse_context *fc = fuse_get_context();
return get(fc);
}
static
const Config &
get(const fuse_context *fc)
{
return *((Config*)fc->private_data);
}
static
Config &
get_writable(void)
{
return (*((Config*)fuse_get_context()->private_data));
}
static const Config &ro(void);
static Config &rw(void);
private:
Str2TFStrMap _map;
};

9
src/dirinfo.hpp

@ -16,16 +16,15 @@
#pragma once
#include "fh.hpp"
#include <string>
class DirInfo
class DirInfo : public FH
{
public:
DirInfo(const char *fusepath_)
: fusepath(fusepath_)
: FH(fusepath_)
{
}
public:
std::string fusepath;
};

21
src/ef.hpp

@ -0,0 +1,21 @@
/*
ISC License
Copyright (c) 2019, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
#define ef(X) else if(X)

78
src/enum.hpp

@ -0,0 +1,78 @@
/*
ISC License
Copyright (c) 2019, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "tofrom_string.hpp"
#include <string>
template<typename E>
class Enum : public ToFromString
{
public:
typedef E ENUM;
public:
Enum<ENUM>()
{
}
Enum<ENUM>(const ENUM data_)
: _data(data_)
{
}
public:
Enum<ENUM>&
operator=(const ENUM data_)
{
_data = data_;
return *this;
}
Enum<ENUM>&
operator=(const std::string &s_)
{
from_string(s_);
return *this;
}
public:
operator ENUM() const
{
return _data;
}
public:
bool operator==(const ENUM data_) const
{
return (_data == data_);
}
public:
std::string to_string() const;
int from_string(const std::string &);
public:
int to_int() const
{
return (int)_data;
}
private:
ENUM _data;
};

31
src/fh.hpp

@ -0,0 +1,31 @@
/*
Copyright (c) 2020, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
#include <string>
class FH
{
public:
FH(const char *fusepath_)
: fusepath(fusepath_)
{
}
public:
std::string fusepath;
};

9
src/fileinfo.hpp

@ -16,19 +16,20 @@
#pragma once
#include "fh.hpp"
#include <string>
class FileInfo
class FileInfo : public FH
{
public:
FileInfo(const int fd_,
const char *fusepath_)
: fd(fd_),
fusepath(fusepath_)
: FH(fusepath_),
fd(fd_)
{
}
public:
int fd;
std::string fusepath;
};

115
src/from_string.cpp

@ -0,0 +1,115 @@
/*
ISC License
Copyright (c) 2019, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "ef.hpp"
#include <string>
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
namespace str
{
int
from(const std::string &value_,
bool *bool_)
{
if((value_ == "true") ||
(value_ == "1") ||
(value_ == "on") ||
(value_ == "yes"))
*bool_ = true;
ef((value_ == "false") ||
(value_ == "0") ||
(value_ == "off") ||
(value_ == "no"))
*bool_ = false;
else
return -EINVAL;
return 0;
}
int
from(const std::string &value_,
int *int_)
{
*int_ = ::strtol(value_.c_str(),NULL,10);
return 0;
}
int
from(const std::string &value_,
uint64_t *uint64_)
{
char *endptr;
uint64_t tmp;
tmp = ::strtoll(value_.c_str(),&endptr,10);
switch(*endptr)
{
case 'k':
case 'K':
tmp *= 1024;
break;
case 'm':
case 'M':
tmp *= (1024 * 1024);
break;
case 'g':
case 'G':
tmp *= (1024 * 1024 * 1024);
break;
case 't':
case 'T':
tmp *= (1024ULL * 1024 * 1024 * 1024);
break;
case '\0':
break;
default:
return -EINVAL;
}
*uint64_ = tmp;
return 0;
}
int
from(const std::string &value_,
std::string *str_)
{
*str_ = value_;
return 0;
}
int
from(const std::string &value_,
const std::string *key_)
{
return -EINVAL;
}
}

32
src/from_string.hpp

@ -0,0 +1,32 @@
/*
ISC License
Copyright (c) 2019, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
#include <string>
#include <stdint.h>
namespace str
{
int from(const std::string &, bool *);
int from(const std::string &, int *);
int from(const std::string &, uint64_t *);
int from(const std::string &, std::string *);
int from(const std::string &, const std::string *);
}

39
src/func.cpp

@ -0,0 +1,39 @@
/*
ISC License
Copyright (c) 2019, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "func.hpp"
int
Func::from_string(const std::string &s_)
{
const Policy *tmp;
tmp = &Policy::find(s_);
if(tmp == Policy::invalid)
return -EINVAL;
policy = tmp;
return 0;
}
std::string
Func::to_string(void) const
{
return policy->to_string();
}

221
src/func.hpp

@ -0,0 +1,221 @@
/*
ISC License
Copyright (c) 2019, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
#include "policy.hpp"
#include "tofrom_string.hpp"
#include <string>
#include <iostream>
class Func : public ToFromString
{
public:
Func(const Policy &policy_)
: policy(&policy_)
{
}
public:
int from_string(const std::string &s);
std::string to_string() const;
public:
const Policy *policy;
};
class FuncAccess : public Func
{
public:
FuncAccess()
: Func(Policy::ff)
{
}
};
class FuncChmod : public Func
{
public:
FuncChmod()
: Func(Policy::epall)
{
}
};
class FuncChown : public Func
{
public:
FuncChown()
: Func(Policy::epall)
{
}
};
class FuncCreate : public Func
{
public:
FuncCreate()
: Func(Policy::epmfs)
{
}
};
class FuncGetAttr : public Func
{
public:
FuncGetAttr()
: Func(Policy::ff)
{
}
};
class FuncGetXAttr : public Func
{
public:
FuncGetXAttr()
: Func(Policy::ff)
{
}
};
class FuncLink : public Func
{
public:
FuncLink()
: Func(Policy::epall)
{
}
};
class FuncListXAttr : public Func
{
public:
FuncListXAttr()
: Func(Policy::ff)
{
}
};
class FuncMkdir : public Func
{
public:
FuncMkdir()
: Func(Policy::epmfs)
{
}
};
class FuncMknod : public Func
{
public:
FuncMknod()
: Func(Policy::epmfs)
{
}
};
class FuncOpen : public Func
{
public:
FuncOpen()
: Func(Policy::ff)
{
}
};
class FuncReadlink : public Func
{
public:
FuncReadlink()
: Func(Policy::ff)
{
}
};
class FuncRemoveXAttr : public Func
{
public:
FuncRemoveXAttr()
: Func(Policy::epall)
{
}
};
class FuncRename : public Func
{
public:
FuncRename()
: Func(Policy::epall)
{
}
};
class FuncRmdir : public Func
{
public:
FuncRmdir()
: Func(Policy::epall)
{
}
};
class FuncSetXAttr : public Func
{
public:
FuncSetXAttr()
: Func(Policy::epall)
{
}
};
class FuncSymlink : public Func
{
public:
FuncSymlink()
: Func(Policy::epmfs)
{
}
};
class FuncTruncate : public Func
{
public:
FuncTruncate()
: Func(Policy::epall)
{
}
};
class FuncUnlink : public Func
{
public:
FuncUnlink()
: Func(Policy::epall)
{
}
};
class FuncUtimens : public Func
{
public:
FuncUtimens()
: Func(Policy::epall)
{
}
};

90
src/func_category.cpp

@ -0,0 +1,90 @@
/*
ISC License
Copyright (c) 2019, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "func_category.hpp"
#include "str.hpp"
#include "buildvector.hpp"
#include <algorithm>
#include <string>
#include <vector>
int
FuncCategory::from_string(const std::string &s_)
{
const Policy *tmp;
tmp = &Policy::find(s_);
if(tmp == &Policy::invalid)
return -EINVAL;
for(uint64_t i = 0; i < _funcs.size(); i++)
_funcs[i]->policy = tmp;
return 0;
}
std::string
FuncCategory::to_string(void) const
{
std::vector<std::string> rv;
for(uint64_t i = 0; i < _funcs.size(); i++)
rv.push_back(_funcs[i]->policy->to_string());
std::sort(rv.begin(),rv.end());
rv.erase(std::unique(rv.begin(),rv.end()),rv.end());
return str::join(rv,',');
}
FuncCategoryCreate::FuncCategoryCreate(Funcs &funcs_)
{
_funcs = buildvector<Func*>
(&funcs_.create)
(&funcs_.mkdir)
(&funcs_.mknod)
(&funcs_.symlink);
}
FuncCategoryAction::FuncCategoryAction(Funcs &funcs_)
{
_funcs = buildvector<Func*>
(&funcs_.chmod)
(&funcs_.chown)
(&funcs_.link)
(&funcs_.removexattr)
(&funcs_.rename)
(&funcs_.rmdir)
(&funcs_.setxattr)
(&funcs_.truncate)
(&funcs_.unlink)
(&funcs_.utimens);
}
FuncCategorySearch::FuncCategorySearch(Funcs &funcs_)
{
_funcs = buildvector<Func*>
(&funcs_.access)
(&funcs_.getattr)
(&funcs_.getxattr)
(&funcs_.listxattr)
(&funcs_.open)
(&funcs_.readlink);
}

69
src/func_category.hpp

@ -0,0 +1,69 @@
/*
ISC License
Copyright (c) 2019, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
#include "funcs.hpp"
#include "tofrom_string.hpp"
#include <string>
#include <vector>
class FuncCategory : public ToFromString
{
public:
int from_string(const std::string &);
std::string to_string(void) const;
protected:
std::vector<Func*> _funcs;
};
class FuncCategoryCreate : public FuncCategory
{
public:
FuncCategoryCreate(Funcs &funcs_);
};
class FuncCategoryAction : public FuncCategory
{
public:
FuncCategoryAction(Funcs &funcs_);
};
class FuncCategorySearch : public FuncCategory
{
public:
FuncCategorySearch(Funcs &funcs_);
};
class FuncCategories
{
public:
FuncCategories(Funcs &funcs_)
: action(funcs_),
create(funcs_),
search(funcs_)
{
}
public:
FuncCategoryAction action;
FuncCategoryCreate create;
FuncCategorySearch search;
};

46
src/funcs.hpp

@ -0,0 +1,46 @@
/*
ISC License
Copyright (c) 2019, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
#include "func.hpp"
class Funcs
{
public:
FuncAccess access;
FuncChmod chmod;
FuncChown chown;
FuncCreate create;
FuncGetAttr getattr;
FuncGetXAttr getxattr;
FuncLink link;
FuncListXAttr listxattr;
FuncMkdir mkdir;
FuncMknod mknod;
FuncOpen open;
FuncReadlink readlink;
FuncRemoveXAttr removexattr;
FuncRename rename;
FuncRmdir rmdir;
FuncSetXAttr setxattr;
FuncSymlink symlink;
FuncTruncate truncate;
FuncUnlink unlink;
FuncUtimens utimens;
};

20
src/fuse_access.cpp

@ -14,16 +14,15 @@
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <string>
#include <vector>
#include "config.hpp"
#include "errno.hpp"
#include "fs_base_access.hpp"
#include "fs_path.hpp"
#include "rwlock.hpp"
#include "ugid.hpp"
#include <string>
#include <vector>
using std::string;
using std::vector;
@ -39,9 +38,9 @@ namespace l
{
int rv;
string fullpath;
vector<const string*> basepaths;
vector<string> basepaths;
rv = searchFunc(branches_,fusepath,minfreespace,basepaths);
rv = searchFunc(branches_,fusepath,minfreespace,&basepaths);
if(rv == -1)
return -errno;
@ -59,12 +58,11 @@ namespace FUSE
access(const char *fusepath,
int mask)
{
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.branches_lock);
const fuse_context *fc = fuse_get_context();
const Config &config = Config::ro();
const ugid::Set ugid(fc->uid,fc->gid);
return l::access(config.access,
return l::access(config.func.access.policy,
config.branches,
config.minfreespace,
fusepath,

22
src/fuse_chmod.cpp

@ -19,7 +19,6 @@
#include "fs_base_chmod.hpp"
#include "fs_path.hpp"
#include "rv.hpp"
#include "rwlock.hpp"
#include "ugid.hpp"
#include <fuse.h>
@ -34,7 +33,7 @@ namespace l
{
static
int
chmod_loop_core(const string *basepath_,
chmod_loop_core(const string &basepath_,
const char *fusepath_,
const mode_t mode_,
const int error_)
@ -51,9 +50,9 @@ namespace l
static
int
chmod_loop(const vector<const string*> &basepaths_,
const char *fusepath_,
const mode_t mode_)
chmod_loop(const vector<string> &basepaths_,
const char *fusepath_,
const mode_t mode_)
{
int error;
@ -75,9 +74,9 @@ namespace l
const mode_t mode_)
{
int rv;
vector<const string*> basepaths;
vector<string> basepaths;
rv = actionFunc_(branches_,fusepath_,minfreespace_,basepaths);
rv = actionFunc_(branches_,fusepath_,minfreespace_,&basepaths);
if(rv == -1)
return -errno;
@ -91,12 +90,11 @@ namespace FUSE
chmod(const char *fusepath_,
mode_t mode_)
{
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.branches_lock);
const fuse_context *fc = fuse_get_context();
const Config &config = Config::ro();
const ugid::Set ugid(fc->uid,fc->gid);
return l::chmod(config.chmod,
return l::chmod(config.func.chmod.policy,
config.branches,
config.minfreespace,
fusepath_,

24
src/fuse_chown.cpp

@ -19,7 +19,6 @@
#include "fs_base_chown.hpp"
#include "fs_path.hpp"
#include "rv.hpp"
#include "rwlock.hpp"
#include "ugid.hpp"
#include <fuse.h>
@ -34,7 +33,7 @@ namespace l
{
static
int
chown_loop_core(const string *basepath_,
chown_loop_core(const string &basepath_,
const char *fusepath_,
const uid_t uid_,
const gid_t gid_,
@ -52,10 +51,10 @@ namespace l
static
int
chown_loop(const vector<const string*> &basepaths_,
const char *fusepath_,
const uid_t uid_,
const gid_t gid_)
chown_loop(const vector<string> &basepaths_,
const char *fusepath_,
const uid_t uid_,
const gid_t gid_)
{
int error;
@ -78,9 +77,9 @@ namespace l
const gid_t gid_)
{
int rv;
vector<const string*> basepaths;
vector<string> basepaths;
rv = actionFunc_(branches_,fusepath_,minfreespace_,basepaths);
rv = actionFunc_(branches_,fusepath_,minfreespace_,&basepaths);
if(rv == -1)
return -errno;
@ -95,12 +94,11 @@ namespace FUSE
uid_t uid_,
gid_t gid_)
{
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.branches_lock);
const fuse_context *fc = fuse_get_context();
const Config &config = Config::ro();
const ugid::Set ugid(fc->uid,fc->gid);
return l::chown(config.chown,
return l::chown(config.func.chown.policy,
config.branches,
config.minfreespace,
fusepath_,

34
src/fuse_create.cpp

@ -21,7 +21,6 @@
#include "fs_base_open.hpp"
#include "fs_clonepath.hpp"
#include "fs_path.hpp"
#include "rwlock.hpp"
#include "ugid.hpp"
#include <fuse.h>
@ -61,27 +60,27 @@ namespace l
{
switch(config_.cache_files)
{
case CacheFiles::LIBFUSE:
case CacheFiles::ENUM::LIBFUSE:
ffi_->direct_io = config_.direct_io;
ffi_->keep_cache = config_.kernel_cache;
ffi_->auto_cache = config_.auto_cache;
break;
case CacheFiles::OFF:
case CacheFiles::ENUM::OFF:
ffi_->direct_io = 1;
ffi_->keep_cache = 0;
ffi_->auto_cache = 0;
break;
case CacheFiles::PARTIAL:
case CacheFiles::ENUM::PARTIAL:
ffi_->direct_io = 0;
ffi_->keep_cache = 0;
ffi_->auto_cache = 0;
break;
case CacheFiles::FULL:
case CacheFiles::ENUM::FULL:
ffi_->direct_io = 0;
ffi_->keep_cache = 1;
ffi_->auto_cache = 0;
break;
case CacheFiles::AUTO_FULL:
case CacheFiles::ENUM::AUTO_FULL:
ffi_->direct_io = 0;
ffi_->keep_cache = 0;
ffi_->auto_cache = 1;
@ -140,24 +139,24 @@ namespace l
int rv;
string fullpath;
string fusedirpath;
vector<const string*> createpaths;
vector<const string*> existingpaths;
vector<string> createpaths;
vector<string> existingpaths;
fusedirpath = fs::path::dirname(fusepath_);
rv = searchFunc_(branches_,fusedirpath,minfreespace_,existingpaths);
rv = searchFunc_(branches_,fusedirpath,minfreespace_,&existingpaths);
if(rv == -1)
return -errno;
rv = createFunc_(branches_,fusedirpath,minfreespace_,createpaths);
rv = createFunc_(branches_,fusedirpath,minfreespace_,&createpaths);
if(rv == -1)
return -errno;
rv = fs::clonepath_as_root(*existingpaths[0],*createpaths[0],fusedirpath);
rv = fs::clonepath_as_root(existingpaths[0],createpaths[0],fusedirpath);
if(rv == -1)
return -errno;
return l::create_core(*createpaths[0],
return l::create_core(createpaths[0],
fusepath_,
mode_,
umask_,
@ -173,18 +172,17 @@ namespace FUSE
mode_t mode_,
fuse_file_info *ffi_)
{
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.branches_lock);
const fuse_context *fc = fuse_get_context();
const Config &config = Config::ro();
const ugid::Set ugid(fc->uid,fc->gid);
l::config_to_ffi_flags(config,ffi_);
if(config.writeback_cache)
l::tweak_flags_writeback_cache(&ffi_->flags);
return l::create(config.getattr,
config.create,
return l::create(config.func.getattr.policy,
config.func.create.policy,
config.branches,
config.minfreespace,
fusepath_,

20
src/fuse_fgetattr.cpp

@ -14,6 +14,7 @@
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.hpp"
#include "errno.hpp"
#include "fileinfo.hpp"
#include "fs_base_stat.hpp"
@ -43,13 +44,22 @@ namespace l
namespace FUSE
{
int
fgetattr(const char *fusepath_,
struct stat *st_,
fuse_file_info *ffi_)
fgetattr(const char *fusepath_,
struct stat *st_,
fuse_file_info *ffi_,
fuse_timeouts_t *timeout_)
{
int rv;
const Config &config = Config::ro();
FileInfo *fi = reinterpret_cast<FileInfo*>(ffi_->fh);
return l::fgetattr(fi->fd,st_);
rv = l::fgetattr(fi->fd,st_);
timeout_->entry = ((rv >= 0) ?
config.cache_entry :
config.cache_negative_entry);
timeout_->attr = config.cache_attr;
return rv;
}
}

7
src/fuse_fgetattr.hpp

@ -25,7 +25,8 @@
namespace FUSE
{
int
fgetattr(const char *fusepath_,
struct stat *st_,
fuse_file_info *ffi_);
fgetattr(const char *fusepath,
struct stat *st,
fuse_file_info *ffi,
fuse_timeouts_t *timeout);
}

39
src/fuse_getattr.cpp

@ -19,7 +19,6 @@
#include "fs_base_stat.hpp"
#include "fs_inode.hpp"
#include "fs_path.hpp"
#include "rwlock.hpp"
#include "symlinkify.hpp"
#include "ugid.hpp"
@ -70,9 +69,9 @@ namespace l
{
int rv;
string fullpath;
vector<const string*> basepaths;
vector<string> basepaths;
rv = searchFunc_(branches_,fusepath_,minfreespace_,basepaths);
rv = searchFunc_(branches_,fusepath_,minfreespace_,&basepaths);
if(rv == -1)
return -errno;
@ -94,24 +93,32 @@ namespace l
namespace FUSE
{
int
getattr(const char *fusepath_,
struct stat *st_)
getattr(const char *fusepath_,
struct stat *st_,
fuse_timeouts_t *timeout_)
{
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
int rv;
const Config &config = Config::ro();
if(fusepath_ == config.controlfile)
return l::getattr_controlfile(st_);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.branches_lock);
const fuse_context *fc = fuse_get_context();
const ugid::Set ugid(fc->uid,fc->gid);
rv = l::getattr(config.func.getattr.policy,
config.branches,
config.minfreespace,
fusepath_,
st_,
config.symlinkify,
config.symlinkify_timeout);
timeout_->entry = ((rv >= 0) ?
config.cache_entry :
config.cache_negative_entry);
timeout_->attr = config.cache_attr;
return l::getattr(config.getattr,
config.branches,
config.minfreespace,
fusepath_,
st_,
config.symlinkify,
config.symlinkify_timeout);
return rv;
}
}

5
src/fuse_getattr.hpp

@ -23,6 +23,7 @@
namespace FUSE
{
int
getattr(const char *fusepath_,
struct stat *buf_);
getattr(const char *fusepath,
struct stat *buf,
fuse_timeouts_t *timeout);
}

364
src/fuse_getxattr.cpp

@ -19,7 +19,6 @@
#include "fs_base_getxattr.hpp"
#include "fs_path.hpp"
#include "fs_statvfs_cache.hpp"
#include "rwlock.hpp"
#include "str.hpp"
#include "ugid.hpp"
#include "version.hpp"
@ -27,7 +26,6 @@
#include <fuse.h>
#include <algorithm>
#include <set>
#include <sstream>
#include <string>
#include <vector>
@ -39,7 +37,6 @@ static const char SECURITY_CAPABILITY[] = "security.capability";
using std::string;
using std::vector;
using std::set;
namespace l
{
@ -64,254 +61,6 @@ namespace l
return ((rv == -1) ? -errno : rv);
}
static
void
getxattr_controlfile_fusefunc_policy(const Config &config_,
const string &attr_,
string &attrvalue_)
{
FuseFunc fusefunc;
fusefunc = FuseFunc::find(attr_);
if(fusefunc != FuseFunc::invalid)
attrvalue_ = (std::string)*config_.policies[(FuseFunc::Enum::Type)*fusefunc];
}
static
void
getxattr_controlfile_category_policy(const Config &config_,
const string &attr_,
string &attrvalue_)
{
Category cat;
cat = Category::find(attr_);
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_ = config_.branches.to_string();
}
static
void
getxattr_controlfile_branches(const Config &config_,
string &attrvalue_)
{
attrvalue_ = config_.branches.to_string(true);
}
static
void
getxattr_controlfile_uint64_t(const uint64_t uint_,
string &attrvalue_)
{
std::ostringstream os;
os << uint_;
attrvalue_ = os.str();
}
static
void
getxattr_controlfile_double(const double d_,
string &attrvalue_)
{
std::ostringstream os;
os << d_;
attrvalue_ = os.str();
}
static
void
getxattr_controlfile_time_t(const time_t time,
string &attrvalue)
{
std::ostringstream os;
os << time;
attrvalue = os.str();
}
static
void
getxattr_controlfile_bool(const bool boolvalue,
string &attrvalue)
{
attrvalue = (boolvalue ? "true" : "false");
}
static
void
getxattr_controlfile_errno(const int errno_,
string &attrvalue)
{
switch(errno_)
{
case 0:
attrvalue = "passthrough";
break;
case ENOATTR:
attrvalue = "noattr";
break;
case ENOSYS:
attrvalue = "nosys";
break;
default:
attrvalue = "ERROR";
break;
}
}
static
void
getxattr_controlfile_statfs(const Config::StatFS::Enum enum_,
string &attrvalue_)
{
switch(enum_)
{
case Config::StatFS::BASE:
attrvalue_ = "base";
break;
case Config::StatFS::FULL:
attrvalue_ = "full";
break;
default:
attrvalue_ = "ERROR";
break;
}
}
static
void
getxattr_controlfile_statfsignore(const Config::StatFSIgnore::Enum enum_,
string &attrvalue_)
{
switch(enum_)
{
case Config::StatFSIgnore::NONE:
attrvalue_ = "none";
break;
case Config::StatFSIgnore::RO:
attrvalue_ = "ro";
break;
case Config::StatFSIgnore::NC:
attrvalue_ = "nc";
break;
default:
attrvalue_ = "ERROR";
break;
}
}
static
void
getxattr_controlfile(const Config::CacheFiles &cache_files_,
string &attrvalue_)
{
attrvalue_ = (string)cache_files_;
}
static
void
getxattr_controlfile(const uint16_t &uint16_,
string &attrvalue_)
{
std::ostringstream os;
os << uint16_;
attrvalue_ = os.str();
}
static
void
getxattr_controlfile_policies(const Config &config,
string &attrvalue)
{
size_t i = Policy::Enum::begin();
attrvalue = (string)Policy::policies[i];
for(i++; i < Policy::Enum::end(); i++)
attrvalue += ',' + (string)Policy::policies[i];
}
static
void
getxattr_controlfile_version(string &attrvalue)
{
attrvalue = MERGERFS_VERSION;
if(attrvalue.empty())
attrvalue = "unknown_possible_problem_with_build";
}
static
void
getxattr_controlfile_pid(string &attrvalue)
{
int pid;
char buf[32];
pid = getpid();
snprintf(buf,sizeof(buf),"%d",pid);
attrvalue = buf;
}
static
void
getxattr_controlfile_cache_attr(string &attrvalue)
{
double d;
d = fuse_config_get_attr_timeout(fuse_get_context()->fuse);
l::getxattr_controlfile_double(d,attrvalue);
}
static
void
getxattr_controlfile_cache_entry(string &attrvalue)
{
double d;
d = fuse_config_get_entry_timeout(fuse_get_context()->fuse);
l::getxattr_controlfile_double(d,attrvalue);
}
static
void
getxattr_controlfile_cache_negative_entry(string &attrvalue)
{
double d;
d = fuse_config_get_negative_entry_timeout(fuse_get_context()->fuse);
l::getxattr_controlfile_double(d,attrvalue);
}
static
int
getxattr_controlfile(const Config &config,
@ -319,93 +68,21 @@ namespace l
char *buf,
const size_t count)
{
int rv;
size_t len;
string attrvalue;
string key;
string val;
vector<string> attr;
str::split(attr,attrname,'.');
if((attr[0] != "user") || (attr[1] != "mergerfs"))
if(!str::startswith(attrname,"user.mergerfs."))
return -ENOATTR;
switch(attr.size())
{
case 3:
if(attr[2] == "srcmounts")
l::getxattr_controlfile_srcmounts(config,attrvalue);
else if(attr[2] == "branches")
l::getxattr_controlfile_branches(config,attrvalue);
else if(attr[2] == "minfreespace")
l::getxattr_controlfile_uint64_t(config.minfreespace,attrvalue);
else if(attr[2] == "moveonenospc")
l::getxattr_controlfile_bool(config.moveonenospc,attrvalue);
else if(attr[2] == "dropcacheonclose")
l::getxattr_controlfile_bool(config.dropcacheonclose,attrvalue);
else if(attr[2] == "symlinkify")
l::getxattr_controlfile_bool(config.symlinkify,attrvalue);
else if(attr[2] == "symlinkify_timeout")
l::getxattr_controlfile_time_t(config.symlinkify_timeout,attrvalue);
else if(attr[2] == "nullrw")
l::getxattr_controlfile_bool(config.nullrw,attrvalue);
else if(attr[2] == "ignorepponrename")
l::getxattr_controlfile_bool(config.ignorepponrename,attrvalue);
else if(attr[2] == "security_capability")
l::getxattr_controlfile_bool(config.security_capability,attrvalue);
else if(attr[2] == "xattr")
l::getxattr_controlfile_errno(config.xattr,attrvalue);
else if(attr[2] == "link_cow")
l::getxattr_controlfile_bool(config.link_cow,attrvalue);
else if(attr[2] == "statfs")
l::getxattr_controlfile_statfs(config.statfs,attrvalue);
else if(attr[2] == "statfs_ignore")
l::getxattr_controlfile_statfsignore(config.statfs_ignore,attrvalue);
else if(attr[2] == "policies")
l::getxattr_controlfile_policies(config,attrvalue);
else if(attr[2] == "version")
l::getxattr_controlfile_version(attrvalue);
else if(attr[2] == "pid")
l::getxattr_controlfile_pid(attrvalue);
else if(attr[2] == "direct_io")
l::getxattr_controlfile_bool(config.direct_io,attrvalue);
else if(attr[2] == "posix_acl")
l::getxattr_controlfile_bool(config.posix_acl,attrvalue);
else if(attr[2] == "async_read")
l::getxattr_controlfile_bool(config.async_read,attrvalue);
else if(attr[2] == "fuse_msg_size")
l::getxattr_controlfile(config.fuse_msg_size,attrvalue);
else if(attr[2] == "readdirplus")
l::getxattr_controlfile_bool(config.readdirplus,attrvalue);
break;
case 4:
if(attr[2] == "category")
l::getxattr_controlfile_category_policy(config,attr[3],attrvalue);
else if(attr[2] == "func")
l::getxattr_controlfile_fusefunc_policy(config,attr[3],attrvalue);
else if((attr[2] == "cache") && (attr[3] == "open"))
l::getxattr_controlfile_uint64_t(config.open_cache.timeout,attrvalue);
else if((attr[2] == "cache") && (attr[3] == "statfs"))
l::getxattr_controlfile_uint64_t(fs::statvfs_cache_timeout(),attrvalue);
else if((attr[2] == "cache") && (attr[3] == "attr"))
l::getxattr_controlfile_cache_attr(attrvalue);
else if((attr[2] == "cache") && (attr[3] == "entry"))
l::getxattr_controlfile_cache_entry(attrvalue);
else if((attr[2] == "cache") && (attr[3] == "negative_entry"))
l::getxattr_controlfile_cache_negative_entry(attrvalue);
else if((attr[2] == "cache") && (attr[3] == "symlinks"))
l::getxattr_controlfile_bool(config.cache_symlinks,attrvalue);
else if((attr[2] == "cache") && (attr[3] == "readdir"))
l::getxattr_controlfile_bool(config.cache_readdir,attrvalue);
else if((attr[2] == "cache") && (attr[3] == "files"))
l::getxattr_controlfile(config.cache_files,attrvalue);
else if((attr[2] == "cache") && (attr[3] == "writeback"))
l::getxattr_controlfile_bool(config.writeback_cache,attrvalue);
break;
}
if(attrvalue.empty())
return -ENOATTR;
key = &attrname[14];
rv = config.get(key,&val);
if(rv < 0)
return rv;
len = attrvalue.size();
len = val.size();
if(count == 0)
return len;
@ -413,7 +90,7 @@ namespace l
if(count < len)
return -ERANGE;
memcpy(buf,attrvalue.c_str(),len);
memcpy(buf,val.c_str(),len);
return (int)len;
}
@ -495,16 +172,16 @@ namespace l
{
int rv;
string fullpath;
vector<const string*> basepaths;
vector<string> basepaths;
rv = searchFunc(branches_,fusepath,minfreespace,basepaths);
rv = searchFunc(branches_,fusepath,minfreespace,&basepaths);
if(rv == -1)
return -errno;
fullpath = fs::path::make(basepaths[0],fusepath);
if(str::isprefix(attrname,"user.mergerfs."))
return l::getxattr_user_mergerfs(*basepaths[0],
if(str::startswith(attrname,"user.mergerfs."))
return l::getxattr_user_mergerfs(basepaths[0],
fusepath,
fullpath,
branches_,
@ -524,8 +201,7 @@ namespace FUSE
char *buf,
size_t count)
{
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const Config &config = Config::ro();
if(fusepath == config.controlfile)
return l::getxattr_controlfile(config,
@ -537,13 +213,13 @@ namespace FUSE
l::is_attrname_security_capability(attrname))
return -ENOATTR;
if(config.xattr)
return -config.xattr;
if(config.xattr.to_int())
return -config.xattr.to_int();
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.branches_lock);
const fuse_context *fc = fuse_get_context();
const ugid::Set ugid(fc->uid,fc->gid);
return l::getxattr(config.getxattr,
return l::getxattr(config.func.getxattr.policy,
config.branches,
config.minfreespace,
fusepath,

18
src/fuse_init.cpp

@ -50,7 +50,7 @@ namespace l
void
want_if_capable(fuse_conn_info *conn_,
const int flag_,
bool *want_)
ConfigBOOL *want_)
{
if(*want_ && l::capable(conn_,flag_))
{
@ -83,24 +83,24 @@ namespace FUSE
void *
init(fuse_conn_info *conn_)
{
Config &c = Config::get_writable();
Config &config = Config::rw();
ugid::init();
l::want_if_capable(conn_,FUSE_CAP_ASYNC_DIO);
l::want_if_capable(conn_,FUSE_CAP_ASYNC_READ,&c.async_read);
l::want_if_capable(conn_,FUSE_CAP_ASYNC_READ,&config.async_read);
l::want_if_capable(conn_,FUSE_CAP_ATOMIC_O_TRUNC);
l::want_if_capable(conn_,FUSE_CAP_BIG_WRITES);
l::want_if_capable(conn_,FUSE_CAP_CACHE_SYMLINKS,&c.cache_symlinks);
l::want_if_capable(conn_,FUSE_CAP_CACHE_SYMLINKS,&config.cache_symlinks);
l::want_if_capable(conn_,FUSE_CAP_DONT_MASK);
l::want_if_capable(conn_,FUSE_CAP_IOCTL_DIR);
l::want_if_capable(conn_,FUSE_CAP_PARALLEL_DIROPS);
l::want_if_capable(conn_,FUSE_CAP_READDIR_PLUS,&c.readdirplus);
l::want_if_capable(conn_,FUSE_CAP_READDIR_PLUS,&config.readdirplus);
//l::want_if_capable(conn_,FUSE_CAP_READDIR_PLUS_AUTO);
l::want_if_capable(conn_,FUSE_CAP_POSIX_ACL,&c.posix_acl);
l::want_if_capable(conn_,FUSE_CAP_WRITEBACK_CACHE,&c.writeback_cache);
l::want_if_capable_max_pages(conn_,c);
l::want_if_capable(conn_,FUSE_CAP_POSIX_ACL,&config.posix_acl);
l::want_if_capable(conn_,FUSE_CAP_WRITEBACK_CACHE,&config.writeback_cache);
l::want_if_capable_max_pages(conn_,config);
return &c;
return &config;
}
}

239
src/fuse_ioctl.cpp

@ -23,7 +23,7 @@
#include "fs_base_ioctl.hpp"
#include "fs_base_open.hpp"
#include "fs_path.hpp"
#include "rwlock.hpp"
#include "str.hpp"
#include "ugid.hpp"
#include <fuse.h>
@ -32,10 +32,23 @@
#include <vector>
#include <fcntl.h>
#include <string.h>
using std::string;
using std::vector;
typedef char IOCTL_BUF[4096];
#define IOCTL_APP_TYPE 0xDF
//#define IOCTL_READ_KEYS 0xD000DF00
//#define IOCTL_READ_VAL 0xD000DF01
//#define IOCTL_WRITE_VAL 0x5000DF02
//#define IOCTL_FILE_INFO 0xD000DF03
#define IOCTL_READ_KEYS _IOWR(IOCTL_APP_TYPE,0,IOCTL_BUF)
#define IOCTL_READ_VAL _IOWR(IOCTL_APP_TYPE,1,IOCTL_BUF)
#define IOCTL_WRITE_VAL _IOW(IOCTL_APP_TYPE,2,IOCTL_BUF)
#define IOCTL_FILE_INFO _IOWR(IOCTL_APP_TYPE,3,IOCTL_BUF)
#ifndef FS_IOC_GETFLAGS
# define FS_IOC_GETFLAGS _IOR('f',1,long)
#endif
@ -69,7 +82,7 @@ using std::vector;
is not possible to safely handle the situation.
https://lwn.net/Articles/575846/
*/
*/
namespace l
{
@ -107,8 +120,8 @@ namespace l
void *data_,
uint32_t *out_bufsz_)
{
FileInfo *fi = reinterpret_cast<FileInfo*>(ffi_->fh);
const fuse_context *fc = fuse_get_context();
FileInfo *fi = reinterpret_cast<FileInfo*>(ffi_->fh);
const fuse_context *fc = fuse_get_context();
const ugid::Set ugid(fc->uid,fc->gid);
return l::ioctl(fi->fd,cmd_,data_,out_bufsz_);
@ -131,16 +144,15 @@ namespace l
int fd;
int rv;
string fullpath;
vector<const string*> basepaths;
vector<string> basepaths;
rv = searchFunc_(branches_,fusepath_,minfreespace_,basepaths);
rv = searchFunc_(branches_,fusepath_,minfreespace_,&basepaths);
if(rv == -1)
return -errno;
fullpath = fs::path::make(basepaths[0],fusepath_);
const int flags = O_RDONLY | O_NOATIME | O_NONBLOCK;
fd = fs::open(fullpath,flags);
fd = fs::open(fullpath,O_RDONLY|O_NOATIME|O_NONBLOCK);
if(fd == -1)
return -errno;
@ -158,13 +170,12 @@ namespace l
void *data_,
uint32_t *out_bufsz_)
{
DirInfo *di = reinterpret_cast<DirInfo*>(ffi_->fh);
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.branches_lock);
DirInfo *di = reinterpret_cast<DirInfo*>(ffi_->fh);
const fuse_context *fc = fuse_get_context();
const Config &config = Config::ro();
const ugid::Set ugid(fc->uid,fc->gid);
return l::ioctl_dir_base(config.open,
return l::ioctl_dir_base(config.func.open.policy,
config.branches,
config.minfreespace,
di->fusepath.c_str(),
@ -172,19 +183,217 @@ namespace l
data_,
out_bufsz_);
}
static
int
strcpy(const std::string &s_,
void *data_)
{
char *data = (char*)data_;
if(s_.size() >= (sizeof(IOCTL_BUF) - 1))
return -ERANGE;
memcpy(data,s_.c_str(),s_.size());
data[s_.size()] = '\0';
return s_.size();
}
static
int
read_keys(void *data_)
{
std::string keys;
const Config &config = Config::ro();
config.keys(keys);
return l::strcpy(keys,data_);
}
static
int
read_val(void *data_)
{
int rv;
char *data;
std::string key;
std::string val;
const Config &config = Config::ro();
data = (char*)data_;
data[sizeof(IOCTL_BUF) - 1] = '\0';
key = data;
rv = config.get(key,&val);
if(rv < 0)
return rv;
return l::strcpy(val,data_);
}
static
int
write_val(void *data_)
{
char *data;
std::string kv;
std::string key;
std::string val;
Config &config = Config::rw();
data = (char*)data_;
data[sizeof(IOCTL_BUF) - 1] = '\0';
kv = data;
str::splitkv(kv,'=',&key,&val);
return config.set(key,val);
}
static
int
file_basepath(Policy::Func::Search searchFunc_,
const Branches &branches_,
const uint64_t minfreespace_,
const char *fusepath_,
void *data_)
{
int rv;
vector<string> basepaths;
rv = searchFunc_(branches_,fusepath_,minfreespace_,&basepaths);
if(rv == -1)
return -errno;
return l::strcpy(basepaths[0],data_);
}
static
int
file_basepath(fuse_file_info *ffi_,
void *data_)
{
const Config &config = Config::ro();
std::string &fusepath = reinterpret_cast<FH*>(ffi_->fh)->fusepath;
return l::file_basepath(config.func.open.policy,
config.branches,
config.minfreespace,
fusepath.c_str(),
data_);
}
static
int
file_relpath(fuse_file_info *ffi_,
void *data_)
{
std::string &fusepath = reinterpret_cast<FH*>(ffi_->fh)->fusepath;
return l::strcpy(fusepath,data_);
}
static
int
file_fullpath(Policy::Func::Search searchFunc_,
const Branches &branches_,
const uint64_t minfreespace_,
const string &fusepath_,
void *data_)
{
int rv;
string fullpath;
vector<string> basepaths;
rv = searchFunc_(branches_,fusepath_,minfreespace_,&basepaths);
if(rv == -1)
return -errno;
fullpath = fs::path::make(basepaths[0],fusepath_);
return l::strcpy(fullpath,data_);
}
static
int
file_fullpath(fuse_file_info *ffi_,
void *data_)
{
const Config &config = Config::ro();
std::string &fusepath = reinterpret_cast<FH*>(ffi_->fh)->fusepath;
return l::file_fullpath(config.func.open.policy,
config.branches,
config.minfreespace,
fusepath,
data_);
}
static
int
file_allpaths(fuse_file_info *ffi_,
void *data_)
{
string concated;
vector<string> paths;
vector<string> branches;
string &fusepath = reinterpret_cast<FH*>(ffi_->fh)->fusepath;
const Config &config = Config::ro();
config.branches.to_paths(branches);
fs::findallfiles(branches,fusepath.c_str(),paths);
concated = str::join(paths,'\0');
return l::strcpy(concated,data_);
}
static
int
file_info(fuse_file_info *ffi_,
void *data_)
{
char *key = (char*)data_;
if(!strcmp("basepath",key))
return l::file_basepath(ffi_,data_);
if(!strcmp("relpath",key))
return l::file_relpath(ffi_,data_);
if(!strcmp("fullpath",key))
return l::file_fullpath(ffi_,data_);
if(!strcmp("allpaths",key))
return l::file_allpaths(ffi_,data_);
return -ENOATTR;
}
}
namespace FUSE
{
int
ioctl(const char *fusepath_,
int cmd_,
unsigned long cmd_,
void *arg_,
fuse_file_info *ffi_,
unsigned int flags_,
void *data_,
uint32_t *out_bufsz_)
{
switch(cmd_)
{
case IOCTL_READ_KEYS:
return l::read_keys(data_);
case IOCTL_READ_VAL:
return l::read_val(data_);
case IOCTL_WRITE_VAL:
return l::write_val(data_);
case IOCTL_FILE_INFO:
return l::file_info(ffi_,data_);
}
if(flags_ & FUSE_IOCTL_DIR)
return l::ioctl_dir(ffi_,cmd_,data_,out_bufsz_);

2
src/fuse_ioctl.hpp

@ -22,7 +22,7 @@ namespace FUSE
{
int
ioctl(const char *fusepath_,
int cmd_,
unsigned long cmd_,
void *arg_,
fuse_file_info *ffi_,
unsigned int flags_,

76
src/fuse_link.cpp

@ -20,7 +20,6 @@
#include "fs_clonepath.hpp"
#include "fs_path.hpp"
#include "rv.hpp"
#include "rwlock.hpp"
#include "ugid.hpp"
#include <fuse.h>
@ -55,11 +54,11 @@ namespace l
static
int
link_create_path_loop(const vector<const string*> &oldbasepaths_,
const string &newbasepath_,
const char *oldfusepath_,
const char *newfusepath_,
const string &newfusedirpath_)
link_create_path_loop(const vector<string> &oldbasepaths_,
const string &newbasepath_,
const char *oldfusepath_,
const char *newfusepath_,
const string &newfusedirpath_)
{
int rv;
int error;
@ -67,11 +66,11 @@ namespace l
error = -1;
for(size_t i = 0, ei = oldbasepaths_.size(); i != ei; i++)
{
rv = fs::clonepath_as_root(newbasepath_,*oldbasepaths_[i],newfusedirpath_);
rv = fs::clonepath_as_root(newbasepath_,oldbasepaths_[i],newfusedirpath_);
if(rv == -1)
error = error::calc(rv,error,errno);
else
error = l::link_create_path_core(*oldbasepaths_[i],newbasepath_,
error = l::link_create_path_core(oldbasepaths_[i],newbasepath_,
oldfusepath_,newfusepath_,
error);
}
@ -90,20 +89,20 @@ namespace l
{
int rv;
string newfusedirpath;
vector<const string*> oldbasepaths;
vector<const string*> newbasepaths;
vector<string> oldbasepaths;
vector<string> newbasepaths;
rv = actionFunc_(branches_,oldfusepath_,minfreespace_,oldbasepaths);
rv = actionFunc_(branches_,oldfusepath_,minfreespace_,&oldbasepaths);
if(rv == -1)
return -errno;
newfusedirpath = fs::path::dirname(newfusepath_);
rv = searchFunc_(branches_,newfusedirpath,minfreespace_,newbasepaths);
rv = searchFunc_(branches_,newfusedirpath,minfreespace_,&newbasepaths);
if(rv == -1)
return -errno;
return l::link_create_path_loop(oldbasepaths,*newbasepaths[0],
return l::link_create_path_loop(oldbasepaths,newbasepaths[0],
oldfusepath_,newfusepath_,
newfusedirpath);
}
@ -120,22 +119,22 @@ namespace l
{
int rv;
string newfusedirpath;
vector<const string*> newbasepath;
vector<string> newbasepath;
newfusedirpath = fs::path::dirname(newfusepath_);
rv = createFunc_(branches_,newfusedirpath,minfreespace_,newbasepath);
rv = createFunc_(branches_,newfusedirpath,minfreespace_,&newbasepath);
if(rv == -1)
return -1;
if(oldbasepath_ != *newbasepath[0])
if(oldbasepath_ != newbasepath[0])
return (errno=EXDEV,-1);
rv = searchFunc_(branches_,newfusedirpath,minfreespace_,newbasepath);
rv = searchFunc_(branches_,newfusedirpath,minfreespace_,&newbasepath);
if(rv == -1)
return -1;
return fs::clonepath_as_root(*newbasepath[0],oldbasepath_,newfusedirpath);
return fs::clonepath_as_root(newbasepath[0],oldbasepath_,newfusedirpath);
}
static
@ -172,13 +171,13 @@ namespace l
static
int
link_preserve_path_loop(Policy::Func::Search searchFunc_,
Policy::Func::Create createFunc_,
const Branches &branches_,
const uint64_t minfreespace_,
const char *oldfusepath_,
const char *newfusepath_,
const vector<const string*> &oldbasepaths_)
link_preserve_path_loop(Policy::Func::Search searchFunc_,
Policy::Func::Create createFunc_,
const Branches &branches_,
const uint64_t minfreespace_,
const char *oldfusepath_,
const char *newfusepath_,
const vector<string> &oldbasepaths_)
{
int error;
@ -187,7 +186,7 @@ namespace l
{
error = l::link_preserve_path_core(searchFunc_,createFunc_,
branches_,minfreespace_,
*oldbasepaths_[i],
oldbasepaths_[i],
oldfusepath_,newfusepath_,
error);
}
@ -206,9 +205,9 @@ namespace l
const char *newfusepath_)
{
int rv;
vector<const string*> oldbasepaths;
vector<string> oldbasepaths;
rv = actionFunc_(branches_,oldfusepath_,minfreespace_,oldbasepaths);
rv = actionFunc_(branches_,oldfusepath_,minfreespace_,&oldbasepaths);
if(rv == -1)
return -errno;
@ -225,22 +224,21 @@ namespace FUSE
link(const char *from_,
const char *to_)
{
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.branches_lock);
if(config.create->path_preserving() && !config.ignorepponrename)
return l::link_preserve_path(config.getattr,
config.link,
config.create,
const fuse_context *fc = fuse_get_context();
const Config &config = Config::ro();
const ugid::Set ugid(fc->uid,fc->gid);
if(config.func.create.policy->path_preserving() && !config.ignorepponrename)
return l::link_preserve_path(config.func.getattr.policy,
config.func.link.policy,
config.func.create.policy,
config.branches,
config.minfreespace,
from_,
to_);
return l::link_create_path(config.link,
config.create,
return l::link_create_path(config.func.link.policy,
config.func.create.policy,
config.branches,
config.minfreespace,
from_,

70
src/fuse_listxattr.cpp

@ -20,7 +20,6 @@
#include "errno.hpp"
#include "fs_base_listxattr.hpp"
#include "fs_path.hpp"
#include "rwlock.hpp"
#include "ugid.hpp"
#include "xattr.hpp"
@ -38,53 +37,13 @@ namespace l
{
static
int
listxattr_controlfile(char *list_,
listxattr_controlfile(const Config &config_,
char *list_,
const size_t size_)
{
string xattrs;
const vector<string> strs =
buildvector<string>
("user.mergerfs.async_read")
("user.mergerfs.branches")
("user.mergerfs.cache.attr")
("user.mergerfs.cache.entry")
("user.mergerfs.cache.files")
("user.mergerfs.cache.negative_entry")
("user.mergerfs.cache.open")
("user.mergerfs.cache.readdir")
("user.mergerfs.cache.statfs")
("user.mergerfs.cache.symlinks")
("user.mergerfs.cache.writeback")
("user.mergerfs.direct_io")
("user.mergerfs.dropcacheonclose")
("user.mergerfs.fuse_msg_size")
("user.mergerfs.ignorepponrename")
("user.mergerfs.link_cow")
("user.mergerfs.minfreespace")
("user.mergerfs.moveonenospc")
("user.mergerfs.nullrw")
("user.mergerfs.pid")
("user.mergerfs.policies")
("user.mergerfs.posix_acl")
("user.mergerfs.readdirplus")
("user.mergerfs.security_capability")
("user.mergerfs.srcmounts")
("user.mergerfs.statfs")
("user.mergerfs.statfs_ignore")
("user.mergerfs.symlinkify")
("user.mergerfs.symlinkify_timeout")
("user.mergerfs.version")
("user.mergerfs.xattr")
;
xattrs.reserve(1024);
for(size_t i = 0; i < strs.size(); i++)
xattrs += (strs[i] + '\0');
for(size_t i = Category::Enum::BEGIN; i < Category::Enum::END; i++)
xattrs += ("user.mergerfs.category." + (std::string)*Category::categories[i] + '\0');
for(size_t i = FuseFunc::Enum::BEGIN; i < FuseFunc::Enum::END; i++)
xattrs += ("user.mergerfs.func." + (std::string)*FuseFunc::fusefuncs[i] + '\0');
config_.keys_xattr(xattrs);
if(size_ == 0)
return xattrs.size();
@ -107,9 +66,9 @@ namespace l
{
int rv;
string fullpath;
vector<const string*> basepaths;
vector<string> basepaths;
rv = searchFunc_(branches_,fusepath_,minfreespace_,basepaths);
rv = searchFunc_(branches_,fusepath_,minfreespace_,&basepaths);
if(rv == -1)
return -errno;
@ -128,26 +87,25 @@ namespace FUSE
char *list_,
size_t size_)
{
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const Config &config = Config::ro();
if(fusepath_ == config.controlfile)
return l::listxattr_controlfile(list_,size_);
return l::listxattr_controlfile(config,list_,size_);
switch(config.xattr)
{
case 0:
case Config::XAttr::ENUM::PASSTHROUGH:
break;
case ENOATTR:
case Config::XAttr::ENUM::NOATTR:
return 0;
default:
return -config.xattr;
case Config::XAttr::ENUM::NOSYS:
return -ENOSYS;
}
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.branches_lock);
const fuse_context *fc = fuse_get_context();
const ugid::Set ugid(fc->uid,fc->gid);
return l::listxattr(config.listxattr,
return l::listxattr(config.func.listxattr.policy,
config.branches,
config.minfreespace,
fusepath_,

48
src/fuse_mkdir.cpp

@ -14,11 +14,6 @@
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <fuse.h>
#include <string>
#include <vector>
#include "config.hpp"
#include "errno.hpp"
#include "fs_acl.hpp"
@ -26,9 +21,13 @@
#include "fs_clonepath.hpp"
#include "fs_path.hpp"
#include "rv.hpp"
#include "rwlock.hpp"
#include "ugid.hpp"
#include <fuse.h>
#include <string>
#include <vector>
using std::string;
using std::vector;
@ -66,12 +65,12 @@ namespace l
static
int
mkdir_loop(const string &existingpath_,
const vector<const string*> &createpaths_,
const char *fusepath_,
const string &fusedirpath_,
const mode_t mode_,
const mode_t umask_)
mkdir_loop(const string &existingpath_,
const vector<string> &createpaths_,
const char *fusepath_,
const string &fusedirpath_,
const mode_t mode_,
const mode_t umask_)
{
int rv;
int error;
@ -79,11 +78,11 @@ namespace l
error = -1;
for(size_t i = 0, ei = createpaths_.size(); i != ei; i++)
{
rv = fs::clonepath_as_root(existingpath_,*createpaths_[i],fusedirpath_);
rv = fs::clonepath_as_root(existingpath_,createpaths_[i],fusedirpath_);
if(rv == -1)
error = error::calc(rv,error,errno);
else
error = l::mkdir_loop_core(*createpaths_[i],
error = l::mkdir_loop_core(createpaths_[i],
fusepath_,
mode_,
umask_,
@ -105,20 +104,20 @@ namespace l
{
int rv;
string fusedirpath;
vector<const string*> createpaths;
vector<const string*> existingpaths;
vector<string> createpaths;
vector<string> existingpaths;
fusedirpath = fs::path::dirname(fusepath_);
rv = searchFunc_(branches_,fusedirpath,minfreespace_,existingpaths);
rv = searchFunc_(branches_,fusedirpath,minfreespace_,&existingpaths);
if(rv == -1)
return -errno;
rv = createFunc_(branches_,fusedirpath,minfreespace_,createpaths);
rv = createFunc_(branches_,fusedirpath,minfreespace_,&createpaths);
if(rv == -1)
return -errno;
return l::mkdir_loop(*existingpaths[0],
return l::mkdir_loop(existingpaths[0],
createpaths,
fusepath_,
fusedirpath,
@ -133,13 +132,12 @@ namespace FUSE
mkdir(const char *fusepath_,
mode_t mode_)
{
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.branches_lock);
const fuse_context *fc = fuse_get_context();
const Config &config = Config::ro();
const ugid::Set ugid(fc->uid,fc->gid);
return l::mkdir(config.getattr,
config.mkdir,
return l::mkdir(config.func.getattr.policy,
config.func.mkdir.policy,
config.branches,
config.minfreespace,
fusepath_,

40
src/fuse_mknod.cpp

@ -21,7 +21,6 @@
#include "fs_clonepath.hpp"
#include "fs_path.hpp"
#include "rv.hpp"
#include "rwlock.hpp"
#include "ugid.hpp"
#include <fuse.h>
@ -69,13 +68,13 @@ namespace l
static
int
mknod_loop(const string &existingpath_,
const vector<const string*> &createpaths_,
const char *fusepath_,
const string &fusedirpath_,
const mode_t mode_,
const mode_t umask_,
const dev_t dev_)
mknod_loop(const string &existingpath_,
const vector<string> &createpaths_,
const char *fusepath_,
const string &fusedirpath_,
const mode_t mode_,
const mode_t umask_,
const dev_t dev_)
{
int rv;
int error;
@ -83,11 +82,11 @@ namespace l
error = -1;
for(size_t i = 0, ei = createpaths_.size(); i != ei; i++)
{
rv = fs::clonepath_as_root(existingpath_,*createpaths_[i],fusedirpath_);
rv = fs::clonepath_as_root(existingpath_,createpaths_[i],fusedirpath_);
if(rv == -1)
error = error::calc(rv,error,errno);
else
error = l::mknod_loop_core(*createpaths_[i],
error = l::mknod_loop_core(createpaths_[i],
fusepath_,
mode_,umask_,dev_,error);
}
@ -108,20 +107,20 @@ namespace l
{
int rv;
string fusedirpath;
vector<const string*> createpaths;
vector<const string*> existingpaths;
vector<string> createpaths;
vector<string> existingpaths;
fusedirpath = fs::path::dirname(fusepath_);
rv = searchFunc_(branches_,fusedirpath,minfreespace_,existingpaths);
rv = searchFunc_(branches_,fusedirpath,minfreespace_,&existingpaths);
if(rv == -1)
return -errno;
rv = createFunc_(branches_,fusedirpath,minfreespace_,createpaths);
rv = createFunc_(branches_,fusedirpath,minfreespace_,&createpaths);
if(rv == -1)
return -errno;
return l::mknod_loop(*existingpaths[0],createpaths,
return l::mknod_loop(existingpaths[0],createpaths,
fusepath_,fusedirpath,
mode_,umask_,dev_);
}
@ -134,13 +133,12 @@ namespace FUSE
mode_t mode_,
dev_t rdev_)
{
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.branches_lock);
const fuse_context *fc = fuse_get_context();
const Config &config = Config::ro();
const ugid::Set ugid(fc->uid,fc->gid);
return l::mknod(config.getattr,
config.mknod,
return l::mknod(config.func.getattr.policy,
config.func.mknod.policy,
config.branches,
config.minfreespace,
fusepath_,

20
src/fuse_open.cpp

@ -21,7 +21,6 @@
#include "fs_cow.hpp"
#include "fs_path.hpp"
#include "policy_cache.hpp"
#include "rwlock.hpp"
#include "ugid.hpp"
#include <fuse.h>
@ -61,27 +60,27 @@ namespace l
{
switch(config_.cache_files)
{
case CacheFiles::LIBFUSE:
case CacheFiles::ENUM::LIBFUSE:
ffi_->direct_io = config_.direct_io;
ffi_->keep_cache = config_.kernel_cache;
ffi_->auto_cache = config_.auto_cache;
break;
case CacheFiles::OFF:
case CacheFiles::ENUM::OFF:
ffi_->direct_io = 1;
ffi_->keep_cache = 0;
ffi_->auto_cache = 0;
break;
case CacheFiles::PARTIAL:
case CacheFiles::ENUM::PARTIAL:
ffi_->direct_io = 0;
ffi_->keep_cache = 0;
ffi_->auto_cache = 0;
break;
case CacheFiles::FULL:
case CacheFiles::ENUM::FULL:
ffi_->direct_io = 0;
ffi_->keep_cache = 1;
ffi_->auto_cache = 0;
break;
case CacheFiles::AUTO_FULL:
case CacheFiles::ENUM::AUTO_FULL:
ffi_->direct_io = 0;
ffi_->keep_cache = 0;
ffi_->auto_cache = 1;
@ -142,17 +141,16 @@ namespace FUSE
open(const char *fusepath_,
fuse_file_info *ffi_)
{
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.branches_lock);
const fuse_context *fc = fuse_get_context();
const Config &config = Config::ro();
const ugid::Set ugid(fc->uid,fc->gid);
l::config_to_ffi_flags(config,ffi_);
if(config.writeback_cache)
l::tweak_flags_writeback_cache(&ffi_->flags);
return l::open(config.open,
return l::open(config.func.open.policy,
config.open_cache,
config.branches,
config.minfreespace,

2
src/fuse_opendir.cpp

@ -25,7 +25,7 @@ namespace FUSE
opendir(const char *fusepath_,
fuse_file_info *ffi_)
{
const Config &config = Config::get();
const Config &config = Config::ro();
ffi_->fh = reinterpret_cast<uint64_t>(new DirInfo(fusepath_));

4
src/fuse_readdir_linux.icpp

@ -140,9 +140,9 @@ namespace FUSE
{
DirInfo *di = reinterpret_cast<DirInfo*>(ffi_->fh);
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const Config &config = Config::ro();
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.branches_lock);
const rwlock::ReadGuard readlock(&config.branches.lock);
return l::readdir(config.branches,
di->fusepath.c_str(),

12
src/fuse_readdir_plus_linux.icpp

@ -69,6 +69,8 @@ namespace l
int
readdir_plus(const Branches &branches_,
const char *dirname_,
const uint64_t entry_timeout_,
const uint64_t attr_timeout_,
fuse_dirents_t *buf_)
{
int rv;
@ -85,8 +87,8 @@ namespace l
entry.nodeid = 0;
entry.generation = 0;
entry.entry_valid = 1;
entry.attr_valid = 1;
entry.entry_valid = entry_timeout_;
entry.attr_valid = attr_timeout_;
entry.entry_valid_nsec = 0;
entry.attr_valid_nsec = 0;
for(size_t i = 0, ei = branches_.size(); i != ei; i++)
@ -151,12 +153,14 @@ namespace FUSE
{
DirInfo *di = reinterpret_cast<DirInfo*>(ffi_->fh);
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const Config &config = Config::ro();
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.branches_lock);
const rwlock::ReadGuard guard(&config.branches.lock);
return l::readdir_plus(config.branches,
di->fusepath.c_str(),
config.cache_entry,
config.cache_attr,
buf_);
}
}

4
src/fuse_readdir_plus_posix.icpp

@ -137,9 +137,9 @@ namespace FUSE
{
DirInfo *di = reinterpret_cast<DirInfo*>(ffi_->fh);
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const Config &config = Config::ro();
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.branches_lock);
const rwlock::ReadGuard guard(&config.branches.lock);
return l::readdir_plus(config.branches,
di->fusepath.c_str(),

4
src/fuse_readdir_posix.icpp

@ -123,9 +123,9 @@ namespace FUSE
{
DirInfo *di = reinterpret_cast<DirInfo*>(ffi_->fh);
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const Config &config = Config::ro();
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.branches_lock);
const rwlock::ReadGuard guard(&config.branches.lock);
return l::readdir(config.branches,
di->fusepath.c_str(),

16
src/fuse_readlink.cpp

@ -19,7 +19,6 @@
#include "fs_base_readlink.hpp"
#include "fs_base_stat.hpp"
#include "fs_path.hpp"
#include "rwlock.hpp"
#include "symlinkify.hpp"
#include "ugid.hpp"
@ -74,7 +73,7 @@ namespace l
static
int
readlink_core(const string *basepath_,
readlink_core(const string &basepath_,
const char *fusepath_,
char *buf_,
const size_t size_,
@ -103,9 +102,9 @@ namespace l
const time_t symlinkify_timeout_)
{
int rv;
vector<const string*> basepaths;
vector<string> basepaths;
rv = searchFunc_(branches_,fusepath_,minfreespace_,basepaths);
rv = searchFunc_(branches_,fusepath_,minfreespace_,&basepaths);
if(rv == -1)
return -errno;
@ -121,12 +120,11 @@ namespace FUSE
char *buf_,
size_t size_)
{
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.branches_lock);
const fuse_context *fc = fuse_get_context();
const Config &config = Config::ro();
const ugid::Set ugid(fc->uid,fc->gid);
return l::readlink(config.readlink,
return l::readlink(config.func.readlink.policy,
config.branches,
config.minfreespace,
fusepath_,

4
src/fuse_release.cpp

@ -53,8 +53,8 @@ namespace FUSE
release(const char *fusepath_,
fuse_file_info *ffi_)
{
const Config &config = Config::get();
FileInfo *fi = reinterpret_cast<FileInfo*>(ffi_->fh);
const Config &config = Config::ro();
FileInfo *fi = reinterpret_cast<FileInfo*>(ffi_->fh);
if(config.open_cache.timeout)
config.open_cache.cleanup(10);

26
src/fuse_removexattr.cpp

@ -19,7 +19,6 @@
#include "fs_base_removexattr.hpp"
#include "fs_path.hpp"
#include "rv.hpp"
#include "rwlock.hpp"
#include "ugid.hpp"
#include <fuse.h>
@ -34,7 +33,7 @@ namespace l
{
static
int
removexattr_loop_core(const string *basepath_,
removexattr_loop_core(const string &basepath_,
const char *fusepath_,
const char *attrname_,
const int error_)
@ -51,9 +50,9 @@ namespace l
static
int
removexattr_loop(const vector<const string*> &basepaths_,
const char *fusepath_,
const char *attrname_)
removexattr_loop(const vector<string> &basepaths_,
const char *fusepath_,
const char *attrname_)
{
int error;
@ -76,9 +75,9 @@ namespace l
const char *attrname_)
{
int rv;
vector<const string*> basepaths;
vector<string> basepaths;
rv = actionFunc_(branches_,fusepath_,minfreespace_,basepaths);
rv = actionFunc_(branches_,fusepath_,minfreespace_,&basepaths);
if(rv == -1)
return -errno;
@ -92,18 +91,17 @@ namespace FUSE
removexattr(const char *fusepath_,
const char *attrname_)
{
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const Config &config = Config::ro();
if(fusepath_ == config.controlfile)
return -ENOATTR;
if(config.xattr)
return -config.xattr;
if(config.xattr.to_int())
return -config.xattr.to_int();
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.branches_lock);
const fuse_context *fc = fuse_get_context();
const ugid::Set ugid(fc->uid,fc->gid);
return l::removexattr(config.removexattr,
return l::removexattr(config.func.removexattr.policy,
config.branches,
config.minfreespace,
fusepath_,

112
src/fuse_rename.cpp

@ -14,11 +14,6 @@
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <algorithm>
#include <set>
#include <string>
#include <vector>
#include "config.hpp"
#include "errno.hpp"
#include "fs_base_remove.hpp"
@ -26,21 +21,25 @@
#include "fs_clonepath.hpp"
#include "fs_path.hpp"
#include "rv.hpp"
#include "rwlock.hpp"
#include "ugid.hpp"
#include <algorithm>
#include <set>
#include <string>
#include <vector>
using std::string;
using std::vector;
using std::set;
static
bool
member(const vector<const string*> &haystack,
const string &needle)
member(const vector<string> &haystack,
const string &needle)
{
for(size_t i = 0, ei = haystack.size(); i != ei; i++)
{
if(*haystack[i] == needle)
if(haystack[i] == needle)
return true;
}
@ -57,14 +56,14 @@ _remove(const vector<string> &toremove)
static
void
_rename_create_path_core(const vector<const string*> &oldbasepaths,
const string &oldbasepath,
const string &newbasepath,
const char *oldfusepath,
const char *newfusepath,
const string &newfusedirpath,
int &error,
vector<string> &tounlink)
_rename_create_path_core(const vector<string> &oldbasepaths,
const string &oldbasepath,
const string &newbasepath,
const char *oldfusepath,
const char *newfusepath,
const string &newfusedirpath,
int &error,
vector<string> &tounlink)
{
int rv;
bool ismember;
@ -108,26 +107,29 @@ _rename_create_path(Policy::Func::Search searchFunc,
int error;
string newfusedirpath;
vector<string> toremove;
vector<const string*> newbasepath;
vector<const string*> oldbasepaths;
vector<string> newbasepath;
vector<string> oldbasepaths;
vector<string> branches;
rv = actionFunc(branches_,oldfusepath,minfreespace,oldbasepaths);
rv = actionFunc(branches_,oldfusepath,minfreespace,&oldbasepaths);
if(rv == -1)
return -errno;
newfusedirpath = fs::path::dirname(newfusepath);
rv = searchFunc(branches_,newfusedirpath,minfreespace,newbasepath);
rv = searchFunc(branches_,newfusedirpath,minfreespace,&newbasepath);
if(rv == -1)
return -errno;
branches_.to_paths(branches);
error = -1;
for(size_t i = 0, ei = branches_.size(); i != ei; i++)
for(size_t i = 0, ei = branches.size(); i != ei; i++)
{
const string &oldbasepath = branches_[i].path;
const string &oldbasepath = branches[i];
_rename_create_path_core(oldbasepaths,
oldbasepath,*newbasepath[0],
oldbasepath,newbasepath[0],
oldfusepath,newfusepath,
newfusedirpath,
error,toremove);
@ -149,13 +151,13 @@ _clonepath(Policy::Func::Search searchFunc,
const string &fusedirpath)
{
int rv;
vector<const string*> srcbasepath;
vector<string> srcbasepath;
rv = searchFunc(branches_,fusedirpath,minfreespace,srcbasepath);
rv = searchFunc(branches_,fusedirpath,minfreespace,&srcbasepath);
if(rv == -1)
return -errno;
fs::clonepath_as_root(*srcbasepath[0],dstbasepath,fusedirpath);
fs::clonepath_as_root(srcbasepath[0],dstbasepath,fusedirpath);
return 0;
}
@ -172,15 +174,15 @@ _clonepath_if_would_create(Policy::Func::Search searchFunc,
{
int rv;
string newfusedirpath;
vector<const string*> newbasepath;
vector<string> newbasepath;
newfusedirpath = fs::path::dirname(newfusepath);
rv = createFunc(branches_,newfusedirpath,minfreespace,newbasepath);
rv = createFunc(branches_,newfusedirpath,minfreespace,&newbasepath);
if(rv == -1)
return rv;
if(oldbasepath == *newbasepath[0])
if(oldbasepath == newbasepath[0])
return _clonepath(searchFunc,branches_,minfreespace,oldbasepath,newfusedirpath);
return (errno=EXDEV,-1);
@ -188,16 +190,16 @@ _clonepath_if_would_create(Policy::Func::Search searchFunc,
static
void
_rename_preserve_path_core(Policy::Func::Search searchFunc,
Policy::Func::Create createFunc,
const Branches &branches_,
const uint64_t minfreespace,
const vector<const string*> &oldbasepaths,
const string &oldbasepath,
const char *oldfusepath,
const char *newfusepath,
int &error,
vector<string> &toremove)
_rename_preserve_path_core(Policy::Func::Search searchFunc,
Policy::Func::Create createFunc,
const Branches &branches_,
const uint64_t minfreespace,
const vector<string> &oldbasepaths,
const string &oldbasepath,
const char *oldfusepath,
const char *newfusepath,
int &error,
vector<string> &toremove)
{
int rv;
bool ismember;
@ -245,16 +247,19 @@ _rename_preserve_path(Policy::Func::Search searchFunc,
int rv;
int error;
vector<string> toremove;
vector<const string*> oldbasepaths;
vector<string> oldbasepaths;
vector<string> branches;
rv = actionFunc(branches_,oldfusepath,minfreespace,oldbasepaths);
rv = actionFunc(branches_,oldfusepath,minfreespace,&oldbasepaths);
if(rv == -1)
return -errno;
branches_.to_paths(branches);
error = -1;
for(size_t i = 0, ei = branches_.size(); i != ei; i++)
for(size_t i = 0, ei = branches.size(); i != ei; i++)
{
const string &oldbasepath = branches_[i].path;
const string &oldbasepath = branches[i];
_rename_preserve_path_core(searchFunc,createFunc,
branches_,minfreespace,
@ -275,24 +280,23 @@ namespace FUSE
rename(const char *oldpath,
const char *newpath)
{
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.branches_lock);
const fuse_context *fc = fuse_get_context();
Config &config = Config::rw();
const ugid::Set ugid(fc->uid,fc->gid);
config.open_cache.erase(oldpath);
if(config.create->path_preserving() && !config.ignorepponrename)
return _rename_preserve_path(config.getattr,
config.rename,
config.create,
if(config.func.create.policy->path_preserving() && !config.ignorepponrename)
return _rename_preserve_path(config.func.getattr.policy,
config.func.rename.policy,
config.func.create.policy,
config.branches,
config.minfreespace,
oldpath,
newpath);
return _rename_create_path(config.getattr,
config.rename,
return _rename_create_path(config.func.getattr.policy,
config.func.rename.policy,
config.branches,
config.minfreespace,
oldpath,

20
src/fuse_rmdir.cpp

@ -19,7 +19,6 @@
#include "fs_base_rmdir.hpp"
#include "fs_path.hpp"
#include "rv.hpp"
#include "rwlock.hpp"
#include "ugid.hpp"
#include <fuse.h>
@ -35,7 +34,7 @@ namespace l
{
static
int
rmdir_loop_core(const string *basepath_,
rmdir_loop_core(const string &basepath_,
const char *fusepath_,
const int error_)
{
@ -52,8 +51,8 @@ namespace l
static
int
rmdir_loop(const vector<const string*> &basepaths_,
const char *fusepath_)
rmdir_loop(const vector<string> &basepaths_,
const char *fusepath_)
{
int error;
@ -74,9 +73,9 @@ namespace l
const char *fusepath_)
{
int rv;
vector<const string*> basepaths;
vector<string> basepaths;
rv = actionFunc_(branches_,fusepath_,minfreespace_,basepaths);
rv = actionFunc_(branches_,fusepath_,minfreespace_,&basepaths);
if(rv == -1)
return -errno;
@ -89,12 +88,11 @@ namespace FUSE
int
rmdir(const char *fusepath_)
{
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readguard(&config.branches_lock);
const fuse_context *fc = fuse_get_context();
const Config &config = Config::ro();
const ugid::Set ugid(fc->uid,fc->gid);
return l::rmdir(config.rmdir,
return l::rmdir(config.func.rmdir.policy,
config.branches,
config.minfreespace,
fusepath_);

454
src/fuse_setxattr.cpp

@ -22,7 +22,6 @@
#include "fs_statvfs_cache.hpp"
#include "num.hpp"
#include "rv.hpp"
#include "rwlock.hpp"
#include "str.hpp"
#include "ugid.hpp"
@ -48,430 +47,40 @@ namespace l
return (strcmp(attrname_,SECURITY_CAPABILITY) == 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(const string &attrval,
const int flags,
Branches &branches_,
pthread_rwlock_t &branches_lock)
{
const rwlock::WriteGuard wrg(&branches_lock);
string instruction;
string values;
if((flags & XATTR_CREATE) == XATTR_CREATE)
return -EEXIST;
l::split_attrval(attrval,instruction,values);
if(instruction == "+")
branches_.add_end(values);
else if(instruction == "+<")
branches_.add_begin(values);
else if(instruction == "+>")
branches_.add_end(values);
else if(instruction == "-")
branches_.erase_fnmatch(values);
else if(instruction == "-<")
branches_.erase_begin();
else if(instruction == "->")
branches_.erase_end();
else if(instruction == "=")
branches_.set(values);
else if(instruction.empty())
branches_.set(values);
else
return -EINVAL;
return 0;
}
static
int
setxattr_uint64_t(const string &attrval,
const int flags,
uint64_t &uint)
{
int rv;
if((flags & XATTR_CREATE) == XATTR_CREATE)
return -EEXIST;
rv = num::to_uint64_t(attrval,uint);
if(rv == -1)
return -EINVAL;
return 0;
}
static
int
setxattr_double(const string &attrval_,
const int flags_,
double *d_)
{
int rv;
if((flags_ & XATTR_CREATE) == XATTR_CREATE)
return -EEXIST;
rv = num::to_double(attrval_,d_);
if(rv == -1)
return -EINVAL;
return 0;
}
static
int
setxattr_time_t(const string &attrval,
const int flags,
time_t &time)
{
int rv;
if((flags & XATTR_CREATE) == XATTR_CREATE)
return -EEXIST;
rv = num::to_time_t(attrval,time);
if(rv == -1)
return -EINVAL;
return 0;
}
static
int
setxattr_bool(const string &attrval,
const int flags,
bool &value)
{
if((flags & XATTR_CREATE) == XATTR_CREATE)
return -EEXIST;
if(attrval == "false")
value = false;
else if(attrval == "true")
value = true;
else
return -EINVAL;
return 0;
}
static
int
setxattr_xattr(const string &attrval_,
const int flags_,
int &xattr_)
{
if((flags_ & XATTR_CREATE) == XATTR_CREATE)
return -EEXIST;
if(attrval_ == "passthrough")
xattr_ = 0;
else if(attrval_ == "noattr")
xattr_ = ENOATTR;
else if(attrval_ == "nosys")
xattr_ = ENOSYS;
else
return -EINVAL;
return 0;
}
static
int
setxattr_statfs(const string &attrval_,
const int flags_,
Config::StatFS::Enum &enum_)
{
if((flags_ & XATTR_CREATE) == XATTR_CREATE)
return -EEXIST;
if(attrval_ == "base")
enum_ = Config::StatFS::BASE;
else if(attrval_ == "full")
enum_ = Config::StatFS::FULL;
else
return -EINVAL;
return 0;
}
static
int
setxattr_statfsignore(const string &attrval_,
const int flags_,
Config::StatFSIgnore::Enum &enum_)
{
if((flags_ & XATTR_CREATE) == XATTR_CREATE)
return -EEXIST;
if(attrval_ == "none")
enum_ = Config::StatFSIgnore::NONE;
else if(attrval_ == "ro")
enum_ = Config::StatFSIgnore::RO;
else if(attrval_ == "nc")
enum_ = Config::StatFSIgnore::NC;
else
return -EINVAL;
return 0;
}
static
int
setxattr(const string &attrval_,
const int flags_,
Config::CacheFiles &cache_files_)
{
Config::CacheFiles tmp;
if((flags_ & XATTR_CREATE) == XATTR_CREATE)
return -EEXIST;
tmp = attrval_;
if(!tmp.valid())
return -EINVAL;
cache_files_ = tmp;
return 0;
}
static
int
setxattr_controlfile_func_policy(Config &config,
const string &funcname,
const string &attrval,
const int flags)
setxattr_controlfile(Config &config,
const string &attrname,
const string &attrval,
const int flags)
{
int rv;
string key;
if((flags & XATTR_CREATE) == XATTR_CREATE)
return -EEXIST;
if(funcname == "open")
config.open_cache.clear();
rv = config.set_func_policy(funcname,attrval);
if(rv == -1)
return -errno;
if(!str::startswith(attrname,"user.mergerfs."))
return -ENOATTR;
return 0;
}
key = &attrname[14];
static
int
setxattr_controlfile_category_policy(Config &config,
const string &categoryname,
const string &attrval,
const int flags)
{
int rv;
if(config.has_key(key) == false)
return -ENOATTR;
if((flags & XATTR_CREATE) == XATTR_CREATE)
return -EEXIST;
if(categoryname == "search")
config.open_cache.clear();
rv = config.set_category_policy(categoryname,attrval);
if(rv == -1)
return -errno;
return 0;
}
static
int
setxattr_statfs_timeout(const string &attrval_,
const int flags_)
{
int rv;
uint64_t timeout;
rv = l::setxattr_uint64_t(attrval_,flags_,timeout);
if(rv >= 0)
fs::statvfs_cache_timeout(timeout);
rv = config.set(key,attrval);
if(rv < 0)
return rv;
return rv;
}
static
int
setxattr_controlfile_cache_attr(const string &attrval_,
const int flags_)
{
int rv;
double d;
rv = l::setxattr_double(attrval_,flags_,&d);
if(rv >= 0)
fuse_config_set_attr_timeout(fuse_get_context()->fuse,d);
return rv;
}
static
int
setxattr_controlfile_cache_entry(const string &attrval_,
const int flags_)
{
int rv;
double d;
rv = l::setxattr_double(attrval_,flags_,&d);
if(rv >= 0)
fuse_config_set_entry_timeout(fuse_get_context()->fuse,d);
return rv;
}
static
int
setxattr_controlfile_cache_negative_entry(const string &attrval_,
const int flags_)
{
int rv;
double d;
rv = l::setxattr_double(attrval_,flags_,&d);
if(rv >= 0)
fuse_config_set_negative_entry_timeout(fuse_get_context()->fuse,d);
config.open_cache.clear();
fs::statvfs_cache_timeout(config.cache_statfs);
return rv;
}
static
int
setxattr_controlfile(Config &config,
const string &attrname,
const string &attrval,
const int flags)
{
vector<string> attr;
str::split(attr,attrname,'.');
switch(attr.size())
{
case 3:
if(attr[2] == "srcmounts")
return l::setxattr_srcmounts(attrval,
flags,
config.branches,
config.branches_lock);
else if(attr[2] == "branches")
return l::setxattr_srcmounts(attrval,
flags,
config.branches,
config.branches_lock);
else if(attr[2] == "minfreespace")
return l::setxattr_uint64_t(attrval,
flags,
config.minfreespace);
else if(attr[2] == "moveonenospc")
return l::setxattr_bool(attrval,
flags,
config.moveonenospc);
else if(attr[2] == "dropcacheonclose")
return l::setxattr_bool(attrval,
flags,
config.dropcacheonclose);
else if(attr[2] == "symlinkify")
return l::setxattr_bool(attrval,
flags,
config.symlinkify);
else if(attr[2] == "symlinkify_timeout")
return l::setxattr_time_t(attrval,
flags,
config.symlinkify_timeout);
else if(attr[2] == "ignorepponrename")
return l::setxattr_bool(attrval,
flags,
config.ignorepponrename);
else if(attr[2] == "security_capability")
return l::setxattr_bool(attrval,
flags,
config.security_capability);
else if(attr[2] == "xattr")
return l::setxattr_xattr(attrval,
flags,
config.xattr);
else if(attr[2] == "link_cow")
return l::setxattr_bool(attrval,
flags,
config.link_cow);
else if(attr[2] == "statfs")
return l::setxattr_statfs(attrval,
flags,
config.statfs);
else if(attr[2] == "statfs_ignore")
return l::setxattr_statfsignore(attrval,
flags,
config.statfs_ignore);
else if(attr[2] == "direct_io")
return l::setxattr_bool(attrval,
flags,
config.direct_io);
break;
case 4:
if(attr[2] == "category")
return l::setxattr_controlfile_category_policy(config,
attr[3],
attrval,
flags);
else if(attr[2] == "func")
return l::setxattr_controlfile_func_policy(config,
attr[3],
attrval,
flags);
else if((attr[2] == "cache") && (attr[3] == "open"))
return l::setxattr_uint64_t(attrval,
flags,
config.open_cache.timeout);
else if((attr[2] == "cache") && (attr[3] == "statfs"))
return l::setxattr_statfs_timeout(attrval,flags);
else if((attr[2] == "cache") && (attr[3] == "attr"))
return l::setxattr_controlfile_cache_attr(attrval,flags);
else if((attr[2] == "cache") && (attr[3] == "entry"))
return l::setxattr_controlfile_cache_entry(attrval,flags);
else if((attr[2] == "cache") && (attr[3] == "negative_entry"))
return l::setxattr_controlfile_cache_negative_entry(attrval,flags);
else if((attr[2] == "cache") && (attr[3] == "readdir"))
return l::setxattr_bool(attrval,flags,config.cache_readdir);
else if((attr[2] == "cache") && (attr[3] == "files"))
return l::setxattr(attrval,flags,config.cache_files);
break;
default:
break;
}
return -EINVAL;
}
static
int
setxattr_loop_core(const string *basepath,
setxattr_loop_core(const string &basepath,
const char *fusepath,
const char *attrname,
const char *attrval,
@ -491,12 +100,12 @@ namespace l
static
int
setxattr_loop(const vector<const string*> &basepaths,
const char *fusepath,
const char *attrname,
const char *attrval,
const size_t attrvalsize,
const int flags)
setxattr_loop(const vector<string> &basepaths,
const char *fusepath,
const char *attrname,
const char *attrval,
const size_t attrvalsize,
const int flags)
{
int error;
@ -523,9 +132,9 @@ namespace l
const int flags)
{
int rv;
vector<const string*> basepaths;
vector<string> basepaths;
rv = actionFunc(branches_,fusepath,minfreespace,basepaths);
rv = actionFunc(branches_,fusepath,minfreespace,&basepaths);
if(rv == -1)
return -errno;
@ -542,11 +151,10 @@ namespace FUSE
size_t attrvalsize,
int flags)
{
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
Config &config = Config::rw();
if(fusepath == config.controlfile)
return l::setxattr_controlfile(Config::get_writable(),
return l::setxattr_controlfile(config,
attrname,
string(attrval,attrvalsize),
flags);
@ -555,13 +163,13 @@ namespace FUSE
l::is_attrname_security_capability(attrname))
return -ENOATTR;
if(config.xattr)
return -config.xattr;
if(config.xattr.to_int())
return -config.xattr.to_int();
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.branches_lock);
const fuse_context *fc = fuse_get_context();
const ugid::Set ugid(fc->uid,fc->gid);
return l::setxattr(config.setxattr,
return l::setxattr(config.func.setxattr.policy,
config.branches,
config.minfreespace,
fusepath,

30
src/fuse_statfs.cpp

@ -19,7 +19,6 @@
#include "fs_base_stat.hpp"
#include "fs_base_statvfs.hpp"
#include "fs_path.hpp"
#include "rwlock.hpp"
#include "statvfs_util.hpp"
#include "ugid.hpp"
@ -70,22 +69,22 @@ namespace l
static
bool
should_ignore(const StatFSIgnore::Enum ignore_,
const Branch *branch_,
const bool readonly_)
should_ignore(const StatFSIgnore ignore_,
const Branch *branch_,
const bool readonly_)
{
return ((((ignore_ == StatFSIgnore::RO) || readonly_) &&
return ((((ignore_ == StatFSIgnore::ENUM::RO) || readonly_) &&
(branch_->ro_or_nc())) ||
((ignore_ == StatFSIgnore::NC) && (branch_->nc())));
((ignore_ == StatFSIgnore::ENUM::NC) && (branch_->nc())));
}
static
int
statfs(const Branches &branches_,
const char *fusepath_,
const StatFS::Enum mode_,
const StatFSIgnore::Enum ignore_,
struct statvfs *fsstat_)
statfs(const Branches &branches_,
const char *fusepath_,
const StatFS mode_,
const StatFSIgnore ignore_,
struct statvfs *fsstat_)
{
int rv;
string fullpath;
@ -101,7 +100,7 @@ namespace l
min_namemax = std::numeric_limits<unsigned long>::max();
for(size_t i = 0, ei = branches_.size(); i < ei; i++)
{
fullpath = ((mode_ == StatFS::FULL) ?
fullpath = ((mode_ == StatFS::ENUM::FULL) ?
fs::path::make(&branches_[i].path,fusepath_) :
branches_[i].path);
@ -153,10 +152,9 @@ namespace FUSE
statfs(const char *fusepath_,
struct statvfs *st_)
{
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.branches_lock);
const fuse_context *fc = fuse_get_context();
const Config &config = Config::ro();
const ugid::Set ugid(fc->uid,fc->gid);
return l::statfs(config.branches,
fusepath_,

36
src/fuse_symlink.cpp

@ -20,7 +20,6 @@
#include "fs_clonepath.hpp"
#include "fs_path.hpp"
#include "rv.hpp"
#include "rwlock.hpp"
#include "ugid.hpp"
#include <fuse.h>
@ -54,11 +53,11 @@ namespace l
static
int
symlink_loop(const string &existingpath_,
const vector<const string*> newbasepaths_,
const char *oldpath_,
const char *newpath_,
const string &newdirpath_)
symlink_loop(const string &existingpath_,
const vector<string> &newbasepaths_,
const char *oldpath_,
const char *newpath_,
const string &newdirpath_)
{
int rv;
int error;
@ -66,11 +65,11 @@ namespace l
error = -1;
for(size_t i = 0, ei = newbasepaths_.size(); i != ei; i++)
{
rv = fs::clonepath_as_root(existingpath_,*newbasepaths_[i],newdirpath_);
rv = fs::clonepath_as_root(existingpath_,newbasepaths_[i],newdirpath_);
if(rv == -1)
error = error::calc(rv,error,errno);
else
error = l::symlink_loop_core(*newbasepaths_[i],
error = l::symlink_loop_core(newbasepaths_[i],
oldpath_,
newpath_,
error);
@ -90,20 +89,20 @@ namespace l
{
int rv;
string newdirpath;
vector<const string*> newbasepaths;
vector<const string*> existingpaths;
vector<string> newbasepaths;
vector<string> existingpaths;
newdirpath = fs::path::dirname(newpath_);
rv = searchFunc_(branches_,newdirpath,minfreespace_,existingpaths);
rv = searchFunc_(branches_,newdirpath,minfreespace_,&existingpaths);
if(rv == -1)
return -errno;
rv = createFunc_(branches_,newdirpath,minfreespace_,newbasepaths);
rv = createFunc_(branches_,newdirpath,minfreespace_,&newbasepaths);
if(rv == -1)
return -errno;
return l::symlink_loop(*existingpaths[0],newbasepaths,
return l::symlink_loop(existingpaths[0],newbasepaths,
oldpath_,newpath_,newdirpath);
}
}
@ -114,13 +113,12 @@ namespace FUSE
symlink(const char *oldpath_,
const char *newpath_)
{
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.branches_lock);
const fuse_context *fc = fuse_get_context();
const Config &config = Config::ro();
const ugid::Set ugid(fc->uid,fc->gid);
return l::symlink(config.getattr,
config.symlink,
return l::symlink(config.func.getattr.policy,
config.func.symlink.policy,
config.branches,
config.minfreespace,
oldpath_,

22
src/fuse_truncate.cpp

@ -19,7 +19,6 @@
#include "fs_base_truncate.hpp"
#include "fs_path.hpp"
#include "rv.hpp"
#include "rwlock.hpp"
#include "ugid.hpp"
#include <fuse.h>
@ -37,7 +36,7 @@ namespace l
{
static
int
truncate_loop_core(const string *basepath_,
truncate_loop_core(const string &basepath_,
const char *fusepath_,
const off_t size_,
const int error_)
@ -54,9 +53,9 @@ namespace l
static
int
truncate_loop(const vector<const string*> &basepaths_,
const char *fusepath_,
const off_t size_)
truncate_loop(const vector<string> &basepaths_,
const char *fusepath_,
const off_t size_)
{
int error;
@ -78,9 +77,9 @@ namespace l
const off_t size_)
{
int rv;
vector<const string*> basepaths;
vector<string> basepaths;
rv = actionFunc_(branches_,fusepath_,minfreespace_,basepaths);
rv = actionFunc_(branches_,fusepath_,minfreespace_,&basepaths);
if(rv == -1)
return -errno;
@ -94,12 +93,11 @@ namespace FUSE
truncate(const char *fusepath_,
off_t size_)
{
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.branches_lock);
const fuse_context *fc = fuse_get_context();
const Config &config = Config::ro();
const ugid::Set ugid(fc->uid,fc->gid);
return l::truncate(config.truncate,
return l::truncate(config.func.truncate.policy,
config.branches,
config.minfreespace,
fusepath_,

20
src/fuse_unlink.cpp

@ -19,7 +19,6 @@
#include "fs_base_unlink.hpp"
#include "fs_path.hpp"
#include "rv.hpp"
#include "rwlock.hpp"
#include "ugid.hpp"
#include <fuse.h>
@ -36,7 +35,7 @@ namespace l
{
static
int
unlink_loop_core(const string *basepath_,
unlink_loop_core(const string &basepath_,
const char *fusepath_,
const int error_)
{
@ -52,8 +51,8 @@ namespace l
static
int
unlink_loop(const vector<const string*> &basepaths_,
const char *fusepath_)
unlink_loop(const vector<string> &basepaths_,
const char *fusepath_)
{
int error;
@ -74,9 +73,9 @@ namespace l
const char *fusepath_)
{
int rv;
vector<const string*> basepaths;
vector<string> basepaths;
rv = actionFunc_(branches_,fusepath_,minfreespace_,basepaths);
rv = actionFunc_(branches_,fusepath_,minfreespace_,&basepaths);
if(rv == -1)
return -errno;
@ -89,14 +88,13 @@ namespace FUSE
int
unlink(const char *fusepath_)
{
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.branches_lock);
const fuse_context *fc = fuse_get_context();
const Config &config = Config::ro();
const ugid::Set ugid(fc->uid,fc->gid);
config.open_cache.erase(fusepath_);
return l::unlink(config.unlink,
return l::unlink(config.func.unlink.policy,
config.branches,
config.minfreespace,
fusepath_);

23
src/fuse_utimens.cpp

@ -19,8 +19,8 @@
#include "fs_base_utime.hpp"
#include "fs_path.hpp"
#include "rv.hpp"
#include "rwlock.hpp"
#include "ugid.hpp"
#include <fuse.h>
#include <string>
@ -35,7 +35,7 @@ namespace l
{
static
int
utimens_loop_core(const string *basepath_,
utimens_loop_core(const string &basepath_,
const char *fusepath_,
const timespec ts_[2],
const int error_)
@ -52,9 +52,9 @@ namespace l
static
int
utimens_loop(const vector<const string*> &basepaths_,
const char *fusepath_,
const timespec ts_[2])
utimens_loop(const vector<string> &basepaths_,
const char *fusepath_,
const timespec ts_[2])
{
int error;
@ -76,9 +76,9 @@ namespace l
const timespec ts_[2])
{
int rv;
vector<const string*> basepaths;
vector<string> basepaths;
rv = actionFunc_(branches_,fusepath_,minfreespace_,basepaths);
rv = actionFunc_(branches_,fusepath_,minfreespace_,&basepaths);
if(rv == -1)
return -errno;
@ -92,12 +92,11 @@ namespace FUSE
utimens(const char *fusepath_,
const timespec ts_[2])
{
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const ugid::Set ugid(fc->uid,fc->gid);
const rwlock::ReadGuard readlock(&config.branches_lock);
const fuse_context *fc = fuse_get_context();
const Config &config = Config::ro();
const ugid::Set ugid(fc->uid,fc->gid);
return l::utimens(config.utimens,
return l::utimens(config.func.utimens.policy,
config.branches,
config.minfreespace,
fusepath_,

5
src/fuse_write.cpp

@ -19,7 +19,6 @@
#include "fileinfo.hpp"
#include "fs_base_write.hpp"
#include "fs_movefile.hpp"
#include "rwlock.hpp"
#include "ugid.hpp"
#include <string>
@ -92,14 +91,12 @@ namespace l
rv = func_(fi->fd,buf_,count_,offset_);
if(l::out_of_space(-rv))
{
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
const Config &config = Config::ro();
if(config.moveonenospc)
{
vector<string> paths;
const ugid::Set ugid(0,0);
const rwlock::ReadGuard readlock(&config.branches_lock);
config.branches.to_paths(paths);

45
src/fuse_write_buf.cpp

@ -20,7 +20,6 @@
#include "fs_movefile.hpp"
#include "fuse_write.hpp"
#include "policy.hpp"
#include "rwlock.hpp"
#include "ugid.hpp"
#include <fuse.h>
@ -61,6 +60,28 @@ namespace l
return fuse_buf_copy(&dst,src_,cpflags);
}
static
int
move_and_write_buf(fuse_bufvec *src_,
off_t offset_,
FileInfo *fi_)
{
int rv;
uint64_t extra;
vector<string> paths;
const ugid::Set ugid(0,0);
const Config &config = Config::ro();
config.branches.to_paths(paths);
extra = fuse_buf_size(src_);
rv = fs::movefile(paths,fi_->fusepath,extra,fi_->fd);
if(rv == -1)
return -ENOSPC;
return l::write_buf(fi_->fd,src_,offset_);
}
}
namespace FUSE
@ -76,27 +97,7 @@ namespace FUSE
rv = l::write_buf(fi->fd,src_,offset_);
if(l::out_of_space(-rv))
{
const fuse_context *fc = fuse_get_context();
const Config &config = Config::get(fc);
if(config.moveonenospc)
{
size_t extra;
vector<string> paths;
const ugid::Set ugid(0,0);
const rwlock::ReadGuard readlock(&config.branches_lock);
config.branches.to_paths(paths);
extra = fuse_buf_size(src_);
rv = fs::movefile(paths,fi->fusepath,extra,fi->fd);
if(rv == -1)
return -ENOSPC;
rv = l::write_buf(fi->fd,src_,offset_);
}
}
rv = l::move_and_write_buf(src_,offset_,fi);
return rv;
}

95
src/fusefunc.cpp

@ -1,95 +0,0 @@
/*
Copyright (c) 2016, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <string>
#include <vector>
#include "buildvector.hpp"
#include "category.hpp"
#include "fusefunc.hpp"
#define FUSEFUNC(X,Y) FuseFunc(FuseFunc::Enum::X,#X,Category::Enum::Y)
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,create))
(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;
}

125
src/fusefunc.hpp

@ -1,125 +0,0 @@
/*
Copyright (c) 2016, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
#include "category.hpp"
#include <string>
#include <vector>
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;
};

35
src/hw_cpu.cpp

@ -0,0 +1,35 @@
/*
ISC License
Copyright (c) 2019, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <unistd.h>
namespace hw
{
namespace cpu
{
int
logical_core_count(void)
{
#if defined _SC_NPROCESSORS_ONLN
return ::sysconf(_SC_NPROCESSORS_ONLN);
#else
return 1;
#endif
}
}
}

27
src/hw_cpu.hpp

@ -0,0 +1,27 @@
/*
ISC License
Copyright (c) 2020, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
namespace hw
{
namespace cpu
{
int logical_core_count(void);
}
}

18
src/mergerfs.cpp

@ -155,15 +155,25 @@ namespace l
main(const int argc_,
char **argv_)
{
fuse_args args;
fuse_operations ops = {0};
Config config;
Config config;
fuse_args args;
fuse_operations ops;
std::vector<std::string> errs;
memset(&ops,0,sizeof(fuse_operations));
args.argc = argc_;
args.argv = argv_;
args.allocated = 0;
options::parse(&args,&config);
options::parse(&args,&config,&errs);
if(errs.size())
{
for(uint64_t i = 0; i < errs.size(); i++)
std::cerr << "* ERROR: " << errs[i] << std::endl;
return 1;
}
l::setup_resources();
l::get_fuse_operations(ops,config.nullrw);

508
src/option_parser.cpp

@ -15,9 +15,11 @@
*/
#include "config.hpp"
#include "ef.hpp"
#include "errno.hpp"
#include "fs_glob.hpp"
#include "fs_statvfs_cache.hpp"
#include "hw_cpu.hpp"
#include "num.hpp"
#include "policy.hpp"
#include "str.hpp"
@ -25,9 +27,9 @@
#include <fuse.h>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
@ -46,6 +48,92 @@ enum
MERGERFS_OPT_VERSION
};
struct Data
{
Config *config;
vector<string> *errs;
};
namespace l
{
static
int
calculate_thread_count(int threads_)
{
int tc;
if(threads_ > 0)
return threads_;
tc = hw::cpu::logical_core_count();
if(threads_ < 0)
tc /= -threads_;
if(tc == 0)
tc = 1;
return tc;
}
static
void
read_config(Data *data_,
std::istream &is_)
{
int rv;
std::string key;
std::string val;
std::string line;
rv = 0;
while(std::getline(is_,line,'\n'))
{
line = str::trim(line);
if(!line.empty() && (line[0] == '#'))
continue;
str::splitkv(line,'=',&key,&val);
key = str::trim(key);
val = str::trim(val);
if(key.empty() || val.empty())
continue;
rv = data_->config->set_raw(key,val);
switch(rv)
{
case -EINVAL:
data_->errs->push_back("invalid argument - " + line);
break;
case -ENOATTR:
data_->errs->push_back("unknown option - " + line);
break;
default:
break;
}
}
}
static
void
read_config(Data *data_,
const std::string &filepath_)
{
std::ifstream is;
is.open(filepath_);
if(is.bad())
{
data_->errs->push_back("unable to open config - " + filepath_);
}
else
{
l::read_config(data_,is);
is.close();
}
}
}
static
void
@ -70,12 +158,26 @@ set_kv_option(fuse_args *args,
set_option(args,option);
}
static
void
set_threads(fuse_args *args_,
Config *config_)
{
int threads;
threads = l::calculate_thread_count(config_->threads);
config_->threads = threads;
set_kv_option(args_,"threads",config_->threads.to_string());
}
static
void
set_fsname(fuse_args *args_,
Config *config_)
{
if(config_->fsname.empty())
if(config_->fsname->empty())
{
vector<string> branches;
@ -104,337 +206,95 @@ set_default_options(fuse_args *args)
static
int
parse_and_process(const std::string &value_,
uint16_t &uint16_,
uint16_t min_,
uint16_t max_)
parse_and_process_kv_arg(Data *data_,
const std::string &key_,
const std::string &val_)
{
int rv;
uint64_t uint64;
rv = num::to_uint64_t(value_,uint64);
if(rv == -1)
return 1;
if((uint64 > max_) || (uint64 < min_))
return 1;
uint16_ = uint64;
return 0;
}
static
int
parse_and_process(const std::string &value_,
uint64_t &int_)
{
int rv;
rv = num::to_uint64_t(value_,int_);
if(rv == -1)
return 1;
return 0;
}
static
int
parse_and_process(const std::string &value,
time_t &time)
{
int rv;
rv = num::to_time_t(value,time);
if(rv == -1)
return 1;
return 0;
}
static
int
parse_and_process(const std::string &value_,
bool &boolean_)
{
if((value_ == "false") || (value_ == "0") || (value_ == "off"))
boolean_ = false;
else if((value_ == "true") || (value_ == "1") || (value_ == "on"))
boolean_ = true;
else
return 1;
return 0;
}
static
int
parse_and_process(const std::string &value_,
std::string &str_)
{
str_ = value_;
return 0;
}
static
int
parse_and_process(const std::string &value_,
Config::CacheFiles &cache_files_)
{
Config::CacheFiles tmp;
std::string key(key_);
std::string val(val_);
rv = 0;
if(key == "config")
return (l::read_config(data_,val_),0);
ef(key == "attr_timeout")
key = "cache.attr";
ef(key == "entry_timeout")
key = "cache.entry";
ef(key == "negative_entry")
key = "cache.negative_entry";
ef(key == "direct_io" && val.empty())
val = "true";
ef(key == "kernel_cache" && val.empty())
val = "true";
ef(key == "auto_cache" && val.empty())
val = "true";
ef(key == "async_read" && val.empty())
val = "true";
ef(key == "sync_read" && val.empty())
{key = "async_read", val = "false";}
ef(key == "defaults")
return 0;
ef(key == "hard_remove")
return 0;
ef(key == "atomic_o_trunc")
return 0;
ef(key == "big_writes")
return 0;
ef(key == "cache.open")
return 0;
tmp = value_;
if(!tmp.valid())
if(data_->config->has_key(key) == false)
return 1;
cache_files_ = tmp;
return 0;
}
static
int
parse_and_process_errno(const std::string &value_,
int &errno_)
{
if(value_ == "passthrough")
errno_ = 0;
else if(value_ == "nosys")
errno_ = ENOSYS;
else if(value_ == "noattr")
errno_ = ENOATTR;
else
return 1;
rv = data_->config->set_raw(key,val);
if(rv)
data_->errs->push_back("invalid argument - " + key_ + '=' + val_);
return 0;
}
static
int
parse_and_process_statfs(const std::string &value_,
Config::StatFS::Enum &enum_)
process_opt(Data *data_,
const std::string &arg_)
{
if(value_ == "base")
enum_ = Config::StatFS::BASE;
else if(value_ == "full")
enum_ = Config::StatFS::FULL;
else
return 1;
return 0;
}
std::string key;
std::string val;
static
int
parse_and_process_statfsignore(const std::string &value_,
Config::StatFSIgnore::Enum &enum_)
{
if(value_ == "none")
enum_ = Config::StatFSIgnore::NONE;
else if(value_ == "ro")
enum_ = Config::StatFSIgnore::RO;
else if(value_ == "nc")
enum_ = Config::StatFSIgnore::NC;
else
return 1;
str::splitkv(arg_,'=',&key,&val);
return 0;
return parse_and_process_kv_arg(data_,key,val);
}
static
int
parse_and_process_statfs_cache(const std::string &value_)
process_branches(Data *data_,
const char *arg_)
{
int rv;
uint64_t timeout;
string arg;
rv = num::to_uint64_t(value_,timeout);
if(rv == -1)
return 1;
fs::statvfs_cache_timeout(timeout);
arg = arg_;
rv = data_->config->set_raw("branches",arg);
if(rv)
data_->errs->push_back("unable to parse 'branches' - " + arg);
return 0;
}
static
int
parse_and_process_cache(Config &config_,
const string &func_,
const string &value_,
fuse_args *outargs)
{
if(func_ == "open")
return parse_and_process(value_,config_.open_cache.timeout);
else if(func_ == "statfs")
return parse_and_process_statfs_cache(value_);
else if(func_ == "entry")
return (set_kv_option(outargs,"entry_timeout",value_),0);
else if(func_ == "negative_entry")
return (set_kv_option(outargs,"negative_timeout",value_),0);
else if(func_ == "attr")
return (set_kv_option(outargs,"attr_timeout",value_),0);
else if(func_ == "symlinks")
return parse_and_process(value_,config_.cache_symlinks);
else if(func_ == "readdir")
return parse_and_process(value_,config_.cache_readdir);
else if(func_ == "files")
return parse_and_process(value_,config_.cache_files);
else if(func_ == "writeback")
return parse_and_process(value_,config_.writeback_cache);
return 1;
}
static
int
parse_and_process_arg(Config &config,
const std::string &arg)
{
if(arg == "defaults")
return 0;
else if(arg == "hard_remove")
return 0;
else if(arg == "direct_io")
return (config.direct_io=true,0);
else if(arg == "kernel_cache")
return (config.kernel_cache=true,0);
else if(arg == "auto_cache")
return (config.auto_cache=true,0);
else if(arg == "async_read")
return (config.async_read=true,0);
else if(arg == "sync_read")
return (config.async_read=false,0);
else if(arg == "atomic_o_trunc")
return 0;
else if(arg == "big_writes")
return 0;
else if(arg == "readdir_ino")
return 0;
return 1;
}
static
int
parse_and_process_kv_arg(Config &config,
const std::string &key,
const std::string &value,
fuse_args *outargs)
{
int rv;
std::vector<std::string> keypart;
rv = -1;
str::split(keypart,key,'.');
if(keypart.size() == 2)
{
if(keypart[0] == "func")
rv = config.set_func_policy(keypart[1],value);
else if(keypart[0] == "category")
rv = config.set_category_policy(keypart[1],value);
else if(keypart[0] == "cache")
rv = parse_and_process_cache(config,keypart[1],value,outargs);
}
else
{
if(key == "minfreespace")
rv = parse_and_process(value,config.minfreespace);
else if(key == "moveonenospc")
rv = parse_and_process(value,config.moveonenospc);
else if(key == "dropcacheonclose")
rv = parse_and_process(value,config.dropcacheonclose);
else if(key == "symlinkify")
rv = parse_and_process(value,config.symlinkify);
else if(key == "symlinkify_timeout")
rv = parse_and_process(value,config.symlinkify_timeout);
else if(key == "nullrw")
rv = parse_and_process(value,config.nullrw);
else if(key == "ignorepponrename")
rv = parse_and_process(value,config.ignorepponrename);
else if(key == "security_capability")
rv = parse_and_process(value,config.security_capability);
else if(key == "link_cow")
rv = parse_and_process(value,config.link_cow);
else if(key == "xattr")
rv = parse_and_process_errno(value,config.xattr);
else if(key == "statfs")
rv = parse_and_process_statfs(value,config.statfs);
else if(key == "statfs_ignore")
rv = parse_and_process_statfsignore(value,config.statfs_ignore);
else if(key == "fsname")
rv = parse_and_process(value,config.fsname);
else if(key == "posix_acl")
rv = parse_and_process(value,config.posix_acl);
else if(key == "direct_io")
rv = parse_and_process(value,config.direct_io);
else if(key == "kernel_cache")
rv = parse_and_process(value,config.kernel_cache);
else if(key == "auto_cache")
rv = parse_and_process(value,config.auto_cache);
else if(key == "async_read")
rv = parse_and_process(value,config.async_read);
else if(key == "readdirplus")
rv = parse_and_process(value,config.readdirplus);
else if(key == "max_write")
rv = 0;
else if(key == "fuse_msg_size")
rv = parse_and_process(value,config.fuse_msg_size,
1,
FUSE_MAX_MAX_PAGES);
}
if(rv == -1)
rv = 1;
return rv;
}
static
int
process_opt(Config &config,
const std::string &arg,
fuse_args *outargs)
process_mount(Data *data_,
const char *arg_)
{
int rv;
std::vector<std::string> argvalue;
string arg;
str::split(argvalue,arg,'=');
switch(argvalue.size())
{
case 1:
rv = parse_and_process_arg(config,argvalue[0]);
break;
case 2:
rv = parse_and_process_kv_arg(config,argvalue[0],argvalue[1],outargs);
break;
default:
rv = 1;
break;
};
return rv;
}
static
int
process_branches(const char *arg,
Config &config)
{
config.branches.set(arg);
return 0;
}
static
int
process_destmounts(const char *arg,
Config &config)
{
config.destmount = arg;
arg = arg_;
rv = data_->config->set_raw("mount",arg);
if(rv)
data_->errs->push_back("unable to set 'mount' - " + arg);
return 1;
}
@ -453,6 +313,7 @@ usage(void)
"mergerfs options:\n"
" <branches> ':' delimited list of directories. Supports\n"
" shell globbing (must be escaped in shell)\n"
" -o config=<file> Read options from file in key=val format\n"
" -o func.<f>=<p> Set function <f> to policy <p>\n"
" -o category.<c>=<p> Set functions in category <c> to <p>\n"
" -o cache.open=<int> 'open' policy cache timeout in seconds.\n"
@ -536,53 +397,53 @@ usage(void)
static
int
option_processor(void *data,
const char *arg,
int key,
fuse_args *outargs)
option_processor(void *data_,
const char *arg_,
int key_,
fuse_args *outargs_)
{
int rv = 0;
Config &config = *(Config*)data;
Data *data = (Data*)data_;
switch(key)
switch(key_)
{
case FUSE_OPT_KEY_OPT:
rv = process_opt(config,arg,outargs);
break;
return process_opt(data,arg_);
case FUSE_OPT_KEY_NONOPT:
rv = config.branches.empty() ?
process_branches(arg,config) :
process_destmounts(arg,config);
break;
if(data->config->branches.empty())
return process_branches(data,arg_);
else
return process_mount(data,arg_);
case MERGERFS_OPT_HELP:
usage();
close(2);
dup(1);
fuse_opt_add_arg(outargs,"-ho");
fuse_opt_add_arg(outargs_,"-ho");
break;
case MERGERFS_OPT_VERSION:
std::cout << "mergerfs version: "
<< (MERGERFS_VERSION[0] ? MERGERFS_VERSION : "unknown")
<< std::endl;
fuse_opt_add_arg(outargs,"--version");
fuse_opt_add_arg(outargs_,"--version");
break;
default:
break;
}
return rv;
return 0;
}
namespace options
{
void
parse(fuse_args *args_,
Config *config_)
parse(fuse_args *args_,
Config *config_,
std::vector<std::string> *errs_)
{
Data data;
const struct fuse_opt opts[] =
{
FUSE_OPT_KEY("-h",MERGERFS_OPT_HELP),
@ -593,13 +454,16 @@ namespace options
{NULL,-1U,0}
};
data.config = config_;
data.errs = errs_;
fuse_opt_parse(args_,
config_,
&data,
opts,
::option_processor);
set_default_options(args_);
set_fsname(args_,config_);
set_subtype(args_);
set_threads(args_,config_);
}
}

8
src/option_parser.hpp

@ -18,11 +18,15 @@
#include "config.hpp"
#include <string>
#include <vector>
#include <fuse.h>
namespace options
{
void
parse(fuse_args *args,
Config *config);
parse(fuse_args *args,
Config *config,
std::vector<std::string> *errs);
}

64
src/policy.hpp

@ -58,30 +58,33 @@ public:
{
typedef std::string string;
typedef std::vector<string> strvec;
typedef std::vector<const string*> cstrptrvec;
typedef const string cstring;
typedef const uint64_t cuint64_t;
typedef const strvec cstrvec;
typedef const Category::Enum::Type CType;
typedef int (*Ptr)(CType,const Branches &,const char *,cuint64_t,cstrptrvec &);
typedef int (*Ptr)(CType,const Branches &,const char *,cuint64_t,strvec *);
template <CType T>
class Base
{
public:
Base(const Policy *p)
: func(p->_func)
Base(const Policy &p_)
: func(p_._func)
{}
Base(const Policy *p_)
: func(p_->_func)
{}
int
operator()(const Branches &b,const char *c,cuint64_t d,cstrptrvec &e)
operator()(const Branches &b,const char *c,cuint64_t d,strvec *e)
{
return func(T,b,c,d,e);
}
int
operator()(const Branches &b,const string &c,cuint64_t d,cstrptrvec &e)
operator()(const Branches &b,const string &c,cuint64_t d,strvec *e)
{
return func(T,b,c.c_str(),d,e);
}
@ -90,11 +93,11 @@ public:
operator()(const Branches &b,const char *c,cuint64_t d,string *e)
{
int rv;
cstrptrvec v;
strvec v;
rv = func(T,b,c,d,v);
rv = func(T,b,c,d,&v);
if(!v.empty())
*e = *v[0];
*e = v[0];
return rv;
}
@ -107,21 +110,21 @@ public:
typedef Base<Category::Enum::create> Create;
typedef Base<Category::Enum::search> Search;
static int invalid(CType,const Branches&,const char *,cuint64_t,cstrptrvec&);
static int all(CType,const Branches&,const char*,cuint64_t,cstrptrvec&);
static int epall(CType,const Branches&,const char*,cuint64_t,cstrptrvec&);
static int epff(CType,const Branches&,const char *,cuint64_t,cstrptrvec&);
static int eplfs(CType,const Branches&,const char *,cuint64_t,cstrptrvec&);
static int eplus(CType,const Branches&,const char *,cuint64_t,cstrptrvec&);
static int epmfs(CType,const Branches&,const char *,cuint64_t,cstrptrvec&);
static int eprand(CType,const Branches&,const char *,cuint64_t,cstrptrvec&);
static int erofs(CType,const Branches&,const char *,cuint64_t,cstrptrvec&);
static int ff(CType,const Branches&,const char *,cuint64_t,cstrptrvec&);
static int lfs(CType,const Branches&,const char *,cuint64_t,cstrptrvec&);
static int lus(CType,const Branches&,const char *,cuint64_t,cstrptrvec&);
static int mfs(CType,const Branches&,const char *,cuint64_t,cstrptrvec&);
static int newest(CType,const Branches&,const char *,cuint64_t,cstrptrvec&);
static int rand(CType,const Branches&,const char *,cuint64_t,cstrptrvec&);
static int invalid(CType,const Branches&,const char *,cuint64_t,strvec*);
static int all(CType,const Branches&,const char*,cuint64_t,strvec*);
static int epall(CType,const Branches&,const char*,cuint64_t,strvec*);
static int epff(CType,const Branches&,const char *,cuint64_t,strvec*);
static int eplfs(CType,const Branches&,const char *,cuint64_t,strvec*);
static int eplus(CType,const Branches&,const char *,cuint64_t,strvec*);
static int epmfs(CType,const Branches&,const char *,cuint64_t,strvec*);
static int eprand(CType,const Branches&,const char *,cuint64_t,strvec*);
static int erofs(CType,const Branches&,const char *,cuint64_t,strvec*);
static int ff(CType,const Branches&,const char *,cuint64_t,strvec*);
static int lfs(CType,const Branches&,const char *,cuint64_t,strvec*);
static int lus(CType,const Branches&,const char *,cuint64_t,strvec*);
static int mfs(CType,const Branches&,const char *,cuint64_t,strvec*);
static int newest(CType,const Branches&,const char *,cuint64_t,strvec*);
static int rand(CType,const Branches&,const char *,cuint64_t,strvec*);
};
private:
@ -161,6 +164,7 @@ public:
operator const std::string&() const { return _str; }
operator const Func::Ptr() const { return _func; }
operator const Policy*() const { return this; }
const std::string& to_string() const { return _str; }
bool operator==(const Enum::Type enum_) const
{ return _enum == enum_; }
@ -180,6 +184,7 @@ public:
static const Policy &find(const Enum::Type);
public:
static const std::vector<Policy> _policies_;
static const Policy * const policies;
@ -199,3 +204,14 @@ public:
static const Policy &newest;
static const Policy &rand;
};
namespace std
{
static
inline
string
to_string(const Policy &p_)
{
return p_.to_string();
}
}

29
src/policy_all.cpp

@ -20,6 +20,7 @@
#include "fs_path.hpp"
#include "policy.hpp"
#include "policy_error.hpp"
#include "rwlock.hpp"
#include <string>
#include <vector>
@ -31,10 +32,12 @@ namespace all
{
static
int
create(const Branches &branches_,
const uint64_t minfreespace,
vector<const string*> &paths)
create(const Branches &branches_,
const uint64_t minfreespace_,
vector<string> *paths_)
{
rwlock::ReadGuard guard(&branches_.lock);
int rv;
int error;
fs::info_t info;
@ -52,13 +55,13 @@ namespace all
error_and_continue(error,ENOENT);
if(info.readonly)
error_and_continue(error,EROFS);
if(info.spaceavail < minfreespace)
if(info.spaceavail < minfreespace_)
error_and_continue(error,ENOSPC);
paths.push_back(&branch->path);
paths_->push_back(branch->path);
}
if(paths.empty())
if(paths_->empty())
return (errno=error,-1);
return 0;
@ -66,14 +69,14 @@ namespace all
}
int
Policy::Func::all(const Category::Enum::Type type,
Policy::Func::all(const Category::Enum::Type type_,
const Branches &branches_,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)
const char *fusepath_,
const uint64_t minfreespace_,
vector<string> *paths_)
{
if(type == Category::Enum::create)
return all::create(branches_,minfreespace,paths);
if(type_ == Category::Enum::create)
return all::create(branches_,minfreespace_,paths_);
return Policy::Func::epall(type,branches_,fusepath,minfreespace,paths);
return Policy::Func::epall(type_,branches_,fusepath_,minfreespace_,paths_);
}

2
src/policy_cache.cpp

@ -3,13 +3,11 @@
#include <cstdlib>
#include <map>
#include <string>
#include <vector>
#include <time.h>
using std::map;
using std::string;
using std::vector;
static const uint64_t DEFAULT_TIMEOUT = 0;

41
src/policy_epall.cpp

@ -22,6 +22,7 @@
#include "fs_statvfs_cache.hpp"
#include "policy.hpp"
#include "policy_error.hpp"
#include "rwlock.hpp"
#include <string>
#include <vector>
@ -33,11 +34,13 @@ namespace epall
{
static
int
create(const Branches &branches_,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)
create(const Branches &branches_,
const char *fusepath,
const uint64_t minfreespace,
vector<string> *paths)
{
rwlock::ReadGuard guard(&branches_.lock);
int rv;
int error;
fs::info_t info;
@ -60,10 +63,10 @@ namespace epall
if(info.spaceavail < minfreespace)
error_and_continue(error,ENOSPC);
paths.push_back(&branch->path);
paths->push_back(branch->path);
}
if(paths.empty())
if(paths->empty())
return (errno=error,-1);
return 0;
@ -71,10 +74,12 @@ namespace epall
static
int
action(const Branches &branches_,
const char *fusepath,
vector<const string*> &paths)
action(const Branches &branches_,
const char *fusepath,
vector<string> *paths)
{
rwlock::ReadGuard guard(&branches_.lock);
int rv;
int error;
bool readonly;
@ -95,10 +100,10 @@ namespace epall
if(readonly)
error_and_continue(error,EROFS);
paths.push_back(&branch->path);
paths->push_back(branch->path);
}
if(paths.empty())
if(paths->empty())
return (errno=error,-1);
return 0;
@ -106,10 +111,12 @@ namespace epall
static
int
search(const Branches &branches_,
const char *fusepath,
vector<const string*> &paths)
search(const Branches &branches_,
const char *fusepath,
vector<string> *paths)
{
rwlock::ReadGuard guard(&branches_.lock);
const Branch *branch;
for(size_t i = 0, ei = branches_.size(); i != ei; i++)
@ -119,10 +126,10 @@ namespace epall
if(!fs::exists(branch->path,fusepath))
continue;
paths.push_back(&branch->path);
paths->push_back(branch->path);
}
if(paths.empty())
if(paths->empty())
return (errno=ENOENT,-1);
return 0;
@ -134,7 +141,7 @@ Policy::Func::epall(const Category::Enum::Type type,
const Branches &branches_,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)
vector<string> *paths)
{
switch(type)
{

57
src/policy_epff.cpp

@ -22,6 +22,7 @@
#include "fs_statvfs_cache.hpp"
#include "policy.hpp"
#include "policy_error.hpp"
#include "rwlock.hpp"
#include <string>
#include <vector>
@ -33,11 +34,13 @@ namespace epff
{
static
int
create(const Branches &branches_,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)
create(const Branches &branches_,
const char *fusepath_,
const uint64_t minfreespace_,
vector<string> *paths_)
{
rwlock::ReadGuard guard(&branches_.lock);
int rv;
int error;
fs::info_t info;
@ -48,7 +51,7 @@ namespace epff
{
branch = &branches_[i];
if(!fs::exists(branch->path,fusepath))
if(!fs::exists(branch->path,fusepath_))
error_and_continue(error,ENOENT);
if(branch->ro_or_nc())
error_and_continue(error,EROFS);
@ -57,10 +60,10 @@ namespace epff
error_and_continue(error,ENOENT);
if(info.readonly)
error_and_continue(error,EROFS);
if(info.spaceavail < minfreespace)
if(info.spaceavail < minfreespace_)
error_and_continue(error,ENOSPC);
paths.push_back(&branch->path);
paths_->push_back(branch->path);
return 0;
}
@ -70,10 +73,12 @@ namespace epff
static
int
action(const Branches &branches_,
const char *fusepath,
vector<const string*> &paths)
action(const Branches &branches_,
const char *fusepath_,
vector<string> *paths_)
{
rwlock::ReadGuard guard(&branches_.lock);
int rv;
int error;
bool readonly;
@ -84,7 +89,7 @@ namespace epff
{
branch = &branches_[i];
if(!fs::exists(branch->path,fusepath))
if(!fs::exists(branch->path,fusepath_))
error_and_continue(error,ENOENT);
if(branch->ro())
error_and_continue(error,EROFS);
@ -94,7 +99,7 @@ namespace epff
if(readonly)
error_and_continue(error,EROFS);
paths.push_back(&branch->path);
paths_->push_back(branch->path);
return 0;
}
@ -104,20 +109,22 @@ namespace epff
static
int
search(const Branches &branches_,
const char *fusepath,
vector<const string*> &paths)
search(const Branches &branches_,
const char *fusepath_,
vector<string> *paths_)
{
rwlock::ReadGuard guard(&branches_.lock);
const Branch *branch;
for(size_t i = 0, ei = branches_.size(); i != ei; i++)
{
branch = &branches_[i];
if(!fs::exists(branch->path,fusepath))
if(!fs::exists(branch->path,fusepath_))
continue;
paths.push_back(&branch->path);
paths_->push_back(branch->path);
return 0;
}
@ -127,20 +134,20 @@ namespace epff
}
int
Policy::Func::epff(const Category::Enum::Type type,
Policy::Func::epff(const Category::Enum::Type type_,
const Branches &branches_,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)
const char *fusepath_,
const uint64_t minfreespace_,
vector<string> *paths_)
{
switch(type)
switch(type_)
{
case Category::Enum::create:
return epff::create(branches_,fusepath,minfreespace,paths);
return epff::create(branches_,fusepath_,minfreespace_,paths_);
case Category::Enum::action:
return epff::action(branches_,fusepath,paths);
return epff::action(branches_,fusepath_,paths_);
case Category::Enum::search:
default:
return epff::search(branches_,fusepath,paths);
return epff::search(branches_,fusepath_,paths_);
}
}

31
src/policy_eplfs.cpp

@ -22,6 +22,7 @@
#include "fs_statvfs_cache.hpp"
#include "policy.hpp"
#include "policy_error.hpp"
#include "rwlock.hpp"
#include <limits>
#include <string>
@ -34,11 +35,13 @@ namespace eplfs
{
static
int
create(const Branches &branches_,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)
create(const Branches &branches_,
const char *fusepath,
const uint64_t minfreespace,
vector<string> *paths)
{
rwlock::ReadGuard guard(&branches_.lock);
int rv;
int error;
uint64_t eplfs;
@ -74,17 +77,19 @@ namespace eplfs
if(eplfsbasepath == NULL)
return (errno=error,-1);
paths.push_back(eplfsbasepath);
paths->push_back(*eplfsbasepath);
return 0;
}
static
int
action(const Branches &branches_,
const char *fusepath,
vector<const string*> &paths)
action(const Branches &branches_,
const char *fusepath,
vector<string> *paths)
{
rwlock::ReadGuard guard(&branches_.lock);
int rv;
int error;
uint64_t eplfs;
@ -118,7 +123,7 @@ namespace eplfs
if(eplfsbasepath == NULL)
return (errno=error,-1);
paths.push_back(eplfsbasepath);
paths->push_back(*eplfsbasepath);
return 0;
}
@ -127,8 +132,10 @@ namespace eplfs
int
search(const Branches &branches_,
const char *fusepath,
vector<const string*> &paths)
vector<string> *paths)
{
rwlock::ReadGuard guard(&branches_.lock);
int rv;
uint64_t eplfs;
uint64_t spaceavail;
@ -156,7 +163,7 @@ namespace eplfs
if(eplfsbasepath == NULL)
return (errno=ENOENT,-1);
paths.push_back(eplfsbasepath);
paths->push_back(*eplfsbasepath);
return 0;
}
@ -167,7 +174,7 @@ Policy::Func::eplfs(const Category::Enum::Type type,
const Branches &branches_,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)
vector<string> *paths)
{
switch(type)
{

35
src/policy_eplus.cpp

@ -22,6 +22,7 @@
#include "fs_statvfs_cache.hpp"
#include "policy.hpp"
#include "policy_error.hpp"
#include "rwlock.hpp"
#include <limits>
#include <string>
@ -34,11 +35,13 @@ namespace eplus
{
static
int
create(const Branches &branches_,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)
create(const Branches &branches_,
const char *fusepath,
const uint64_t minfreespace,
vector<string> *paths)
{
rwlock::ReadGuard guard(&branches_.lock);
int rv;
int error;
uint64_t eplus;
@ -74,17 +77,19 @@ namespace eplus
if(eplusbasepath == NULL)
return (errno=error,-1);
paths.push_back(eplusbasepath);
paths->push_back(*eplusbasepath);
return 0;
}
static
int
action(const Branches &branches_,
const char *fusepath,
vector<const string*> &paths)
action(const Branches &branches_,
const char *fusepath,
vector<string> *paths)
{
rwlock::ReadGuard guard(&branches_.lock);
int rv;
int error;
uint64_t eplus;
@ -118,17 +123,19 @@ namespace eplus
if(eplusbasepath == NULL)
return (errno=error,-1);
paths.push_back(eplusbasepath);
paths->push_back(*eplusbasepath);
return 0;
}
static
int
search(const Branches &branches_,
const char *fusepath,
vector<const string*> &paths)
search(const Branches &branches_,
const char *fusepath,
vector<string> *paths)
{
rwlock::ReadGuard guard(&branches_.lock);
int rv;
uint64_t eplus;
uint64_t spaceused;
@ -156,7 +163,7 @@ namespace eplus
if(eplusbasepath == NULL)
return (errno=ENOENT,-1);
paths.push_back(eplusbasepath);
paths->push_back(*eplusbasepath);
return 0;
}
@ -167,7 +174,7 @@ Policy::Func::eplus(const Category::Enum::Type type,
const Branches &branches_,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)
vector<string> *paths)
{
switch(type)
{

57
src/policy_epmfs.cpp

@ -22,6 +22,7 @@
#include "fs_statvfs_cache.hpp"
#include "policy.hpp"
#include "policy_error.hpp"
#include "rwlock.hpp"
#include <limits>
#include <string>
@ -34,11 +35,13 @@ namespace epmfs
{
static
int
create(const Branches &branches_,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)
create(const Branches &branches_,
const char *fusepath_,
const uint64_t minfreespace_,
vector<string> *paths_)
{
rwlock::ReadGuard guard(&branches_.lock);
int rv;
int error;
uint64_t epmfs;
@ -53,7 +56,7 @@ namespace epmfs
{
branch = &branches_[i];
if(!fs::exists(branch->path,fusepath))
if(!fs::exists(branch->path,fusepath_))
error_and_continue(error,ENOENT);
if(branch->ro_or_nc())
error_and_continue(error,EROFS);
@ -62,7 +65,7 @@ namespace epmfs
error_and_continue(error,ENOENT);
if(info.readonly)
error_and_continue(error,EROFS);
if(info.spaceavail < minfreespace)
if(info.spaceavail < minfreespace_)
error_and_continue(error,ENOSPC);
if(info.spaceavail < epmfs)
continue;
@ -74,17 +77,19 @@ namespace epmfs
if(epmfsbasepath == NULL)
return (errno=error,-1);
paths.push_back(epmfsbasepath);
paths_->push_back(*epmfsbasepath);
return 0;
}
static
int
action(const Branches &branches_,
const char *fusepath,
vector<const string*> &paths)
action(const Branches &branches_,
const char *fusepath_,
vector<string> *paths_)
{
rwlock::ReadGuard guard(&branches_.lock);
int rv;
int error;
uint64_t epmfs;
@ -99,7 +104,7 @@ namespace epmfs
{
branch = &branches_[i];
if(!fs::exists(branch->path,fusepath))
if(!fs::exists(branch->path,fusepath_))
error_and_continue(error,ENOENT);
if(branch->ro())
error_and_continue(error,EROFS);
@ -118,17 +123,19 @@ namespace epmfs
if(epmfsbasepath == NULL)
return (errno=error,-1);
paths.push_back(epmfsbasepath);
paths_->push_back(*epmfsbasepath);
return 0;
}
static
int
search(const Branches &branches_,
const char *fusepath,
vector<const string*> &paths)
search(const Branches &branches_,
const char *fusepath_,
vector<string> *paths_)
{
rwlock::ReadGuard guard(&branches_.lock);
int rv;
uint64_t epmfs;
uint64_t spaceavail;
@ -141,7 +148,7 @@ namespace epmfs
{
branch = &branches_[i];
if(!fs::exists(branch->path,fusepath))
if(!fs::exists(branch->path,fusepath_))
continue;
rv = fs::statvfs_cache_spaceavail(branch->path,&spaceavail);
if(rv == -1)
@ -156,27 +163,27 @@ namespace epmfs
if(epmfsbasepath == NULL)
return (errno=ENOENT,-1);
paths.push_back(epmfsbasepath);
paths_->push_back(*epmfsbasepath);
return 0;
}
}
int
Policy::Func::epmfs(const Category::Enum::Type type,
Policy::Func::epmfs(const Category::Enum::Type type_,
const Branches &branches_,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)
const char *fusepath_,
const uint64_t minfreespace_,
vector<string> *paths_)
{
switch(type)
switch(type_)
{
case Category::Enum::create:
return epmfs::create(branches_,fusepath,minfreespace,paths);
return epmfs::create(branches_,fusepath_,minfreespace_,paths_);
case Category::Enum::action:
return epmfs::action(branches_,fusepath,paths);
return epmfs::action(branches_,fusepath_,paths_);
case Category::Enum::search:
default:
return epmfs::search(branches_,fusepath,paths);
return epmfs::search(branches_,fusepath_,paths_);
}
}

6
src/policy_eprand.cpp

@ -29,15 +29,15 @@ Policy::Func::eprand(const Category::Enum::Type type_,
const Branches &branches_,
const char *fusepath_,
const uint64_t minfreespace_,
vector<const string*> &paths_)
vector<string> *paths_)
{
int rv;
rv = Policy::Func::epall(type_,branches_,fusepath_,minfreespace_,paths_);
if(rv == 0)
{
std::random_shuffle(paths_.begin(),paths_.end());
paths_.resize(1);
std::random_shuffle(paths_->begin(),paths_->end());
paths_->resize(1);
}
return rv;

8
src/policy_erofs.cpp

@ -24,11 +24,11 @@ using std::string;
using std::vector;
int
Policy::Func::erofs(const Category::Enum::Type type,
Policy::Func::erofs(const Category::Enum::Type type_,
const Branches &branches_,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)
const char *fusepath_,
const uint64_t minfreespace_,
vector<string> *paths)
{
return (errno=EROFS,-1);
}

13
src/policy_ff.cpp

@ -21,6 +21,7 @@
#include "fs_path.hpp"
#include "policy.hpp"
#include "policy_error.hpp"
#include "rwlock.hpp"
#include <string>
#include <vector>
@ -32,10 +33,12 @@ namespace ff
{
static
int
create(const Branches &branches_,
const uint64_t minfreespace,
vector<const string*> &paths)
create(const Branches &branches_,
const uint64_t minfreespace,
vector<string> *paths)
{
rwlock::ReadGuard guard(&branches_.lock);
int rv;
int error;
fs::info_t info;
@ -56,7 +59,7 @@ namespace ff
if(info.spaceavail < minfreespace)
error_and_continue(error,ENOSPC);
paths.push_back(&branch->path);
paths->push_back(branch->path);
return 0;
}
@ -70,7 +73,7 @@ Policy::Func::ff(const Category::Enum::Type type,
const Branches &branches_,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)
vector<string> *paths)
{
if(type == Category::Enum::create)
return ff::create(branches_,minfreespace,paths);

2
src/policy_invalid.cpp

@ -28,7 +28,7 @@ Policy::Func::invalid(const Category::Enum::Type type,
const Branches &branches_,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)
vector<string> *paths)
{
return (errno=EINVAL,-1);
}

15
src/policy_lfs.cpp

@ -21,6 +21,7 @@
#include "fs_path.hpp"
#include "policy.hpp"
#include "policy_error.hpp"
#include "rwlock.hpp"
#include <limits>
#include <string>
@ -33,10 +34,12 @@ namespace lfs
{
static
int
create(const Branches &branches_,
const uint64_t minfreespace,
vector<const string*> &paths)
create(const Branches &branches_,
const uint64_t minfreespace_,
vector<string> *paths_)
{
rwlock::ReadGuard guard(&branches_.lock);
int rv;
int error;
uint64_t lfs;
@ -58,7 +61,7 @@ namespace lfs
error_and_continue(error,ENOENT);
if(info.readonly)
error_and_continue(error,EROFS);
if(info.spaceavail < minfreespace)
if(info.spaceavail < minfreespace_)
error_and_continue(error,ENOSPC);
if(info.spaceavail > lfs)
continue;
@ -70,7 +73,7 @@ namespace lfs
if(lfsbasepath == NULL)
return (errno=error,-1);
paths.push_back(lfsbasepath);
paths_->push_back(*lfsbasepath);
return 0;
}
@ -81,7 +84,7 @@ Policy::Func::lfs(const Category::Enum::Type type,
const Branches &branches_,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)
vector<string> *paths)
{
if(type == Category::Enum::create)
return lfs::create(branches_,minfreespace,paths);

13
src/policy_lus.cpp

@ -21,6 +21,7 @@
#include "fs_path.hpp"
#include "policy.hpp"
#include "policy_error.hpp"
#include "rwlock.hpp"
#include <limits>
#include <string>
@ -33,10 +34,12 @@ namespace lus
{
static
int
create(const Branches &branches_,
const uint64_t minfreespace,
vector<const string*> &paths)
create(const Branches &branches_,
const uint64_t minfreespace,
vector<string> *paths)
{
rwlock::ReadGuard guard(&branches_.lock);
int rv;
int error;
uint64_t lus;
@ -70,7 +73,7 @@ namespace lus
if(lusbasepath == NULL)
return (errno=error,-1);
paths.push_back(lusbasepath);
paths->push_back(*lusbasepath);
return 0;
}
@ -81,7 +84,7 @@ Policy::Func::lus(const Category::Enum::Type type,
const Branches &branches_,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)
vector<string> *paths)
{
if(type == Category::Enum::create)
return lus::create(branches_,minfreespace,paths);

15
src/policy_mfs.cpp

@ -21,6 +21,7 @@
#include "fs_path.hpp"
#include "policy.hpp"
#include "policy_error.hpp"
#include "rwlock.hpp"
#include <string>
#include <vector>
@ -32,10 +33,12 @@ namespace mfs
{
static
int
create(const Branches &branches_,
const uint64_t minfreespace,
vector<const string*> &paths)
create(const Branches &branches_,
const uint64_t minfreespace_,
vector<string> *paths_)
{
rwlock::ReadGuard guard(&branches_.lock);
int rv;
int error;
uint64_t mfs;
@ -57,7 +60,7 @@ namespace mfs
error_and_continue(error,ENOENT);
if(info.readonly)
error_and_continue(error,EROFS);
if(info.spaceavail < minfreespace)
if(info.spaceavail < minfreespace_)
error_and_continue(error,ENOSPC);
if(info.spaceavail < mfs)
continue;
@ -69,7 +72,7 @@ namespace mfs
if(mfsbasepath == NULL)
return (errno=error,-1);
paths.push_back(mfsbasepath);
paths_->push_back(*mfsbasepath);
return 0;
}
@ -80,7 +83,7 @@ Policy::Func::mfs(const Category::Enum::Type type,
const Branches &branches_,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)
vector<string> *paths)
{
if(type == Category::Enum::create)
return mfs::create(branches_,minfreespace,paths);

35
src/policy_newest.cpp

@ -22,6 +22,7 @@
#include "fs_statvfs_cache.hpp"
#include "policy.hpp"
#include "policy_error.hpp"
#include "rwlock.hpp"
#include <string>
#include <vector>
@ -36,11 +37,13 @@ namespace newest
{
static
int
create(const Branches &branches_,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)
create(const Branches &branches_,
const char *fusepath,
const uint64_t minfreespace,
vector<string> *paths)
{
rwlock::ReadGuard guard(&branches_.lock);
int rv;
int error;
time_t newest;
@ -77,17 +80,19 @@ namespace newest
if(newestbasepath == NULL)
return (errno=error,-1);
paths.push_back(newestbasepath);
paths->push_back(*newestbasepath);
return 0;
}
static
int
action(const Branches &branches_,
const char *fusepath,
vector<const string*> &paths)
action(const Branches &branches_,
const char *fusepath,
vector<string> *paths)
{
rwlock::ReadGuard guard(&branches_.lock);
int rv;
int error;
bool readonly;
@ -122,17 +127,19 @@ namespace newest
if(newestbasepath == NULL)
return (errno=error,-1);
paths.push_back(newestbasepath);
paths->push_back(*newestbasepath);
return 0;
}
static
int
search(const Branches &branches_,
const char *fusepath,
vector<const string*> &paths)
search(const Branches &branches_,
const char *fusepath,
vector<string> *paths)
{
rwlock::ReadGuard guard(&branches_.lock);
time_t newest;
struct stat st;
const Branch *branch;
@ -156,7 +163,7 @@ namespace newest
if(newestbasepath == NULL)
return (errno=ENOENT,-1);
paths.push_back(newestbasepath);
paths->push_back(*newestbasepath);
return 0;
}
@ -167,7 +174,7 @@ Policy::Func::newest(const Category::Enum::Type type,
const Branches &branches_,
const char *fusepath,
const uint64_t minfreespace,
vector<const string*> &paths)
vector<string> *paths)
{
switch(type)
{

6
src/policy_rand.cpp

@ -29,15 +29,15 @@ Policy::Func::rand(const Category::Enum::Type type_,
const Branches &branches_,
const char *fusepath_,
const uint64_t minfreespace_,
vector<const string*> &paths_)
vector<string> *paths_)
{
int rv;
rv = Policy::Func::all(type_,branches_,fusepath_,minfreespace_,paths_);
if(rv == 0)
{
std::random_shuffle(paths_.begin(),paths_.end());
paths_.resize(1);
std::random_shuffle(paths_->begin(),paths_->end());
paths_->resize(1);
}
return rv;

47
src/str.cpp

@ -43,7 +43,25 @@ namespace str
const string &str,
const char delimiter)
{
return split(result,str.c_str(),delimiter);
return str::split(result,str.c_str(),delimiter);
}
void
splitkv(const string &str_,
const char delimiter_,
string *key_,
string *val_)
{
istringstream iss;
std::string key;
std::string val;
iss.str(str_);
std::getline(iss,key,delimiter_);
std::getline(iss,val,'\0');
*key_ = key;
*val_ = val;
}
string
@ -149,8 +167,16 @@ namespace str
}
bool
ends_with(const string &str_,
const string &suffix_)
startswith(const string &str_,
const string &prefix_)
{
return ((str_.size() >= prefix_.size()) &&
(str_.compare(0,prefix_.size(),prefix_) == 0));
}
bool
endswith(const string &str_,
const string &suffix_)
{
if(suffix_.size() > str_.size())
return false;
@ -159,4 +185,19 @@ namespace str
suffix_.rend(),
str_.rbegin());
}
std::string
trim(const std::string &str_)
{
std::string rv;
rv = str_;
while(!rv.empty() && (rv[0] == ' '))
rv.erase(0);
while(!rv.empty() && (rv[rv.size()-1] == ' '))
rv.erase(rv.size()-1);
return rv;
}
}

17
src/str.hpp

@ -30,6 +30,12 @@ namespace str
const std::string &str,
const char delimiter);
void
splitkv(const std::string &str,
const char delimiter,
std::string *key,
std::string *value);
std::string
join(const std::vector<std::string> &vec,
const size_t substridx,
@ -58,6 +64,13 @@ namespace str
const std::string &s1);
bool
ends_with(const std::string &str_,
const std::string &suffix_);
startswith(const std::string &str_,
const std::string &prefix_);
bool
endswith(const std::string &str_,
const std::string &suffix_);
std::string
trim(const std::string &str);
}

58
src/to_string.cpp

@ -0,0 +1,58 @@
/*
ISC License
Copyright (c) 2019, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <string>
#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
namespace str
{
std::string
to(const bool bool_)
{
return (bool_ ? "true" : "false");
}
std::string
to(const int int_)
{
char buf[24];
sprintf(buf,"%d",int_);
return buf;
}
std::string
to(const uint64_t uint64_)
{
char buf[64];
sprintf(buf,"%llu",(unsigned long long)uint64_);
return buf;
}
std::string
to(const std::string &s_)
{
return s_;
}
}

31
src/to_string.hpp

@ -0,0 +1,31 @@
/*
ISC License
Copyright (c) 2019, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
#include <string>
#include <stdint.h>
namespace str
{
std::string to(const bool);
std::string to(const int);
std::string to(const uint64_t);
std::string to(const std::string&);
}

47
src/tofrom_string.hpp

@ -0,0 +1,47 @@
/*
ISC License
Copyright (c) 2019, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
#include <string>
class ToFromString
{
public:
virtual std::string to_string() const = 0;
virtual int from_string(const std::string &) = 0;
};
namespace std
{
inline
static
std::string
to_string(const ToFromString *o_)
{
return o_->to_string();
}
inline
static
std::string
to_string(const ToFromString &o_)
{
return o_.to_string();
}
}

131
src/tofrom_wrapper.hpp

@ -0,0 +1,131 @@
/*
ISC License
Copyright (c) 2020, Antonio SJ Musumeci <trapexit@spawn.link>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
#include "from_string.hpp"
#include "to_string.hpp"
#include "tofrom_string.hpp"
#include <errno.h>
template<typename T>
class ToFromWrapper : public ToFromString
{
public:
int
from_string(const std::string &s_)
{
return str::from(s_,&_data);
}
std::string
to_string(void) const
{
return str::to(_data);
}
public:
ToFromWrapper<T>()
{
}
ToFromWrapper<T>(const T data_)
: _data(data_)
{
}
public:
ToFromWrapper<T>&
operator=(const T data_)
{
_data = data_;
return *this;
}
public:
operator T() const
{
return _data;
}
T*
operator->()
{
return &_data;
}
public:
bool
operator==(const T data_) const
{
return (_data == data_);
}
private:
T _data;
};
template<typename T>
class ROToFromWrapper : public ToFromString
{
public:
int
from_string(const std::string &s_)
{
return -EINVAL;
}
std::string
to_string(void) const
{
return str::to(_data);
}
public:
ROToFromWrapper<T>()
{
}
ROToFromWrapper<T>(const T data_)
: _data(data_)
{
}
public:
operator T() const
{
return _data;
}
T*
operator->()
{
return &_data;
}
public:
bool
operator==(const T data_) const
{
return (_data == data_);
}
private:
T _data;
};
Loading…
Cancel
Save