mirror of
https://github.com/trapexit/mergerfs.git
synced 2026-10-26 19:01:56 -04:00
Rework config, centralize fuse config (#1547)
This commit is contained in:
parent
00e5467e99
commit
727c522fb8
46 changed files with 1294 additions and 1316 deletions
38
libfuse/include/fuse_cfg.hpp
Normal file
38
libfuse/include/fuse_cfg.hpp
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
#pragma once
|
||||
|
||||
#include "int_types.h"
|
||||
|
||||
#include <climits>
|
||||
#include <string>
|
||||
|
||||
#define FUSE_CFG_INVALID_ID -1
|
||||
#define FUSE_CFG_INVALID_UMASK -1
|
||||
|
||||
|
||||
struct fuse_cfg_t
|
||||
{
|
||||
s64 uid = FUSE_CFG_INVALID_ID;
|
||||
bool valid_uid() const;
|
||||
|
||||
s64 gid = FUSE_CFG_INVALID_ID;
|
||||
bool valid_gid() const;
|
||||
|
||||
s64 umask = FUSE_CFG_INVALID_UMASK;
|
||||
bool valid_umask() const;
|
||||
|
||||
s64 remember_nodes = 0;
|
||||
|
||||
bool debug = false;
|
||||
|
||||
int max_background = 0;
|
||||
int congestion_threshold = 0;
|
||||
u32 max_pages = 32;
|
||||
int passthrough_max_stack_depth = 1;
|
||||
|
||||
int read_thread_count = 0;
|
||||
int process_thread_count = -1;
|
||||
int process_thread_queue_depth = 2;
|
||||
std::string pin_threads = "false";
|
||||
};
|
||||
|
||||
extern fuse_cfg_t fuse_cfg;
|
||||
|
|
@ -139,13 +139,8 @@ struct fuse_conn_info
|
|||
{
|
||||
unsigned proto_major;
|
||||
unsigned proto_minor;
|
||||
unsigned max_write;
|
||||
unsigned max_readahead;
|
||||
uint64_t capable;
|
||||
uint64_t want;
|
||||
unsigned max_background;
|
||||
unsigned congestion_threshold;
|
||||
uint16_t max_pages;
|
||||
};
|
||||
|
||||
struct fuse_session;
|
||||
|
|
|
|||
|
|
@ -1,31 +0,0 @@
|
|||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2023, 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>
|
||||
|
||||
int fuse_config_get_read_thread_count();
|
||||
int fuse_config_get_process_thread_count();
|
||||
int fuse_config_get_process_thread_queue_depth();
|
||||
std::string fuse_config_get_pin_threads();
|
||||
|
||||
void fuse_config_set_read_thread_count(int const);
|
||||
void fuse_config_set_process_thread_count(int const);
|
||||
void fuse_config_set_process_thread_queue_depth(int const);
|
||||
void fuse_config_set_pin_threads(std::string const);
|
||||
|
|
@ -18,11 +18,10 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "int_types.h"
|
||||
#include "fuse_msgbuf_t.h"
|
||||
#include "extern_c.h"
|
||||
|
||||
EXTERN_C_BEGIN
|
||||
|
||||
u32 msgbuf_get_pagesize();
|
||||
void msgbuf_set_bufsize(const uint32_t size);
|
||||
uint64_t msgbuf_get_bufsize();
|
||||
|
||||
|
|
@ -35,8 +34,3 @@ void msgbuf_gc_10percent();
|
|||
|
||||
uint64_t msgbuf_alloc_count();
|
||||
uint64_t msgbuf_avail_count();
|
||||
|
||||
void msgbuf_page_align(fuse_msgbuf_t *msgbuf);
|
||||
void msgbuf_write_align(fuse_msgbuf_t *msgbuf);
|
||||
|
||||
EXTERN_C_END
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
#include "mutex.hpp"
|
||||
#include "node.hpp"
|
||||
|
||||
#include "fuse_cfg.hpp"
|
||||
#include "fuse_req.h"
|
||||
#include "fuse_dirents.hpp"
|
||||
#include "fuse_i.h"
|
||||
|
|
@ -70,16 +71,6 @@ static int g_LOG_METRICS = 0;
|
|||
|
||||
struct fuse_config
|
||||
{
|
||||
unsigned int uid;
|
||||
unsigned int gid;
|
||||
unsigned int umask;
|
||||
int remember;
|
||||
int debug;
|
||||
int nogc;
|
||||
int set_mode;
|
||||
int set_uid;
|
||||
int set_gid;
|
||||
int help;
|
||||
};
|
||||
|
||||
struct lock_queue_element
|
||||
|
|
@ -620,7 +611,7 @@ inline
|
|||
int
|
||||
remember_nodes()
|
||||
{
|
||||
return (f.conf.remember > 0);
|
||||
return (fuse_cfg.remember_nodes > 0);
|
||||
}
|
||||
|
||||
static
|
||||
|
|
@ -703,7 +694,7 @@ find_node(uint64_t parent,
|
|||
goto out_err;
|
||||
|
||||
node->nodeid = generate_nodeid(&f.nodeid_gen);
|
||||
if(f.conf.remember)
|
||||
if(fuse_cfg.remember_nodes)
|
||||
inc_nlookup(node);
|
||||
|
||||
if(hash_name(node,parent,name) == -1)
|
||||
|
|
@ -1264,12 +1255,12 @@ void
|
|||
set_stat(uint64_t nodeid,
|
||||
struct stat *stbuf)
|
||||
{
|
||||
if(f.conf.set_mode)
|
||||
stbuf->st_mode = (stbuf->st_mode & S_IFMT) | (0777 & ~f.conf.umask);
|
||||
if(f.conf.set_uid)
|
||||
stbuf->st_uid = f.conf.uid;
|
||||
if(f.conf.set_gid)
|
||||
stbuf->st_gid = f.conf.gid;
|
||||
if(fuse_cfg.valid_uid())
|
||||
stbuf->st_uid = fuse_cfg.uid;
|
||||
if(fuse_cfg.valid_gid())
|
||||
stbuf->st_gid = fuse_cfg.gid;
|
||||
if(fuse_cfg.valid_umask())
|
||||
stbuf->st_mode = (stbuf->st_mode & S_IFMT) | (0777 & ~fuse_cfg.umask);
|
||||
}
|
||||
|
||||
static
|
||||
|
|
@ -3449,7 +3440,7 @@ fuse_prune_some_remembered_nodes(int *offset_)
|
|||
|
||||
checked++;
|
||||
age = (now - fn->time);
|
||||
if(f.conf.remember > age)
|
||||
if(fuse_cfg.remember_nodes > age)
|
||||
break;
|
||||
|
||||
assert(fn->node->nlookup == 1);
|
||||
|
|
@ -3597,38 +3588,9 @@ enum
|
|||
|
||||
static const struct fuse_opt fuse_lib_opts[] =
|
||||
{
|
||||
FUSE_OPT_KEY("-h", KEY_HELP),
|
||||
FUSE_OPT_KEY("--help", KEY_HELP),
|
||||
FUSE_OPT_KEY("debug", FUSE_OPT_KEY_KEEP),
|
||||
FUSE_OPT_KEY("-d", FUSE_OPT_KEY_KEEP),
|
||||
FUSE_LIB_OPT("debug", debug,1),
|
||||
FUSE_LIB_OPT("-d", debug,1),
|
||||
FUSE_LIB_OPT("nogc", nogc,1),
|
||||
FUSE_LIB_OPT("umask=", set_mode,1),
|
||||
FUSE_LIB_OPT("umask=%o", umask,0),
|
||||
FUSE_LIB_OPT("uid=", set_uid,1),
|
||||
FUSE_LIB_OPT("uid=%d", uid,0),
|
||||
FUSE_LIB_OPT("gid=", set_gid,1),
|
||||
FUSE_LIB_OPT("gid=%d", gid,0),
|
||||
FUSE_LIB_OPT("noforget", remember,-1),
|
||||
FUSE_LIB_OPT("remember=%u", remember,0),
|
||||
FUSE_OPT_END
|
||||
};
|
||||
|
||||
static void fuse_lib_help(void)
|
||||
{
|
||||
fprintf(stderr,
|
||||
" -o umask=M set file permissions (octal)\n"
|
||||
" -o uid=N set file owner\n"
|
||||
" -o gid=N set file group\n"
|
||||
" -o noforget never forget cached inodes\n"
|
||||
" -o remember=T remember cached inodes for T seconds (0s)\n"
|
||||
" -o threads=NUM number of worker threads. 0 = autodetect.\n"
|
||||
" Negative values autodetect then divide by\n"
|
||||
" absolute value. default = 0\n"
|
||||
"\n");
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
fuse_lib_opt_proc(void *data,
|
||||
|
|
@ -3636,24 +3598,9 @@ fuse_lib_opt_proc(void *data,
|
|||
int key,
|
||||
struct fuse_args *outargs)
|
||||
{
|
||||
(void)arg; (void)outargs;
|
||||
|
||||
if(key == KEY_HELP)
|
||||
{
|
||||
struct fuse_config *conf = (struct fuse_config *)data;
|
||||
fuse_lib_help();
|
||||
conf->help = 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
fuse_is_lib_option(const char *opt)
|
||||
{
|
||||
return fuse_lowlevel_is_lib_option(opt) || fuse_opt_match(fuse_lib_opts,opt);
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
node_table_init(struct node_table *t)
|
||||
|
|
@ -3872,7 +3819,7 @@ fuse_new(struct fuse_chan *ch,
|
|||
if(fuse_opt_parse(args,&f.conf,fuse_lib_opts,fuse_lib_opt_proc) == -1)
|
||||
goto out_free_fs;
|
||||
|
||||
g_LOG_METRICS = f.conf.debug;
|
||||
g_LOG_METRICS = fuse_cfg.debug;
|
||||
|
||||
f.se = fuse_lowlevel_new_common(args,&llop,sizeof(llop),&f);
|
||||
if(f.se == NULL)
|
||||
|
|
|
|||
21
libfuse/lib/fuse_cfg.cpp
Normal file
21
libfuse/lib/fuse_cfg.cpp
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#include "fuse_cfg.hpp"
|
||||
|
||||
fuse_cfg_t fuse_cfg;
|
||||
|
||||
bool
|
||||
fuse_cfg_t::valid_uid() const
|
||||
{
|
||||
return (uid != FUSE_CFG_INVALID_ID);
|
||||
}
|
||||
|
||||
bool
|
||||
fuse_cfg_t::valid_gid() const
|
||||
{
|
||||
return (gid != FUSE_CFG_INVALID_ID);
|
||||
}
|
||||
|
||||
bool
|
||||
fuse_cfg_t::valid_umask() const
|
||||
{
|
||||
return (umask != FUSE_CFG_INVALID_UMASK);
|
||||
}
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
/*
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2023, 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>
|
||||
|
||||
static int g_READ_THREAD_COUNT = -1;
|
||||
static int g_PROCESS_THREAD_COUNT = -1;
|
||||
static int g_PROCESS_THREAD_QUEUE_DEPTH = -1;
|
||||
static std::string g_PIN_THREADS = {};
|
||||
|
||||
|
||||
int
|
||||
fuse_config_get_read_thread_count()
|
||||
{
|
||||
return g_READ_THREAD_COUNT;
|
||||
}
|
||||
|
||||
void
|
||||
fuse_config_set_read_thread_count(int const v_)
|
||||
{
|
||||
g_READ_THREAD_COUNT = v_;
|
||||
}
|
||||
|
||||
int
|
||||
fuse_config_get_process_thread_count()
|
||||
{
|
||||
return g_PROCESS_THREAD_COUNT;
|
||||
}
|
||||
|
||||
void
|
||||
fuse_config_set_process_thread_count(int const v_)
|
||||
{
|
||||
g_PROCESS_THREAD_COUNT = v_;
|
||||
}
|
||||
|
||||
int
|
||||
fuse_config_get_process_thread_queue_depth()
|
||||
{
|
||||
return g_PROCESS_THREAD_QUEUE_DEPTH;
|
||||
}
|
||||
|
||||
void
|
||||
fuse_config_set_process_thread_queue_depth(int const v_)
|
||||
{
|
||||
g_PROCESS_THREAD_QUEUE_DEPTH = v_;
|
||||
}
|
||||
|
||||
std::string
|
||||
fuse_config_get_pin_threads()
|
||||
{
|
||||
return g_PIN_THREADS;
|
||||
}
|
||||
|
||||
void
|
||||
fuse_config_set_pin_threads(std::string const v_)
|
||||
{
|
||||
g_PIN_THREADS = v_;
|
||||
}
|
||||
|
|
@ -45,11 +45,6 @@ struct fuse_notify_req
|
|||
|
||||
struct fuse_ll
|
||||
{
|
||||
unsigned passthrough_max_stack_depth;
|
||||
int debug;
|
||||
int no_remote_posix_lock;
|
||||
int no_remote_flock;
|
||||
int big_writes;
|
||||
struct fuse_lowlevel_ops op;
|
||||
void *userdata;
|
||||
uid_t owner;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
#include "fuse_kernel.h"
|
||||
#include "fuse_lowlevel.h"
|
||||
|
||||
#include "fuse_config.hpp"
|
||||
#include "fuse_cfg.hpp"
|
||||
#include "fuse_msgbuf.hpp"
|
||||
#include "fuse_ll.hpp"
|
||||
|
||||
|
|
@ -336,10 +336,10 @@ fuse_loop_mt(struct fuse *f_)
|
|||
fuse_populate_maintenance_thread(f_);
|
||||
|
||||
res = fuse_session_loop_mt(fuse_get_session(),
|
||||
fuse_config_get_read_thread_count(),
|
||||
fuse_config_get_process_thread_count(),
|
||||
fuse_config_get_process_thread_queue_depth(),
|
||||
fuse_config_get_pin_threads());
|
||||
fuse_cfg.read_thread_count,
|
||||
fuse_cfg.process_thread_count,
|
||||
fuse_cfg.process_thread_queue_depth,
|
||||
fuse_cfg.pin_threads);
|
||||
|
||||
MaintenanceThread::stop();
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include "lfmp.h"
|
||||
|
||||
#include "debug.hpp"
|
||||
#include "fuse_cfg.hpp"
|
||||
#include "fuse_i.h"
|
||||
#include "fuse_kernel.h"
|
||||
#include "fuse_msgbuf.hpp"
|
||||
|
|
@ -43,7 +44,7 @@
|
|||
#define OFFSET_MAX 0x7fffffffffffffffLL
|
||||
|
||||
#define container_of(ptr, type, member) ({ \
|
||||
const decltype( ((type*)0)->member ) *__mptr = (ptr); \
|
||||
const decltype( ((type*)0)->member ) *__mptr = (ptr); \
|
||||
(type *)( (char*)__mptr - offsetof(type,member) );})
|
||||
|
||||
static size_t pagesize;
|
||||
|
|
@ -1117,11 +1118,17 @@ do_init(fuse_req_t *req,
|
|||
struct fuse_in_header *hdr_)
|
||||
{
|
||||
struct fuse_init_out outarg = {0};
|
||||
struct fuse_init_in *arg = (struct fuse_init_in *) &hdr_[1];
|
||||
struct fuse_init_in *arg = (struct fuse_init_in *)&hdr_[1];
|
||||
struct fuse_ll *f = req->f;
|
||||
size_t bufsize = fuse_chan_bufsize(req->ch);
|
||||
size_t bufsize;
|
||||
uint64_t inargflags;
|
||||
uint64_t outargflags;
|
||||
u32 max_write;
|
||||
u32 max_readahead;
|
||||
|
||||
max_write = UINT_MAX;
|
||||
max_readahead = UINT_MAX;
|
||||
bufsize = fuse_chan_bufsize(req->ch);
|
||||
|
||||
inargflags = 0;
|
||||
outargflags = 0;
|
||||
|
|
@ -1158,8 +1165,8 @@ do_init(fuse_req_t *req,
|
|||
if(inargflags & FUSE_INIT_EXT)
|
||||
inargflags |= (((uint64_t)arg->flags2) << 32);
|
||||
|
||||
if(arg->max_readahead < f->conn.max_readahead)
|
||||
f->conn.max_readahead = arg->max_readahead;
|
||||
if(arg->max_readahead < max_readahead)
|
||||
max_readahead = arg->max_readahead;
|
||||
|
||||
if(inargflags & FUSE_ASYNC_READ)
|
||||
f->conn.capable |= FUSE_CAP_ASYNC_READ;
|
||||
|
|
@ -1209,15 +1216,15 @@ do_init(fuse_req_t *req,
|
|||
else
|
||||
{
|
||||
f->conn.want &= ~FUSE_CAP_ASYNC_READ;
|
||||
f->conn.max_readahead = 0;
|
||||
max_readahead = 0;
|
||||
}
|
||||
|
||||
if(req->f->conn.proto_minor >= 18)
|
||||
f->conn.capable |= FUSE_CAP_IOCTL_DIR;
|
||||
|
||||
if(f->op.getlk && f->op.setlk && !f->no_remote_posix_lock)
|
||||
if(f->op.getlk && f->op.setlk)
|
||||
f->conn.want |= FUSE_CAP_POSIX_LOCKS;
|
||||
if(f->op.flock && !f->no_remote_flock)
|
||||
if(f->op.flock)
|
||||
f->conn.want |= FUSE_CAP_FLOCK_LOCKS;
|
||||
|
||||
if(bufsize < FUSE_MIN_READ_BUFFER)
|
||||
|
|
@ -1228,8 +1235,8 @@ do_init(fuse_req_t *req,
|
|||
}
|
||||
|
||||
bufsize -= pagesize;
|
||||
if(bufsize < f->conn.max_write)
|
||||
f->conn.max_write = bufsize;
|
||||
if(bufsize < max_write)
|
||||
max_write = bufsize;
|
||||
|
||||
f->got_init = 1;
|
||||
if(f->op.init)
|
||||
|
|
@ -1238,10 +1245,11 @@ do_init(fuse_req_t *req,
|
|||
outargflags = outarg.flags;
|
||||
if((inargflags & FUSE_MAX_PAGES) && (f->conn.want & FUSE_CAP_MAX_PAGES))
|
||||
{
|
||||
outargflags |= FUSE_MAX_PAGES;
|
||||
outarg.max_pages = f->conn.max_pages;
|
||||
outargflags |= FUSE_MAX_PAGES;
|
||||
outarg.max_pages = fuse_cfg.max_pages;
|
||||
|
||||
msgbuf_set_bufsize(outarg.max_pages + 1);
|
||||
msgbuf_set_bufsize(outarg.max_pages);
|
||||
max_write = (msgbuf_get_pagesize() * outarg.max_pages);
|
||||
}
|
||||
|
||||
if(f->conn.want & FUSE_CAP_ASYNC_READ)
|
||||
|
|
@ -1288,7 +1296,7 @@ do_init(fuse_req_t *req,
|
|||
if(f->conn.want & FUSE_CAP_PASSTHROUGH)
|
||||
{
|
||||
outargflags |= FUSE_PASSTHROUGH;
|
||||
outarg.max_stack_depth = f->passthrough_max_stack_depth;
|
||||
outarg.max_stack_depth = fuse_cfg.passthrough_max_stack_depth;
|
||||
}
|
||||
|
||||
if(inargflags & FUSE_INIT_EXT)
|
||||
|
|
@ -1299,21 +1307,19 @@ do_init(fuse_req_t *req,
|
|||
|
||||
outarg.flags = outargflags;
|
||||
|
||||
outarg.max_readahead = f->conn.max_readahead;
|
||||
outarg.max_write = f->conn.max_write;
|
||||
outarg.max_readahead = max_readahead;
|
||||
outarg.max_write = max_write;
|
||||
if(f->conn.proto_minor >= 13)
|
||||
{
|
||||
if(f->conn.max_background >= (1 << 16))
|
||||
f->conn.max_background = (1 << 16) - 1;
|
||||
if(f->conn.congestion_threshold > f->conn.max_background)
|
||||
f->conn.congestion_threshold = f->conn.max_background;
|
||||
if(!f->conn.congestion_threshold)
|
||||
{
|
||||
f->conn.congestion_threshold = f->conn.max_background * 3 / 4;
|
||||
}
|
||||
if(fuse_cfg.max_background >= (1 << 16))
|
||||
fuse_cfg.max_background = ((1 << 16) - 1);
|
||||
if(fuse_cfg.congestion_threshold > fuse_cfg.max_background)
|
||||
fuse_cfg.congestion_threshold = fuse_cfg.max_background;
|
||||
if(!fuse_cfg.congestion_threshold)
|
||||
fuse_cfg.congestion_threshold = (fuse_cfg.max_background * 3 / 4);
|
||||
|
||||
outarg.max_background = f->conn.max_background;
|
||||
outarg.congestion_threshold = f->conn.congestion_threshold;
|
||||
outarg.max_background = fuse_cfg.max_background;
|
||||
outarg.congestion_threshold = fuse_cfg.congestion_threshold;
|
||||
}
|
||||
|
||||
if(f->conn.proto_minor >= 23)
|
||||
|
|
@ -1780,86 +1786,6 @@ fuse_ll_funcs[FUSE_OPCODE_LEN] =
|
|||
|
||||
#define FUSE_MAXOPS (sizeof(fuse_ll_funcs) / sizeof(fuse_ll_funcs[0]))
|
||||
|
||||
enum {
|
||||
KEY_HELP,
|
||||
KEY_VERSION,
|
||||
};
|
||||
|
||||
static const struct fuse_opt fuse_ll_opts[] =
|
||||
{
|
||||
{ "debug", offsetof(struct fuse_ll, debug), 1 },
|
||||
{ "-d", offsetof(struct fuse_ll, debug), 1 },
|
||||
{ "max_readahead=%u", offsetof(struct fuse_ll, conn.max_readahead), 0 },
|
||||
{ "max_background=%u", offsetof(struct fuse_ll, conn.max_background), 0 },
|
||||
{ "congestion_threshold=%u",
|
||||
offsetof(struct fuse_ll, conn.congestion_threshold), 0 },
|
||||
{ "no_remote_lock", offsetof(struct fuse_ll, no_remote_posix_lock), 1},
|
||||
{ "no_remote_lock", offsetof(struct fuse_ll, no_remote_flock), 1},
|
||||
{ "no_remote_flock", offsetof(struct fuse_ll, no_remote_flock), 1},
|
||||
{ "no_remote_posix_lock", offsetof(struct fuse_ll, no_remote_posix_lock), 1},
|
||||
{ "passthrough-max-stack-depth=%u", offsetof(struct fuse_ll, passthrough_max_stack_depth), 0},
|
||||
FUSE_OPT_KEY("max_read=", FUSE_OPT_KEY_DISCARD),
|
||||
FUSE_OPT_KEY("-h", KEY_HELP),
|
||||
FUSE_OPT_KEY("--help", KEY_HELP),
|
||||
FUSE_OPT_KEY("-V", KEY_VERSION),
|
||||
FUSE_OPT_KEY("--version", KEY_VERSION),
|
||||
FUSE_OPT_END
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
fuse_ll_version(void)
|
||||
{
|
||||
fprintf(stderr, "using FUSE kernel interface version %i.%i\n",
|
||||
FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
fuse_ll_help(void)
|
||||
{
|
||||
fprintf(stderr,
|
||||
" -o max_readahead=N set maximum readahead\n"
|
||||
" -o max_background=N set number of maximum background requests\n"
|
||||
" -o congestion_threshold=N set kernel's congestion threshold\n"
|
||||
" -o no_remote_lock disable remote file locking\n"
|
||||
" -o no_remote_flock disable remote file locking (BSD)\n"
|
||||
" -o no_remote_posix_lock disable remove file locking (POSIX)\n"
|
||||
);
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
fuse_ll_opt_proc(void *data,
|
||||
const char *arg,
|
||||
int key,
|
||||
struct fuse_args *outargs)
|
||||
{
|
||||
(void) data; (void) outargs;
|
||||
|
||||
switch (key)
|
||||
{
|
||||
case KEY_HELP:
|
||||
fuse_ll_help();
|
||||
break;
|
||||
|
||||
case KEY_VERSION:
|
||||
fuse_ll_version();
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "fuse: unknown option `%s'\n", arg);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
fuse_lowlevel_is_lib_option(const char *opt)
|
||||
{
|
||||
return fuse_opt_match(fuse_ll_opts, opt);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
fuse_ll_destroy(void *data)
|
||||
|
|
@ -2032,6 +1958,23 @@ fuse_ll_buf_process_read_init(struct fuse_session *se_,
|
|||
return;
|
||||
}
|
||||
|
||||
static const struct fuse_opt fuse_ll_opts[] =
|
||||
{
|
||||
FUSE_OPT_END
|
||||
};
|
||||
|
||||
static
|
||||
int
|
||||
fuse_ll_opt_proc(void *data_,
|
||||
const char *arg_,
|
||||
int key_,
|
||||
struct fuse_args *outargs_)
|
||||
{
|
||||
fmt::print(stderr, "* ERROR: unknown option '{}'\n", arg_);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* always call fuse_lowlevel_new_common() internally, to work around a
|
||||
* misfeature in the FreeBSD runtime linker, which links the old
|
||||
|
|
@ -2060,9 +2003,6 @@ fuse_lowlevel_new_common(struct fuse_args *args,
|
|||
goto out;
|
||||
}
|
||||
|
||||
f->conn.max_write = UINT_MAX;
|
||||
f->conn.max_readahead = UINT_MAX;
|
||||
f->passthrough_max_stack_depth = 2;
|
||||
list_init_nreq(&f->notify_list);
|
||||
f->notify_ctr = 1;
|
||||
mutex_init(&f->lock);
|
||||
|
|
@ -2075,7 +2015,7 @@ fuse_lowlevel_new_common(struct fuse_args *args,
|
|||
goto out_free;
|
||||
}
|
||||
|
||||
if(fuse_opt_parse(args, f, fuse_ll_opts, fuse_ll_opt_proc) == -1)
|
||||
if(fuse_opt_parse(args,NULL,fuse_ll_opts,fuse_ll_opt_proc) == -1)
|
||||
goto out_key_destroy;
|
||||
|
||||
memcpy(&f->op, op, op_size);
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
*/
|
||||
|
||||
#include "fuse_msgbuf.hpp"
|
||||
|
||||
#include "fuse.h"
|
||||
#include "fuse_kernel.h"
|
||||
|
||||
|
|
@ -42,22 +43,34 @@ msgbuf_get_bufsize()
|
|||
return g_BUFSIZE;
|
||||
}
|
||||
|
||||
u32
|
||||
msgbuf_get_pagesize()
|
||||
{
|
||||
return g_PAGESIZE;
|
||||
}
|
||||
|
||||
// +1 page so that it is used for the "header" of the allocation as
|
||||
// well as to allow for offseting for write requests to be page
|
||||
// aligned. +1 again for fuse header as the max_pages value is for the
|
||||
// body.
|
||||
void
|
||||
msgbuf_set_bufsize(const uint32_t size_in_pages_)
|
||||
{
|
||||
g_BUFSIZE = ((size_in_pages_ + 1) * g_PAGESIZE);
|
||||
g_BUFSIZE = ((size_in_pages_ + 2) * g_PAGESIZE);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
msgbuf_page_align(fuse_msgbuf_t *msgbuf_)
|
||||
_msgbuf_page_align(fuse_msgbuf_t *msgbuf_)
|
||||
{
|
||||
msgbuf_->mem = (char*)msgbuf_;
|
||||
msgbuf_->mem += g_PAGESIZE;
|
||||
msgbuf_->size = (g_BUFSIZE - g_PAGESIZE);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
msgbuf_write_align(fuse_msgbuf_t *msgbuf_)
|
||||
_msgbuf_write_align(fuse_msgbuf_t *msgbuf_)
|
||||
{
|
||||
msgbuf_->mem = (char*)msgbuf_;
|
||||
msgbuf_->mem += g_PAGESIZE;
|
||||
|
|
@ -72,8 +85,8 @@ void
|
|||
_msgbuf_constructor()
|
||||
{
|
||||
g_PAGESIZE = sysconf(_SC_PAGESIZE);
|
||||
// FUSE_MAX_MAX_PAGES for payload + 1 for message header
|
||||
msgbuf_set_bufsize(FUSE_DEFAULT_MAX_MAX_PAGES + 1);
|
||||
|
||||
msgbuf_set_bufsize(FUSE_DEFAULT_MAX_MAX_PAGES);
|
||||
}
|
||||
|
||||
static
|
||||
|
|
@ -86,7 +99,7 @@ _msgbuf_destructor()
|
|||
|
||||
static
|
||||
void*
|
||||
page_aligned_malloc(const uint64_t size_)
|
||||
_page_aligned_malloc(const uint64_t size_)
|
||||
{
|
||||
int rv;
|
||||
void *buf = NULL;
|
||||
|
|
@ -111,7 +124,7 @@ _msgbuf_alloc(msgbuf_setup_func_t setup_func_)
|
|||
{
|
||||
g_MUTEX.unlock();
|
||||
|
||||
msgbuf = (fuse_msgbuf_t*)page_aligned_malloc(g_BUFSIZE);
|
||||
msgbuf = (fuse_msgbuf_t*)_page_aligned_malloc(g_BUFSIZE);
|
||||
if(msgbuf == NULL)
|
||||
return NULL;
|
||||
|
||||
|
|
@ -135,13 +148,13 @@ _msgbuf_alloc(msgbuf_setup_func_t setup_func_)
|
|||
fuse_msgbuf_t*
|
||||
msgbuf_alloc()
|
||||
{
|
||||
return _msgbuf_alloc(msgbuf_write_align);
|
||||
return _msgbuf_alloc(_msgbuf_write_align);
|
||||
}
|
||||
|
||||
fuse_msgbuf_t*
|
||||
msgbuf_alloc_page_aligned()
|
||||
{
|
||||
return _msgbuf_alloc(msgbuf_page_align);
|
||||
return _msgbuf_alloc(_msgbuf_page_align);
|
||||
}
|
||||
|
||||
static
|
||||
|
|
|
|||
|
|
@ -41,74 +41,43 @@ struct fuse_opt fuse_helper_opts[] =
|
|||
FUSE_HELPER_OPT("-f", foreground),
|
||||
FUSE_HELPER_OPT("fsname=", nodefault_subtype),
|
||||
FUSE_HELPER_OPT("subtype=", nodefault_subtype),
|
||||
FUSE_OPT_KEY("-h", KEY_HELP),
|
||||
FUSE_OPT_KEY("--help", KEY_HELP),
|
||||
FUSE_OPT_KEY("-ho", KEY_HELP_NOHEADER),
|
||||
FUSE_OPT_KEY("-V", KEY_VERSION),
|
||||
FUSE_OPT_KEY("--version", KEY_VERSION),
|
||||
FUSE_OPT_KEY("-d", FUSE_OPT_KEY_KEEP),
|
||||
FUSE_OPT_KEY("debug", FUSE_OPT_KEY_KEEP),
|
||||
FUSE_OPT_KEY("fsname=", FUSE_OPT_KEY_KEEP),
|
||||
FUSE_OPT_KEY("subtype=", FUSE_OPT_KEY_KEEP),
|
||||
FUSE_OPT_END
|
||||
};
|
||||
|
||||
static void usage(const char *progname)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"usage: %s mountpoint [options]\n\n", progname);
|
||||
fprintf(stderr,
|
||||
"general options:\n"
|
||||
" -o opt,[opt...] mount options\n"
|
||||
" -h --help print help\n"
|
||||
" -V --version print version\n"
|
||||
"\n");
|
||||
}
|
||||
|
||||
static void helper_help(void)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"FUSE options:\n"
|
||||
" -d -o debug enable debug output (implies -f)\n"
|
||||
" -f foreground operation\n"
|
||||
"\n"
|
||||
);
|
||||
}
|
||||
|
||||
static int fuse_helper_opt_proc(void *data, const char *arg, int key,
|
||||
struct fuse_args *outargs)
|
||||
static
|
||||
int
|
||||
fuse_helper_opt_proc(void *data,
|
||||
const char *arg,
|
||||
int key,
|
||||
struct fuse_args *outargs)
|
||||
{
|
||||
struct helper_opts *hopts = data;
|
||||
|
||||
switch (key) {
|
||||
case KEY_HELP:
|
||||
usage(outargs->argv[0]);
|
||||
/* fall through */
|
||||
switch (key)
|
||||
{
|
||||
case FUSE_OPT_KEY_NONOPT:
|
||||
if(!hopts->mountpoint)
|
||||
{
|
||||
char mountpoint[PATH_MAX];
|
||||
if(realpath(arg, mountpoint) == NULL)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"fuse: bad mount point `%s': %s\n",
|
||||
arg, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
return fuse_opt_add_opt(&hopts->mountpoint, mountpoint);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "fuse: invalid argument `%s'\n", arg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
case KEY_HELP_NOHEADER:
|
||||
helper_help();
|
||||
return fuse_opt_add_arg(outargs, "-h");
|
||||
|
||||
case KEY_VERSION:
|
||||
return 1;
|
||||
|
||||
case FUSE_OPT_KEY_NONOPT:
|
||||
if (!hopts->mountpoint) {
|
||||
char mountpoint[PATH_MAX];
|
||||
if (realpath(arg, mountpoint) == NULL) {
|
||||
fprintf(stderr,
|
||||
"fuse: bad mount point `%s': %s\n",
|
||||
arg, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
return fuse_opt_add_opt(&hopts->mountpoint, mountpoint);
|
||||
} else {
|
||||
fprintf(stderr, "fuse: invalid argument `%s'\n", arg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
default:
|
||||
return 1;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -191,36 +191,37 @@ static int fuse_mount_opt_proc(void *data, const char *arg, int key,
|
|||
{
|
||||
struct mount_opts *mo = data;
|
||||
|
||||
switch (key) {
|
||||
case KEY_RO:
|
||||
arg = "ro";
|
||||
/* fall through */
|
||||
case KEY_KERN_FLAG:
|
||||
set_mount_flag(arg, &mo->flags);
|
||||
return 0;
|
||||
switch (key)
|
||||
{
|
||||
case KEY_RO:
|
||||
arg = "ro";
|
||||
/* fall through */
|
||||
case KEY_KERN_FLAG:
|
||||
set_mount_flag(arg, &mo->flags);
|
||||
return 0;
|
||||
|
||||
case KEY_KERN_OPT:
|
||||
return fuse_opt_add_opt(&mo->kernel_opts, arg);
|
||||
case KEY_KERN_OPT:
|
||||
return fuse_opt_add_opt(&mo->kernel_opts, arg);
|
||||
|
||||
case KEY_FUSERMOUNT_OPT:
|
||||
return fuse_opt_add_opt_escaped(&mo->fusermount_opts, arg);
|
||||
case KEY_FUSERMOUNT_OPT:
|
||||
return fuse_opt_add_opt_escaped(&mo->fusermount_opts, arg);
|
||||
|
||||
case KEY_SUBTYPE_OPT:
|
||||
return fuse_opt_add_opt(&mo->subtype_opt, arg);
|
||||
case KEY_SUBTYPE_OPT:
|
||||
return fuse_opt_add_opt(&mo->subtype_opt, arg);
|
||||
|
||||
case KEY_MTAB_OPT:
|
||||
return fuse_opt_add_opt(&mo->mtab_opts, arg);
|
||||
case KEY_MTAB_OPT:
|
||||
return fuse_opt_add_opt(&mo->mtab_opts, arg);
|
||||
|
||||
case KEY_HELP:
|
||||
mount_help();
|
||||
mo->ishelp = 1;
|
||||
break;
|
||||
case KEY_HELP:
|
||||
mount_help();
|
||||
mo->ishelp = 1;
|
||||
break;
|
||||
|
||||
case KEY_VERSION:
|
||||
mount_version();
|
||||
mo->ishelp = 1;
|
||||
break;
|
||||
}
|
||||
case KEY_VERSION:
|
||||
mount_version();
|
||||
mo->ishelp = 1;
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
@ -542,8 +543,8 @@ int fuse_kern_mount(const char *mountpoint, struct fuse_args *args)
|
|||
memset(&mo, 0, sizeof(mo));
|
||||
mo.flags = MS_NOSUID | MS_NODEV;
|
||||
|
||||
if (args &&
|
||||
fuse_opt_parse(args, &mo, fuse_mount_opts, fuse_mount_opt_proc) == -1)
|
||||
if(args &&
|
||||
fuse_opt_parse(args, &mo, fuse_mount_opts, fuse_mount_opt_proc) == -1)
|
||||
return -1;
|
||||
|
||||
res = 0;
|
||||
|
|
|
|||
|
|
@ -41,10 +41,10 @@ transparently enable page caching when mmap is requested. This means
|
|||
it should be safe to set `cache.files=off`. However, on Linux v6.5 and
|
||||
below you will need to configure `cache.files` as you need.
|
||||
|
||||
If [passthrough](passthrough.md) is enabled so must be page
|
||||
caching. mergerfs will set `cache.files=auto-full` if `passthrough` is
|
||||
enabled. And when using `passthrough` the there is no double page
|
||||
caching since it is in fact passing through the IO.
|
||||
If [passthrough.io](passthrough.md) is enabled so must page
|
||||
caching. mergerfs will set `cache.files=auto-full` if `passthrough.io`
|
||||
is enabled. And when using `passthrough.io` the there is no double
|
||||
page caching since it is in fact passing through the IO.
|
||||
|
||||
|
||||
[^1]: This is not unique to mergerfs and affects all FUSE
|
||||
|
|
|
|||
|
|
@ -3,19 +3,21 @@
|
|||
These are old, deprecated options which may no longer have any
|
||||
function or have been replaced. **They should not be used.**
|
||||
|
||||
* **direct_io**: Bypass page cache. Use `cache.files=off`
|
||||
instead.
|
||||
* **kernel_cache**: Do not invalidate data cache on file open. Use
|
||||
`cache.files=full` instead.
|
||||
* **auto_cache**: Invalidate data cache if file mtime or
|
||||
size change. Use `cache.files=auto-full` instead. (default: false)
|
||||
* **async_read**: Perform reads asynchronously. Use
|
||||
`async_read=true` instead.
|
||||
* **sync_read**: Perform reads synchronously. Use
|
||||
`async_read=false` instead.
|
||||
* **splice_read**: Does nothing.
|
||||
* **splice_write**: Does nothing.
|
||||
* **splice_move**: Does nothing.
|
||||
* **allow_other**: mergerfs v2.35.0 and above sets this FUSE option
|
||||
automatically if running as root.
|
||||
* **use_ino**: Effectively replaced with `inodecalc`.
|
||||
* **async_read**: Use `async_read=true`.
|
||||
* **atomic_o_trunc**: Does nothing.
|
||||
* **big_writes**: Does nothing.
|
||||
* **attr_timeout**: Use `cache.attr`.
|
||||
* **auto_cache**: Use `cache.files=auto-full`.
|
||||
* **direct_io**: Use `cache.files=off`.
|
||||
* **entry_timeout**: Use `cache.entry`.
|
||||
* **hard_remove**: Does nothing.
|
||||
* **kernel_cache**: Use `cache.files=full`.
|
||||
* **negative_entry**: Use `cache.negative_entry`.
|
||||
* **nonempty**: Does nothing.
|
||||
* **splice_move**: Does nothing.
|
||||
* **splice_read**: Does nothing.
|
||||
* **splice_write**: Does nothing.
|
||||
* **sync_read**: Use `async_read=false`.
|
||||
* **use_ino**: Use `inodecalc`.
|
||||
|
|
|
|||
|
|
@ -30,9 +30,11 @@ config file.
|
|||
## mount options
|
||||
|
||||
* **config**: Path to a config file. Same arguments as below in
|
||||
key=val / ini style format.
|
||||
`key` or `key=val` style format. `#` used for comments.
|
||||
* **[branches](branches.md)**: Colon delimited list of branches. Used
|
||||
primarily in config file.
|
||||
primarily in a config file.
|
||||
* **mountpoint=DIRPATH**: The path to the mountpoint. Used primarily
|
||||
in a config file.
|
||||
* **[branches-mount-timeout](branches-mount-timeout.md)=UINT**: Number
|
||||
of seconds to wait at startup for branches to be a mount other than
|
||||
the mountpoint's filesystem. (default: 0)
|
||||
|
|
@ -95,7 +97,7 @@ config file.
|
|||
xattrs. Default is to passthrough xattr requests. 'noattr' will
|
||||
short circuit as if nothing exists. 'nosys' will respond with ENOSYS
|
||||
as if xattrs are not supported or disabled. (default: passthrough)
|
||||
* **[link_cow](link_cow.md)=BOOL**: When enabled if a regular file is
|
||||
* **[link-cow](link_cow.md)=BOOL**: When enabled if a regular file is
|
||||
opened which has a link count > 1 it will copy the file to a
|
||||
temporary file and rename over the original. Breaking the link and
|
||||
providing a basic copy-on-write function similar to
|
||||
|
|
@ -130,11 +132,11 @@ config file.
|
|||
* **[fuse_msg_size](fuse_msg_size.md)=PAGESIZE**: Set the max number of
|
||||
pages per FUSE message. Only available on Linux >= 4.20 and ignored
|
||||
otherwise. (min: 1; max: 65535; default: "1M")
|
||||
* **[threads](threads.md)=INT**: Number of threads to use. When used
|
||||
alone (`process-thread-count=-1`) it sets the number of threads
|
||||
reading and processing FUSE messages. When used together it sets the
|
||||
number of threads reading from FUSE. When set to zero it will
|
||||
attempt to discover and use the number of logical cores. If the
|
||||
* **[read-thread-count](threads.md)=INT**: Number of threads to
|
||||
use. When used alone (`process-thread-count=-1`) it sets the number
|
||||
of threads reading and processing FUSE messages. When used together
|
||||
it sets the number of threads reading from FUSE. When set to zero it
|
||||
will attempt to discover and use the number of logical cores. If the
|
||||
thread count is set negative it will look up the number of cores
|
||||
then divide by the absolute value. ie. threads=-2 on an 8 core
|
||||
machine will result in 8 / 2 = 4 threads. There will always be at
|
||||
|
|
@ -142,7 +144,6 @@ config file.
|
|||
`process-thread-count` then it will try to pick reasonable values
|
||||
based on CPU thread count. NOTE: higher number of threads increases
|
||||
parallelism but usually decreases throughput. (default: 0)
|
||||
* **[read-thread-count](threads.md)=INT**: Alias for `threads`.
|
||||
* **[process-thread-count](threads.md)=INT**: Enables separate thread
|
||||
pool to asynchronously process FUSE requests. In this mode
|
||||
`read-thread-count` refers to the number of threads reading FUSE
|
||||
|
|
@ -153,7 +154,7 @@ config file.
|
|||
time. Meaning the total memory usage of the queues is queue depth
|
||||
multiplied by the number of process threads plus read thread
|
||||
count. 0 sets the depth to the same as the process thread
|
||||
count. (default: 0)
|
||||
count. (default: 2)
|
||||
* **[pin-threads](pin-threads.md)=STR**: Selects a strategy to pin
|
||||
threads to CPUs (default: unset)
|
||||
* **[flush-on-close](flush-on-close.md)=never|always|opened-for-write**:
|
||||
|
|
@ -170,9 +171,9 @@ config file.
|
|||
* **[func.FUNC](functions_categories_policies.md)=POLICY**: Sets the
|
||||
specific FUSE function's policy. See below for the list of value
|
||||
types. Example: **func.getattr=newest**
|
||||
* **[func.readdir](func_readdir.md)=seq|cosr|cor|cosr:INT|cor:INT**: Sets `readdir`
|
||||
policy. INT value sets the number of threads to use for
|
||||
concurrency. (default: seq)
|
||||
* **[func.readdir](func_readdir.md)=seq|cosr|cor|cosr:INT|cor:INT**:
|
||||
Sets `readdir` policy. INT value sets the number of threads to use
|
||||
for concurrency. (default: seq)
|
||||
* **[category.action](functions_categories_policies.md)=POLICY**: Sets
|
||||
policy of all FUSE functions in the action category. (default:
|
||||
epall)
|
||||
|
|
@ -206,15 +207,23 @@ config file.
|
|||
with `cache.files=per-process` (if the process is not in
|
||||
`process-names`) or `cache.files=off`. (Is a kernel feature added in
|
||||
v6.2) (default: true)
|
||||
* **[passthrough](passthrough.md)**: Enable [FUSE IO
|
||||
* **[passthrough.io](passthrough.md)=ENUM**: Enable [FUSE IO
|
||||
passthrough](https://kernelnewbies.org/Linux_6.9#Faster_FUSE_I.2FO)
|
||||
if available. (default: off)
|
||||
* **gid-cache-expire-timeout**: Number of seconds till supplemental
|
||||
group data is refreshed in the [GID
|
||||
* **passthrough.max-stack-depth=INT**: Set to `1` another filesystem
|
||||
can be stacked on mergerfs. Set to `2` to have mergerfs stacked over
|
||||
another filesystem. (default: 1)
|
||||
* **gid-cache.expire-timeout=INT**: Number of seconds till
|
||||
supplemental group data is refreshed in the [GID
|
||||
cache](../known_issues_bugs.md#supplemental-user-groups). (default:
|
||||
3600)
|
||||
* **gid-cache-remove-timeout**: Number of seconds to wait till cached
|
||||
data is removed due to lack of usage. (default: 43200)
|
||||
* **gid-cache.remove-timeout=INT**: Number of seconds to wait till
|
||||
cached data is removed due to lack of usage. (default: 43200)
|
||||
* **remember-nodes=INT**: The number of seconds to keep the internal
|
||||
representation of a file once the OS tells mergerfs it is no longer
|
||||
needed. Really only needed for [exporting mergerfs via
|
||||
NFS](../remote_filesystems.md) (default: 0)
|
||||
* **noforget**: Effectively sets `remember-nodes` to infinity.
|
||||
|
||||
**NOTE:** Options are evaluated in the order listed so if the options
|
||||
are **func.rmdir=rand,category.action=ff** the **action** category
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# passthrough
|
||||
# passthrough.io
|
||||
|
||||
* default: `off`
|
||||
* arguments:
|
||||
|
|
@ -14,7 +14,7 @@ to act as an active proxy for all read and write requests. This
|
|||
results in, at times, significant overhead compared to direct
|
||||
interaction with the underlying filesystems. Not because `mergerfs` is
|
||||
doing anything particularly slow but because the additional
|
||||
communication and data transfers required. With the `passthrough`
|
||||
communication and data transfers required. With the `passthrough.io`
|
||||
feature `mergerfs` is able to instruct the kernel to perform reads and
|
||||
writes directly on the underlying file. Bypassing `mergerfs` entirely
|
||||
(specifically and only for reads and writes) and thereby providing
|
||||
|
|
@ -37,48 +37,47 @@ features related to read/write are affected.
|
|||
requests being sent to `mergerfs` there is little reason to have
|
||||
larger message sizes since the only other message larger than 1
|
||||
page, directory reading, currently has a small fixed buffer size.
|
||||
* parallel-direct-writes: Not only is `direct-io` and `passthrough`
|
||||
* parallel-direct-writes: Not only is `direct-io` and `passthrough.io`
|
||||
mutually exclusive but since `mergerfs` no longer receives write
|
||||
requests in passthrough mode then there is no parallel writes
|
||||
possible.
|
||||
* [cache.writeback](cache.md): FUSE's writeback caching is
|
||||
incompatible with `passthrough`. If `cache.writeback=true` when
|
||||
enabling `passthrough` it will be reset to `false`.
|
||||
* [cache.files](cache.md): Must be enabled for `passthrough` to
|
||||
incompatible with `passthrough.io`. If `cache.writeback=true` when
|
||||
enabling `passthrough.io` it will be reset to `false`.
|
||||
* [cache.files](cache.md): Must be enabled for `passthrough.io` to
|
||||
work. When `cache.files=off` FUSE's `direct-io` mode is enabled
|
||||
which overrides `passthrough.` Meaning `cache.files` should be set
|
||||
to `partial`, `full` or `auto-full` to use `passthrough`. If
|
||||
`cache.files` is set to `off` when using `passthrough` it will
|
||||
which overrides `passthrough.io`. Meaning `cache.files` should be
|
||||
set to `partial`, `full` or `auto-full` to use `passthrough.io`. If
|
||||
`cache.files` is set to `off` when using `passthrough.io` it will
|
||||
reset it to `auto-full`.
|
||||
|
||||
Technically `mergerfs` has the ability to choose options like
|
||||
`passthrough`, `direct-io`, and page caching independently for every
|
||||
file opened. However, at the moment there is no use case for picking
|
||||
and choosing which to enable outside `cache.files=per-process` (which
|
||||
is largely unnecessary on Linux v6.6 and above. See
|
||||
`passthrough.io`, `direct-io`, and page caching independently for
|
||||
every file opened. However, at the moment there is no use case for
|
||||
picking and choosing which to enable outside `cache.files=per-process`
|
||||
(which is largely unnecessary on Linux v6.6 and above. See
|
||||
[direct-io-allow-mmap](options.md)) If such a use case arises please
|
||||
[reach out to the author](../support.md) to discuss.
|
||||
|
||||
Unlike [preload.so](../tooling.md#preloadso), `passthrough` will work for
|
||||
any software interacting with `mergerfs`. However, `passthrough`
|
||||
requires Linux v6.9 or above to work.
|
||||
Unlike [preload.so](../tooling.md#preloadso), `passthrough.io` will
|
||||
work for any software interacting with `mergerfs`. However,
|
||||
`passthrough.io` requires Linux v6.9 or above to work.
|
||||
|
||||
**NOTE:** This feature will **ONLY** work if `mergerfs` is running as
|
||||
`root` as currently only `root` is allowed to leverage the kernel
|
||||
feature.
|
||||
|
||||
**NOTE:** If a file has been opened and `passthrough` enabled, while
|
||||
that file is open, if another open request is made `mergerfs` must
|
||||
also enable `passthrough` for the second open request. This is a
|
||||
limitation of how the passthrough feature works. Though there is no
|
||||
known usecase where this is useful.
|
||||
**NOTE:** If a file has been opened and `passthrough.io` enabled,
|
||||
while that file is open, if another open request is made `mergerfs`
|
||||
must also enable `passthrough.io` for the second open request. This is
|
||||
a limitation of how the passthrough feature works.
|
||||
|
||||
**NOTE:** In order to add `passthrough` feature to `mergerfs` it was
|
||||
necessary to remove the "feature" where mergerfs could open the same
|
||||
file on different branches. Such as using `func.open=rand` and having
|
||||
multiple files at the same relative path across different
|
||||
**NOTE:** In order to add `passthrough.io` feature to `mergerfs` it
|
||||
was necessary to remove the "feature" where mergerfs could open the
|
||||
same file on different branches. Such as using `func.open=rand` and
|
||||
having multiple files at the same relative path across different
|
||||
branches. This "feature" was very very rarely used and it was
|
||||
impossible to support `passthrough` without changing the behavior.
|
||||
impossible to support `passthrough.io` without changing that behavior.
|
||||
|
||||
|
||||
## Alternatives
|
||||
|
|
|
|||
|
|
@ -33,11 +33,11 @@ proxying may be added in a future release.
|
|||
## Conflicts With Other Options
|
||||
|
||||
If using [IO passthrough](passthrough.md) there is no reason to set
|
||||
this value to `true`. However, since `passthrough` leads to mergerfs
|
||||
IO calls being bypassed entirely will have no impact on performance or
|
||||
behavior regardless.
|
||||
this value to `true`. However, since `passthrough.io` leads to
|
||||
mergerfs IO calls being bypassed entirely will have no impact on
|
||||
performance or behavior regardless.
|
||||
|
||||
When using `passthrough` the IO priority of the client app would be
|
||||
When using `passthrough.io` the IO priority of the client app would be
|
||||
used directly given the IO is passed through.
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -18,37 +18,71 @@ mergerfs [does not impact](usage_and_functionality.md) the underlying
|
|||
filesystems and can be added or removed without any impact it is
|
||||
extremely easy to test and experiment with different settings.
|
||||
|
||||
Additional reading:
|
||||
|
||||
* [Config/Options](../config/options.md)
|
||||
|
||||
|
||||
## What create policy should I use?
|
||||
|
||||
If it isn't clear to you then you should use `pfrd` as described in
|
||||
the [quickstart guide](../quickstart.md).
|
||||
|
||||
|
||||
## Why is pfrd the default create policy?
|
||||
|
||||
Originally the default was `epmfs` however it was found to cause
|
||||
significant confusion among new users who expected mergerfs to always
|
||||
choose a branch with available space. For mergerfs v2.41.0 it was
|
||||
decided to change the default to the less restrictive policy `pfrd`.
|
||||
significant confusion among new users who assumed mergerfs to behave
|
||||
like other software which always choose a branch with most available
|
||||
space without any filtering or restrictions. The reason for using
|
||||
`epmfs` was to allow people to use mergerfs without impacting the
|
||||
layout of their data across the branches. Keeping the total entropy of
|
||||
the system in check. This, however, was not understood by users who
|
||||
did not read the documentation and caused significant support burdens.
|
||||
|
||||
For mergerfs v2.41.0 it was decided to change the default to `pfrd` to
|
||||
minimize the support burden.
|
||||
|
||||
`pfrd` was chosen because it prioritizes placement to branches based
|
||||
on free space (percentage wise) without overloading a specific branch
|
||||
as `mfs`, `lus`, or other policies could when a singular branch has
|
||||
significantly more free space. Since it is impossible to know the
|
||||
usage and access patterns of the filesystem spreading out files across
|
||||
the branches also spreads the load and therefore good for general use
|
||||
cases making it a good default.
|
||||
|
||||
Additional reading:
|
||||
|
||||
* [functions, categories and policies](../config/functions_categories_policies.md)
|
||||
|
||||
|
||||
## How can I ensure files are collocated on the same branch?
|
||||
|
||||
Many people like the idea of ensuring related files, such as all the
|
||||
files to a TV show season or songs in an album, are stored on the same
|
||||
storage device. However, most people have no actual need for this
|
||||
storage device. However, most people have no technical need for this
|
||||
behavior.
|
||||
|
||||
1. If you backup your data it is extremely likely your backup solution
|
||||
can restore only those files you are missing.
|
||||
2. Software such as **Sonarr** can manage the downloading and post
|
||||
processing of bespoke episodes which may be missing in a
|
||||
season. Either by downloading the episode individually if available
|
||||
or by downloading a full season.
|
||||
can restore only those files you are missing. There is no need to
|
||||
store data in related units to ensure restoring data is easy.
|
||||
2. Software such as [Sonarr](https://sonarr.tv) can manage the
|
||||
downloading and post processing of bespoke files / episodes which
|
||||
may be missing. Either by downloading the episode individually if
|
||||
available or by downloading a full season.
|
||||
3. There is no benefit to keeping files collocated with regard to
|
||||
drive spinup, caching, or other secondary concern.
|
||||
drive spinup, caching, or other secondary concern. In fact
|
||||
depending on usage patterns placing the data on the same underlying
|
||||
device could harm performance.
|
||||
|
||||
The main use case for wanting collocation is where the branch is going
|
||||
to be removed from the pool and you wish to have all data from some
|
||||
logical set on that device. Such as you intend to take a drive out of
|
||||
the pool to take on a trip and want a whole show on the
|
||||
drive. However, even in these situations you typically end up needing
|
||||
to curate the files anyway because it has show A but not show B.
|
||||
to be removed from the pool and used in isolation temporarily and you
|
||||
wish to have all data from some logical set on that device. Such as
|
||||
you intend to take a drive out of the pool to take on a trip and want
|
||||
a whole show or artist's works on the drive. However, even in these
|
||||
situations you typically end up needing to curate the files anyway
|
||||
because it has show A but not show B. In such cases it is more
|
||||
convient to use a separate devices and sync what you wish to it.
|
||||
|
||||
All that said you can accomplish collocation to varying degrees using
|
||||
the following methods:
|
||||
|
|
@ -56,33 +90,37 @@ the following methods:
|
|||
1. Use
|
||||
[mergerfs.consolidate](https://github.com/trapexit/mergerfs-tools/blob/master/src/mergerfs.consolidate)
|
||||
when consolidation is needed.
|
||||
2. Use a `msp` create policy.
|
||||
2. Use a `msp` create policy to do a best effort collocation without
|
||||
needing explicit directory curation.
|
||||
3. Use `epmfs` or other `ep` create policy and manually create paths
|
||||
on the branches directly.
|
||||
4. Use a `ep` `create` policy and `rand` for `mkdir`.
|
||||
on the branches directly as you need/desire.
|
||||
4. Use a `ep` `create` policy and `pfrd` or `rand` for `mkdir`.
|
||||
|
||||
|
||||
## How can I balance files across the pool?
|
||||
|
||||
Similar to collocation there is generally little reason to balance
|
||||
files.
|
||||
files across the pool branches.
|
||||
|
||||
1. Since prediction of a filesystem's death or loss of data is near
|
||||
impossible there is little reason to balance in hopes of limiting
|
||||
data loss.
|
||||
1. Since prediction of a device's/filesystem's death or loss of data
|
||||
is near impossible there is little reason to balance in hopes of
|
||||
limiting data loss.
|
||||
2. While performance could be impacted by having too much reading or
|
||||
writing happen to singular underlying filesystems balancing won't
|
||||
help unless you have the ability to manage the access patterns to
|
||||
the pool.
|
||||
3. Over time most configurations will lead to a random distribution of
|
||||
files across the branches which is effectively "balancing."
|
||||
3. Over time the churn of adding and removing files will lead to a
|
||||
random distribution of files across the branches which is
|
||||
effectively "balancing" if using create policies which do not favor
|
||||
any particular branch.
|
||||
|
||||
If you wish to move files around or balance the pool you can:
|
||||
All that said if you wish to move files around or balance the pool you
|
||||
can:
|
||||
|
||||
1. Use `rand` or `pfrd` create policies and just use your system as
|
||||
normal.
|
||||
2. Write simple scripts using rsync or similar to move files around as
|
||||
you wish.
|
||||
2. Write scripts using rsync or similar to move files around as you
|
||||
wish.
|
||||
3. Use
|
||||
[mergerfs.balance](https://github.com/trapexit/mergerfs-tools/blob/master/src/mergerfs.balance). Keep
|
||||
in mind that this tool is really just an example of how to
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
## What should mergerfs NOT be used for?
|
||||
|
||||
* Situations where you need large amounts of contiguous space beyond
|
||||
that available on any singular device. Such as putting 10GiB file on
|
||||
2 6GiB filesystems.
|
||||
* databases: Even if the database stored data in separate files
|
||||
(mergerfs wouldn't offer much otherwise) the higher latency of the
|
||||
indirection will really harm performance. If it is a lightly used
|
||||
|
|
@ -13,9 +16,9 @@
|
|||
should stick with RAID. However, it is fine to put a filesystem
|
||||
which is on a RAID setup in mergerfs.
|
||||
|
||||
**However, if using [passthrough](../config/passthrough.md) the above
|
||||
situations are less likely to be a concern. Best to do testing for
|
||||
your specific use case.**
|
||||
**However, if using [passthrough](../config/passthrough.md) the
|
||||
performance related issues above are less likely to be a concern. Best
|
||||
to do testing for your specific use case.**
|
||||
|
||||
|
||||
## It's mentioned that there are some security issues with mhddfs. What are they? How does mergerfs address them?
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
block to change credentials as required by numerous filesystem
|
||||
functions. This impacts performance.
|
||||
* FreeBSD's FUSE implementation is lacking many features of Linux.
|
||||
* passthrough
|
||||
* IO passthrough
|
||||
* statx
|
||||
* lazy umount
|
||||
* oom_score_adj
|
||||
|
|
|
|||
|
|
@ -64,6 +64,15 @@ branches](config/branches.md#branch-setup).
|
|||
|
||||
### Command Line
|
||||
|
||||
```
|
||||
# Two positional options: `:` colon separated branches and mountpoint
|
||||
mergerfs -o opt[,opt...] /branch0:/branch1 /mountpoint
|
||||
# or list of positional options where the last is the mountpoint
|
||||
mergerfs -o opt[,opt...] /branch0 /branch1 /mountpoint
|
||||
# or set mountpoint via -o mountpoint
|
||||
mergerfs -o mountpoint=/mountpoint /branch0 /branch1
|
||||
```
|
||||
|
||||
```
|
||||
mergerfs -o cache.files=off,category.create=pfrd,func.getattr=newest,dropcacheonclose=false /mnt/hdd0:/mnt/hdd1 /media
|
||||
```
|
||||
|
|
@ -129,7 +138,8 @@ ExecStart=/usr/bin/mergerfs \
|
|||
-o category.create=pfrd \
|
||||
-o func.getattr=newest \
|
||||
-o dropcacheonclose=false \
|
||||
/mnt/hdd0:/mnt/hdd1 \
|
||||
/mnt/hdd0 \
|
||||
/mnt/hdd1 \
|
||||
/media
|
||||
ExecStop=/usr/bin/umount /media
|
||||
# Or if you need fusermount
|
||||
|
|
@ -195,7 +205,8 @@ ExecStart=/usr/bin/mergerfs \
|
|||
-o category.create=pfrd \
|
||||
-o func.getattr=newest \
|
||||
-o dropcacheonclose=false \
|
||||
/mnt/hdd0:/mnt/hdd1 \
|
||||
/mnt/hdd0 \
|
||||
/mnt/hdd1 \
|
||||
/media
|
||||
ExecStop=/usr/bin/umount /media
|
||||
# Or if you need fusermount
|
||||
|
|
|
|||
|
|
@ -76,19 +76,21 @@ but if you run into problems it may be worth trying Samba/SMB.
|
|||
|
||||
**mergerfs settings:**
|
||||
|
||||
* `noforget`
|
||||
* `noforget` or `remember-nodes=SECONDS`
|
||||
* `inodecalc=path-hash`
|
||||
* `lazy-umount-mountpoint=false`
|
||||
|
||||
`noforget` is needed because NFS uses the `name_to_handle_at` and
|
||||
`noforget` (or `remember-nodes` with a large value such as `86400` for
|
||||
a full day) is needed because NFS uses the `name_to_handle_at` and
|
||||
`open_by_handle_at` functions which allow a program to keep a
|
||||
reference to a file without technically having it open in the typical
|
||||
sense. The problem is that FUSE has no way to know that NFS has a
|
||||
handle that it will later use to open the file again. As a result, it
|
||||
is possible for the kernel to tell mergerfs to forget about the file
|
||||
node and should NFS ever ask for that node's details in the future it
|
||||
would have nothing to respond with. Keeping nodes around forever is
|
||||
not ideal but at the moment the only way to manage the situation.
|
||||
would have nothing to respond with. Keeping nodes around forever (or
|
||||
however long is set with `remember-nodes`) is not ideal but at the
|
||||
moment the only way to manage the situation.
|
||||
|
||||
`inodecalc=path-hash` is needed because NFS is sensitive to
|
||||
out-of-band changes. FUSE doesn't care if a file's inode value changes
|
||||
|
|
|
|||
|
|
@ -50,8 +50,8 @@ OPTIONS:
|
|||
|
||||
EXPERIMENTAL
|
||||
|
||||
For some time there has been work to enable
|
||||
[passthrough](config/passthrough.md) IO in FUSE. Passthrough IO would
|
||||
For some time there has been work to enable IO
|
||||
[passthrough](config/passthrough.md) in FUSE. IO Passthrough would
|
||||
allow for near native performance with regards to reads and writes (at
|
||||
the expense of certain mergerfs features.) In Linux v6.9 that feature
|
||||
made its way into the kernel however in a somewhat limited form which
|
||||
|
|
|
|||
|
|
@ -108,9 +108,9 @@ nav:
|
|||
- extended_usage_patterns.md
|
||||
- FAQ:
|
||||
- faq/why_isnt_it_working.md
|
||||
- faq/reliability_and_scalability.md
|
||||
- faq/usage_and_functionality.md
|
||||
- faq/configuration_and_policies.md
|
||||
- faq/usage_and_functionality.md
|
||||
- faq/reliability_and_scalability.md
|
||||
- faq/compatibility_and_integration.md
|
||||
- faq/recommendations_and_warnings.md
|
||||
- faq/technical_behavior_and_limitations.md
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ Branch::to_string(void) const
|
|||
break;
|
||||
}
|
||||
|
||||
|
||||
if(std::holds_alternative<u64>(_minfreespace))
|
||||
{
|
||||
rv += ',';
|
||||
|
|
|
|||
564
src/config.cpp
564
src/config.cpp
|
|
@ -39,213 +39,263 @@
|
|||
|
||||
#define MINFREESPACE_DEFAULT (4294967295ULL)
|
||||
|
||||
#define IFERT(S) if(S == s_) return true
|
||||
|
||||
constexpr static const char CACHE_FILES_PROCESS_NAMES_DEFAULT[] =
|
||||
"rtorrent|"
|
||||
"qbittorrent-nox";
|
||||
|
||||
Config cfg;
|
||||
|
||||
namespace l
|
||||
Config::CfgConfigFile::CfgConfigFile()
|
||||
{
|
||||
static
|
||||
bool
|
||||
readonly(const std::string &s_)
|
||||
{
|
||||
IFERT("allow-idmap");
|
||||
IFERT("async_read");
|
||||
IFERT("branches-mount-timeout");
|
||||
IFERT("branches-mount-timeout-fail");
|
||||
IFERT("cache.symlinks");
|
||||
IFERT("cache.writeback");
|
||||
IFERT("direct-io-allow-mmap");
|
||||
IFERT("export-support");
|
||||
IFERT("fsname");
|
||||
IFERT("fuse_msg_size");
|
||||
IFERT("kernel-permissions-check");
|
||||
IFERT("mount");
|
||||
IFERT("nullrw");
|
||||
IFERT("pid");
|
||||
IFERT("pin-threads");
|
||||
IFERT("process-thread-count");
|
||||
IFERT("process-thread-queue-depth");
|
||||
IFERT("read-thread-count");
|
||||
IFERT("readdirplus");
|
||||
IFERT("scheduling-priority");
|
||||
IFERT("srcmounts");
|
||||
IFERT("threads");
|
||||
IFERT("version");
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Config::Config()
|
||||
: allow_idmap(false),
|
||||
async_read(true),
|
||||
auto_cache(false),
|
||||
minfreespace(MINFREESPACE_DEFAULT),
|
||||
branches(minfreespace),
|
||||
branches_mount_timeout(0),
|
||||
branches_mount_timeout_fail(false),
|
||||
cache_attr(1),
|
||||
cache_entry(1),
|
||||
cache_files(CacheFiles::ENUM::OFF),
|
||||
cache_files_process_names(CACHE_FILES_PROCESS_NAMES_DEFAULT),
|
||||
cache_negative_entry(0),
|
||||
cache_readdir(false),
|
||||
cache_statfs(0),
|
||||
cache_symlinks(false),
|
||||
category(func),
|
||||
direct_io(false),
|
||||
direct_io_allow_mmap(true),
|
||||
dropcacheonclose(false),
|
||||
export_support(true),
|
||||
flushonclose(FlushOnClose::ENUM::OPENED_FOR_WRITE),
|
||||
follow_symlinks(FollowSymlinks::ENUM::NEVER),
|
||||
fsname(),
|
||||
func(),
|
||||
fuse_msg_size("1M"),
|
||||
gid_cache_expire_timeout(60 * 60),
|
||||
gid_cache_remove_timeout(60 * 60 * 12),
|
||||
ignorepponrename(false),
|
||||
inodecalc("hybrid-hash"),
|
||||
lazy_umount_mountpoint(false),
|
||||
link_cow(false),
|
||||
link_exdev(LinkEXDEV::ENUM::PASSTHROUGH),
|
||||
log_metrics(false),
|
||||
mountpoint(),
|
||||
moveonenospc(true),
|
||||
nfsopenhack(NFSOpenHack::ENUM::OFF),
|
||||
nullrw(false),
|
||||
parallel_direct_writes(true),
|
||||
posix_acl(false),
|
||||
proxy_ioprio(false),
|
||||
readahead(0),
|
||||
readdir("seq"),
|
||||
readdirplus(false),
|
||||
rename_exdev(RenameEXDEV::ENUM::PASSTHROUGH),
|
||||
scheduling_priority(-10),
|
||||
security_capability(true),
|
||||
srcmounts(branches),
|
||||
statfs(StatFS::ENUM::BASE),
|
||||
statfs_ignore(StatFSIgnore::ENUM::NONE),
|
||||
symlinkify(false),
|
||||
symlinkify_timeout(3600),
|
||||
fuse_read_thread_count(0),
|
||||
fuse_process_thread_count(-1),
|
||||
fuse_process_thread_queue_depth(2),
|
||||
fuse_pin_threads("false"),
|
||||
version(MERGERFS_VERSION),
|
||||
writeback_cache(false),
|
||||
xattr(XAttr::ENUM::PASSTHROUGH),
|
||||
_initialized(false)
|
||||
{
|
||||
_map["allow-idmap"] = &allow_idmap;
|
||||
_map["async_read"] = &async_read;
|
||||
_map["auto_cache"] = &auto_cache;
|
||||
_map["branches"] = &branches;
|
||||
_map["branches-mount-timeout"] = &branches_mount_timeout;
|
||||
_map["branches-mount-timeout-fail"] = &branches_mount_timeout_fail;
|
||||
_map["cache.attr"] = &cache_attr;
|
||||
_map["cache.entry"] = &cache_entry;
|
||||
_map["cache.files"] = &cache_files;
|
||||
_map["cache.files.process-names"] = &cache_files_process_names;
|
||||
_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["direct-io-allow-mmap"] = &direct_io_allow_mmap;
|
||||
_map["dropcacheonclose"] = &dropcacheonclose;
|
||||
_map["export-support"] = &export_support;
|
||||
_map["flush-on-close"] = &flushonclose;
|
||||
_map["follow-symlinks"] = &follow_symlinks;
|
||||
_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.readdir"] = &readdir;
|
||||
_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["gid-cache-expire-timeout"] = &gid_cache_expire_timeout;
|
||||
_map["gid-cache-remove-timeout"] = &gid_cache_remove_timeout;
|
||||
_map["handle-killpriv"] = &handle_killpriv;
|
||||
_map["handle-killpriv-v2"] = &handle_killpriv_v2;
|
||||
_map["ignorepponrename"] = &ignorepponrename;
|
||||
_map["inodecalc"] = &inodecalc;
|
||||
_map["kernel_cache"] = &kernel_cache;
|
||||
_map["kernel-permissions-check"] = &kernel_permissions_check;
|
||||
_map["lazy-umount-mountpoint"] = &lazy_umount_mountpoint;
|
||||
_map["link_cow"] = &link_cow;
|
||||
_map["link-exdev"] = &link_exdev;
|
||||
_map["log.metrics"] = &log_metrics;
|
||||
_map["minfreespace"] = &minfreespace;
|
||||
_map["mount"] = &mountpoint;
|
||||
_map["moveonenospc"] = &moveonenospc;
|
||||
_map["nfsopenhack"] = &nfsopenhack;
|
||||
_map["nullrw"] = &nullrw;
|
||||
_map["passthrough"] = &passthrough;
|
||||
_map["pid"] = &pid;
|
||||
_map["parallel-direct-writes"] = ¶llel_direct_writes;
|
||||
_map["pin-threads"] = &fuse_pin_threads;
|
||||
_map["posix_acl"] = &posix_acl;
|
||||
_map["proxy-ioprio"] = &proxy_ioprio;
|
||||
_map["readahead"] = &readahead;
|
||||
_map["readdirplus"] = &readdirplus;
|
||||
_map["rename-exdev"] = &rename_exdev;
|
||||
_map["scheduling-priority"] = &scheduling_priority;
|
||||
_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"] = &fuse_read_thread_count;
|
||||
_map["read-thread-count"] = &fuse_read_thread_count;
|
||||
_map["process-thread-count"] = &fuse_process_thread_count;
|
||||
_map["process-thread-queue-depth"] = &fuse_process_thread_queue_depth;
|
||||
_map["version"] = &version;
|
||||
_map["xattr"] = &xattr;
|
||||
}
|
||||
|
||||
Config&
|
||||
Config::operator=(const Config &cfg_)
|
||||
int
|
||||
Config::CfgConfigFile::from_string(const std::string_view s_)
|
||||
{
|
||||
int rv;
|
||||
std::string val;
|
||||
fs::path cfg_file;
|
||||
|
||||
for(auto &kv : _map)
|
||||
{
|
||||
rv = cfg_.get(kv.first,&val);
|
||||
if(rv)
|
||||
continue;
|
||||
if(_depth > 5)
|
||||
return -ELOOP;
|
||||
_depth++;
|
||||
|
||||
kv.second->from_string(val);
|
||||
}
|
||||
cfg_file = (s_.empty() ? _cfg_file : s_);
|
||||
|
||||
return *this;
|
||||
rv = cfg.from_file(cfg_file);
|
||||
if(rv == 0)
|
||||
_cfg_file = cfg_file;
|
||||
|
||||
_depth--;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
std::string
|
||||
Config::CfgConfigFile::to_string() const
|
||||
{
|
||||
return _cfg_file;
|
||||
}
|
||||
|
||||
|
||||
Config::Config()
|
||||
:
|
||||
allow_idmap(false),
|
||||
async_read(true),
|
||||
branches(minfreespace),
|
||||
branches_mount_timeout(0),
|
||||
branches_mount_timeout_fail(false),
|
||||
cache_attr(1),
|
||||
cache_entry(1),
|
||||
cache_files(CacheFiles::ENUM::OFF),
|
||||
cache_files_process_names(CACHE_FILES_PROCESS_NAMES_DEFAULT),
|
||||
cache_negative_entry(0),
|
||||
cache_readdir(false),
|
||||
cache_statfs(0),
|
||||
cache_symlinks(false),
|
||||
cache_writeback(false),
|
||||
category(func),
|
||||
config_file(),
|
||||
congestion_threshold(fuse_cfg.congestion_threshold,0),
|
||||
debug(fuse_cfg.debug,false),
|
||||
direct_io_allow_mmap(true),
|
||||
dropcacheonclose(false),
|
||||
export_support(true),
|
||||
flushonclose(FlushOnClose::ENUM::OPENED_FOR_WRITE),
|
||||
follow_symlinks(FollowSymlinks::ENUM::NEVER),
|
||||
fsname(),
|
||||
func(),
|
||||
fuse_msg_size("1M"),
|
||||
gid(fuse_cfg.gid,FUSE_CFG_INVALID_ID),
|
||||
gid_cache_expire_timeout(60 * 60),
|
||||
gid_cache_remove_timeout(60 * 60 * 12),
|
||||
handle_killpriv(true),
|
||||
handle_killpriv_v2(true),
|
||||
ignorepponrename(false),
|
||||
inodecalc("hybrid-hash"),
|
||||
kernel_permissions_check(true),
|
||||
lazy_umount_mountpoint(false),
|
||||
link_cow(false),
|
||||
link_exdev(LinkEXDEV::ENUM::PASSTHROUGH),
|
||||
log_metrics(false),
|
||||
max_background(fuse_cfg.max_background,0),
|
||||
minfreespace(MINFREESPACE_DEFAULT),
|
||||
mountpoint(),
|
||||
_mount(mountpoint),
|
||||
_mountpoint(mountpoint),
|
||||
moveonenospc(true),
|
||||
nfsopenhack(NFSOpenHack::ENUM::OFF),
|
||||
noforget(),
|
||||
nullrw(false),
|
||||
parallel_direct_writes(true),
|
||||
passthrough_io(PassthroughIO::ENUM::OFF),
|
||||
passthrough_max_stack_depth(fuse_cfg.passthrough_max_stack_depth,1),
|
||||
pin_threads(fuse_cfg.pin_threads,"false"),
|
||||
posix_acl(false),
|
||||
process_thread_count(fuse_cfg.process_thread_count,-1),
|
||||
process_thread_queue_depth(fuse_cfg.process_thread_queue_depth,2),
|
||||
proxy_ioprio(false),
|
||||
read_thread_count(fuse_cfg.read_thread_count,0),
|
||||
readahead(0),
|
||||
readdir("seq"),
|
||||
remember_nodes(fuse_cfg.remember_nodes,0),
|
||||
rename_exdev(RenameEXDEV::ENUM::PASSTHROUGH),
|
||||
scheduling_priority(-10),
|
||||
security_capability(true),
|
||||
srcmounts(branches),
|
||||
statfs(StatFS::ENUM::BASE),
|
||||
statfs_ignore(StatFSIgnore::ENUM::NONE),
|
||||
symlinkify(false),
|
||||
symlinkify_timeout(3600),
|
||||
threads(fuse_cfg.read_thread_count),
|
||||
uid(fuse_cfg.uid,FUSE_CFG_INVALID_ID),
|
||||
umask(fuse_cfg.umask,FUSE_CFG_INVALID_UMASK),
|
||||
version(MERGERFS_VERSION),
|
||||
xattr(XAttr::ENUM::PASSTHROUGH),
|
||||
_initialized(false)
|
||||
{
|
||||
allow_idmap.ro =
|
||||
async_read.ro =
|
||||
branches_mount_timeout.ro =
|
||||
branches_mount_timeout_fail.ro =
|
||||
cache_symlinks.ro =
|
||||
cache_writeback.ro =
|
||||
congestion_threshold.ro =
|
||||
direct_io_allow_mmap.ro =
|
||||
export_support.ro =
|
||||
fsname.ro =
|
||||
fuse_msg_size.ro =
|
||||
handle_killpriv.ro =
|
||||
handle_killpriv_v2.ro =
|
||||
kernel_permissions_check.ro =
|
||||
max_background.ro =
|
||||
_mount.ro =
|
||||
_mountpoint.ro =
|
||||
nullrw.ro =
|
||||
pid.ro =
|
||||
pin_threads.ro =
|
||||
process_thread_count.ro =
|
||||
process_thread_queue_depth.ro =
|
||||
read_thread_count.ro =
|
||||
scheduling_priority.ro =
|
||||
srcmounts.ro =
|
||||
version.ro =
|
||||
true;
|
||||
congestion_threshold.ro =
|
||||
gid.display =
|
||||
max_background.ro =
|
||||
threads.display =
|
||||
_mount.display =
|
||||
uid.display =
|
||||
umask.display =
|
||||
false;
|
||||
|
||||
_map["allow-idmap"] = &allow_idmap;
|
||||
_map["async-read"] = &async_read;
|
||||
_map["atomic-o-trunc"] = &_dummy;
|
||||
_map["auto-cache"] = &_dummy;
|
||||
_map["big-writes"] = &_dummy;
|
||||
_map["branches"] = &branches;
|
||||
_map["branches-mount-timeout"] = &branches_mount_timeout;
|
||||
_map["branches-mount-timeout-fail"] = &branches_mount_timeout_fail;
|
||||
_map["cache.attr"] = &cache_attr;
|
||||
_map["cache.entry"] = &cache_entry;
|
||||
_map["cache.files"] = &cache_files;
|
||||
_map["cache.files.process-names"] = &cache_files_process_names;
|
||||
_map["cache.negative-entry"] = &cache_negative_entry;
|
||||
_map["cache.open"] = &_dummy;
|
||||
_map["cache.readdir"] = &cache_readdir;
|
||||
_map["cache.statfs"] = &cache_statfs;
|
||||
_map["cache.symlinks"] = &cache_symlinks;
|
||||
_map["cache.writeback"] = &cache_writeback;
|
||||
_map["category.action"] = &category.action;
|
||||
_map["category.create"] = &category.create;
|
||||
_map["category.search"] = &category.search;
|
||||
_map["config"] = &config_file;
|
||||
_map["debug"] = &debug;
|
||||
_map["direct-io-allow-mmap"] = &direct_io_allow_mmap;
|
||||
_map["direct-io"] = &_dummy;
|
||||
_map["dropcacheonclose"] = &dropcacheonclose;
|
||||
_map["export-support"] = &export_support;
|
||||
_map["flush-on-close"] = &flushonclose;
|
||||
_map["follow-symlinks"] = &follow_symlinks;
|
||||
_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.readdir"] = &readdir;
|
||||
_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["gid"] = &gid;
|
||||
_map["gid-cache.expire-timeout"] = &gid_cache_expire_timeout;
|
||||
_map["gid-cache.remove-timeout"] = &gid_cache_remove_timeout;
|
||||
_map["handle-killpriv"] = &handle_killpriv;
|
||||
_map["handle-killpriv-v2"] = &handle_killpriv_v2;
|
||||
_map["hard-remove"] = &_dummy;
|
||||
_map["ignorepponrename"] = &ignorepponrename;
|
||||
_map["inodecalc"] = &inodecalc;
|
||||
_map["kernel-permissions-check"] = &kernel_permissions_check;
|
||||
_map["kernel-cache"] = &_dummy;
|
||||
_map["lazy-umount-mountpoint"] = &lazy_umount_mountpoint;
|
||||
_map["link-exdev"] = &link_exdev;
|
||||
_map["link-cow"] = &link_cow;
|
||||
_map["log.metrics"] = &log_metrics;
|
||||
_map["minfreespace"] = &minfreespace;
|
||||
_map["mount"] = &_mount;
|
||||
_map["mountpoint"] = &_mountpoint;
|
||||
_map["moveonenospc"] = &moveonenospc;
|
||||
_map["negative-entry"] = &_dummy;
|
||||
_map["nfsopenhack"] = &nfsopenhack;
|
||||
_map["no-splice-move"] = &_dummy;
|
||||
_map["no-splice-read"] = &_dummy;
|
||||
_map["no-splice-write"] = &_dummy;
|
||||
_map["noforget"] = &noforget;
|
||||
_map["nonempty"] = &_dummy;
|
||||
_map["nullrw"] = &nullrw;
|
||||
_map["parallel-direct-writes"] = ¶llel_direct_writes;
|
||||
_map["passthrough.io"] = &passthrough_io;
|
||||
_map["passthrough.max-stack-depth"] = &passthrough_max_stack_depth;
|
||||
_map["pid"] = &pid;
|
||||
_map["pin-threads"] = &pin_threads;
|
||||
_map["posix_acl"] = &posix_acl;
|
||||
_map["process-thread-count"] = &process_thread_count;
|
||||
_map["process-thread-queue-depth"] = &process_thread_queue_depth;
|
||||
_map["proxy-ioprio"] = &proxy_ioprio;
|
||||
_map["read-thread-count"] = &read_thread_count;
|
||||
_map["readahead"] = &readahead;
|
||||
_map["remember-nodes"] = &remember_nodes;
|
||||
_map["rename-exdev"] = &rename_exdev;
|
||||
_map["scheduling-priority"] = &scheduling_priority;
|
||||
_map["security-capability"] = &security_capability;
|
||||
_map["splice-move"] = &_dummy;
|
||||
_map["splice-read"] = &_dummy;
|
||||
_map["splice-write"] = &_dummy;
|
||||
_map["srcmounts"] = &srcmounts;
|
||||
_map["statfs"] = &statfs;
|
||||
_map["statfs-ignore"] = &statfs_ignore;
|
||||
_map["symlinkify"] = &symlinkify;
|
||||
_map["symlinkify-timeout"] = &symlinkify_timeout;
|
||||
_map["threads"] = &threads;
|
||||
_map["uid"] = &uid;
|
||||
_map["umask"] = &umask;
|
||||
_map["use-ino"] = &_dummy;
|
||||
_map["version"] = &version;
|
||||
_map["xattr"] = &xattr;
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
@ -275,6 +325,9 @@ Config::keys_xattr(std::string &s_) const
|
|||
s_.reserve(1024);
|
||||
for(const auto &[key,val] : _map)
|
||||
{
|
||||
if(val->display == false)
|
||||
continue;
|
||||
|
||||
s_ += "user.mergerfs.";
|
||||
s_ += key;
|
||||
s_ += '\0';
|
||||
|
|
@ -289,6 +342,9 @@ Config::keys_listxattr_size() const
|
|||
rv = 0;
|
||||
for(const auto &[key,val] : _map)
|
||||
{
|
||||
if(val->display == false)
|
||||
continue;
|
||||
|
||||
rv += sizeof("user.mergerfs.");
|
||||
rv += key.size();
|
||||
}
|
||||
|
|
@ -308,6 +364,9 @@ Config::keys_listxattr(char *list_,
|
|||
|
||||
for(const auto &[key,val] : _map)
|
||||
{
|
||||
if(val->display == false)
|
||||
continue;
|
||||
|
||||
auto rv = fmt::format_to_n(list,size,
|
||||
"user.mergerfs.{}\0",
|
||||
key);
|
||||
|
|
@ -324,9 +383,12 @@ int
|
|||
Config::get(const std::string &key_,
|
||||
std::string *val_) const
|
||||
{
|
||||
std::string key;
|
||||
Str2TFStrMap::const_iterator i;
|
||||
|
||||
i = _map.find(key_);
|
||||
key = str::replace(key_,'_','-');
|
||||
|
||||
i = _map.find(key);
|
||||
if(i == _map.end())
|
||||
return -ENOATTR;
|
||||
|
||||
|
|
@ -335,27 +397,22 @@ Config::get(const std::string &key_,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
Config::set_raw(const std::string &key_,
|
||||
const std::string &value_)
|
||||
{
|
||||
Str2TFStrMap::iterator i;
|
||||
|
||||
i = _map.find(key_);
|
||||
if(i == _map.end())
|
||||
return -ENOATTR;
|
||||
|
||||
return i->second->from_string(value_);
|
||||
}
|
||||
|
||||
int
|
||||
Config::set(const std::string &key_,
|
||||
const std::string &value_)
|
||||
{
|
||||
if(_initialized && l::readonly(key_))
|
||||
std::string key;
|
||||
Str2TFStrMap::iterator i;
|
||||
|
||||
key = str::replace(key_,'_','-');
|
||||
|
||||
i = _map.find(key);
|
||||
if(i == _map.end())
|
||||
return -ENOATTR;
|
||||
if(_initialized && i->second->ro)
|
||||
return -EROFS;
|
||||
|
||||
return set_raw(key_,value_);
|
||||
return i->second->from_string(value_);
|
||||
}
|
||||
|
||||
int
|
||||
|
|
@ -372,16 +429,13 @@ Config::set(const std::string &kv_)
|
|||
}
|
||||
|
||||
int
|
||||
Config::from_stream(std::istream &istrm_,
|
||||
ErrVec *errs_)
|
||||
Config::from_stream(std::istream &istrm_)
|
||||
{
|
||||
int rv;
|
||||
std::string line;
|
||||
std::string key;
|
||||
std::string val;
|
||||
Config newcfg;
|
||||
|
||||
newcfg = *this;
|
||||
Config::ErrVec new_errs;
|
||||
|
||||
while(std::getline(istrm_,line,'\n'))
|
||||
{
|
||||
|
|
@ -393,22 +447,21 @@ Config::from_stream(std::istream &istrm_,
|
|||
key = str::trim(key);
|
||||
val = str::trim(val);
|
||||
|
||||
rv = newcfg.set(key,val);
|
||||
rv = set(key,val);
|
||||
if(rv < 0)
|
||||
errs_->push_back({rv,key});
|
||||
new_errs.push_back({-rv,key+'='+val});
|
||||
}
|
||||
|
||||
if(!errs_->empty())
|
||||
return -EINVAL;
|
||||
rv = (new_errs.empty() ? 0 : -EINVAL);
|
||||
errs.insert(errs.end(),
|
||||
new_errs.begin(),
|
||||
new_errs.end());
|
||||
|
||||
*this = newcfg;
|
||||
|
||||
return 0;
|
||||
return rv;
|
||||
}
|
||||
|
||||
int
|
||||
Config::from_file(const std::string &filepath_,
|
||||
ErrVec *errs_)
|
||||
Config::from_file(const std::string &filepath_)
|
||||
{
|
||||
int rv;
|
||||
std::ifstream ifstrm;
|
||||
|
|
@ -416,11 +469,11 @@ Config::from_file(const std::string &filepath_,
|
|||
ifstrm.open(filepath_);
|
||||
if(!ifstrm.good())
|
||||
{
|
||||
errs_->push_back({-errno,filepath_});
|
||||
errs.push_back({errno,filepath_});
|
||||
return -errno;
|
||||
}
|
||||
|
||||
rv = from_stream(ifstrm,errs_);
|
||||
rv = from_stream(ifstrm);
|
||||
|
||||
ifstrm.close();
|
||||
|
||||
|
|
@ -477,52 +530,31 @@ Config::prune_cmd_xattr(const std::string_view &s_)
|
|||
return {};
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream &os_,
|
||||
const Config &c_)
|
||||
{
|
||||
for(const auto &[key,val] : c_._map)
|
||||
os_ << key << '=' << val->to_string() << std::endl;
|
||||
|
||||
return os_;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
std::string
|
||||
err2str(const int err_)
|
||||
Config::Err::to_string() const
|
||||
{
|
||||
switch(err_)
|
||||
std::string s;
|
||||
|
||||
switch(err)
|
||||
{
|
||||
case 0:
|
||||
return std::string();
|
||||
case -EINVAL:
|
||||
return "invalid value";
|
||||
case -ENOATTR:
|
||||
return "unknown option";
|
||||
case -EROFS:
|
||||
return "read-only option";
|
||||
break;
|
||||
case EINVAL:
|
||||
s = "invalid value";
|
||||
break;
|
||||
case ENOATTR:
|
||||
s = "unknown option";
|
||||
break;
|
||||
case EROFS:
|
||||
s = "read-only option";
|
||||
break;
|
||||
case ELOOP:
|
||||
s = "too many levels of config";
|
||||
break;
|
||||
default:
|
||||
return strerror(-err_);
|
||||
s = strerror(err);
|
||||
break;
|
||||
}
|
||||
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream &os_,
|
||||
const Config::ErrVec &ev_)
|
||||
{
|
||||
std::string errstr;
|
||||
|
||||
for(auto &err : ev_)
|
||||
{
|
||||
os_ << "* ERROR: ";
|
||||
errstr = err2str(err.err);
|
||||
if(!errstr.empty())
|
||||
os_ << errstr << " - ";
|
||||
os_ << err.str << std::endl;
|
||||
}
|
||||
|
||||
return os_;
|
||||
return fmt::format("{} - {}",s,str);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
#include "branches.hpp"
|
||||
#include "category.hpp"
|
||||
#include "config_cachefiles.hpp"
|
||||
#include "config_dummy.hpp"
|
||||
#include "config_flushonclose.hpp"
|
||||
#include "config_follow_symlinks.hpp"
|
||||
#include "config_gidcache.hpp"
|
||||
|
|
@ -27,8 +28,9 @@
|
|||
#include "config_log_metrics.hpp"
|
||||
#include "config_moveonenospc.hpp"
|
||||
#include "config_nfsopenhack.hpp"
|
||||
#include "config_noforget.hpp"
|
||||
#include "config_pagesize.hpp"
|
||||
#include "config_passthrough.hpp"
|
||||
#include "config_passthrough_io.hpp"
|
||||
#include "config_pid.hpp"
|
||||
#include "config_proxy_ioprio.hpp"
|
||||
#include "config_rename_exdev.hpp"
|
||||
|
|
@ -40,10 +42,13 @@
|
|||
#include "errno.hpp"
|
||||
#include "fs_path.hpp"
|
||||
#include "funcs.hpp"
|
||||
#include "fuse_cfg.hpp"
|
||||
#include "fuse_readdir.hpp"
|
||||
#include "policy.hpp"
|
||||
#include "rwlock.hpp"
|
||||
#include "tofrom_ref.hpp"
|
||||
#include "tofrom_wrapper.hpp"
|
||||
#include "syslog.hpp"
|
||||
|
||||
#include "fuse.h"
|
||||
|
||||
|
|
@ -55,13 +60,14 @@
|
|||
|
||||
#include <sys/stat.h>
|
||||
|
||||
typedef ToFromWrapper<bool> ConfigBOOL;
|
||||
typedef ToFromWrapper<uint64_t> ConfigUINT64;
|
||||
typedef ToFromWrapper<int64_t> ConfigS64;
|
||||
typedef ToFromWrapper<int> ConfigINT;
|
||||
typedef ToFromWrapper<std::string> ConfigSTR;
|
||||
typedef ToFromWrapper<fs::path> ConfigPath;
|
||||
typedef std::map<std::string,ToFromString*> Str2TFStrMap;
|
||||
typedef ToFromWrapper<bool> ConfigBOOL;
|
||||
typedef ToFromWrapper<uint64_t> ConfigUINT64;
|
||||
typedef ToFromWrapper<int64_t> ConfigS64;
|
||||
typedef ToFromWrapper<int> ConfigINT;
|
||||
typedef ToFromWrapper<std::string> ConfigSTR;
|
||||
typedef ToFromWrapper<fs::path> ConfigPath;
|
||||
typedef std::map<std::string,ToFromString*> Str2TFStrMap;
|
||||
typedef ROToFromWrapper<std::string> ConfigROSTR;
|
||||
|
||||
extern const std::string CONTROLFILE;
|
||||
|
||||
|
|
@ -72,10 +78,30 @@ public:
|
|||
{
|
||||
int err;
|
||||
std::string str;
|
||||
|
||||
std::string to_string() const;
|
||||
};
|
||||
|
||||
typedef std::vector<Err> ErrVec;
|
||||
|
||||
public:
|
||||
ErrVec errs;
|
||||
|
||||
public:
|
||||
class CfgConfigFile : public ToFromString
|
||||
{
|
||||
private:
|
||||
fs::path _cfg_file;
|
||||
int _depth = 0;
|
||||
|
||||
public:
|
||||
CfgConfigFile();
|
||||
|
||||
public:
|
||||
int from_string(const std::string_view s_) final;
|
||||
std::string to_string(void) const final;
|
||||
};
|
||||
|
||||
public:
|
||||
Config();
|
||||
|
||||
|
|
@ -85,8 +111,6 @@ public:
|
|||
public:
|
||||
ConfigBOOL allow_idmap;
|
||||
ConfigBOOL async_read;
|
||||
ConfigBOOL auto_cache;
|
||||
ConfigUINT64 minfreespace;
|
||||
Branches branches;
|
||||
ConfigUINT64 branches_mount_timeout;
|
||||
ConfigBOOL branches_mount_timeout_fail;
|
||||
|
|
@ -98,8 +122,11 @@ public:
|
|||
ConfigBOOL cache_readdir;
|
||||
ConfigUINT64 cache_statfs;
|
||||
ConfigBOOL cache_symlinks;
|
||||
ConfigBOOL cache_writeback;
|
||||
Categories category;
|
||||
ConfigBOOL direct_io;
|
||||
CfgConfigFile config_file;
|
||||
TFSRef<int> congestion_threshold;
|
||||
TFSRef<bool> debug;
|
||||
ConfigBOOL direct_io_allow_mmap;
|
||||
ConfigBOOL dropcacheonclose;
|
||||
ConfigBOOL export_support;
|
||||
|
|
@ -108,30 +135,40 @@ public:
|
|||
ConfigSTR fsname;
|
||||
Funcs func;
|
||||
ConfigPageSize fuse_msg_size;
|
||||
TFSRef<s64> gid;
|
||||
GIDCacheExpireTimeout gid_cache_expire_timeout;
|
||||
GIDCacheRemoveTimeout gid_cache_remove_timeout;
|
||||
ConfigBOOL handle_killpriv = true;
|
||||
ConfigBOOL handle_killpriv_v2 = true;
|
||||
ConfigBOOL handle_killpriv;
|
||||
ConfigBOOL handle_killpriv_v2;
|
||||
ConfigBOOL ignorepponrename;
|
||||
InodeCalc inodecalc;
|
||||
ConfigBOOL kernel_cache;
|
||||
ConfigBOOL kernel_permissions_check = true;
|
||||
ConfigBOOL kernel_permissions_check;
|
||||
ConfigBOOL lazy_umount_mountpoint;
|
||||
ConfigBOOL link_cow;
|
||||
LinkEXDEV link_exdev;
|
||||
LogMetrics log_metrics;
|
||||
ConfigPath mountpoint;
|
||||
TFSRef<int> max_background;
|
||||
ConfigUINT64 minfreespace;
|
||||
fs::path mountpoint;
|
||||
TFSRef<fs::path> _mount;
|
||||
TFSRef<fs::path> _mountpoint;
|
||||
MoveOnENOSPC moveonenospc;
|
||||
NFSOpenHack nfsopenhack;
|
||||
CfgNoforget noforget;
|
||||
ConfigBOOL nullrw;
|
||||
Passthrough passthrough = Passthrough::ENUM::OFF;
|
||||
ConfigBOOL parallel_direct_writes;
|
||||
PassthroughIO passthrough_io;
|
||||
TFSRef<int> passthrough_max_stack_depth;
|
||||
ConfigGetPid pid;
|
||||
TFSRef<std::string> pin_threads;
|
||||
ConfigBOOL posix_acl;
|
||||
TFSRef<int> process_thread_count;
|
||||
TFSRef<int> process_thread_queue_depth;
|
||||
ProxyIOPrio proxy_ioprio;
|
||||
TFSRef<int> read_thread_count;
|
||||
ConfigUINT64 readahead;
|
||||
FUSE::ReadDir readdir;
|
||||
ConfigBOOL readdirplus;
|
||||
TFSRef<s64> remember_nodes;
|
||||
RenameEXDEV rename_exdev;
|
||||
ConfigINT scheduling_priority;
|
||||
ConfigBOOL security_capability;
|
||||
|
|
@ -140,14 +177,15 @@ public:
|
|||
StatFSIgnore statfs_ignore;
|
||||
ConfigBOOL symlinkify;
|
||||
ConfigS64 symlinkify_timeout;
|
||||
ConfigINT fuse_read_thread_count;
|
||||
ConfigINT fuse_process_thread_count;
|
||||
ConfigINT fuse_process_thread_queue_depth;
|
||||
ConfigSTR fuse_pin_threads;
|
||||
ConfigSTR version;
|
||||
ConfigBOOL writeback_cache;
|
||||
TFSRef<int> threads;
|
||||
TFSRef<s64> uid;
|
||||
TFSRef<s64> umask;
|
||||
ConfigROSTR version;
|
||||
XAttr xattr;
|
||||
|
||||
private:
|
||||
CfgDummy _dummy;
|
||||
|
||||
private:
|
||||
bool _initialized;
|
||||
|
||||
|
|
@ -167,13 +205,12 @@ public:
|
|||
|
||||
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);
|
||||
int set(const std::string &kv);
|
||||
|
||||
public:
|
||||
int from_stream(std::istream &istrm, ErrVec *errs);
|
||||
int from_file(const std::string &filepath, ErrVec *errs);
|
||||
int from_stream(std::istream &istrm);
|
||||
int from_file(const std::string &filepath);
|
||||
|
||||
public:
|
||||
static bool is_rootdir(const fs::path &fusepath);
|
||||
|
|
@ -185,10 +222,6 @@ public:
|
|||
|
||||
private:
|
||||
Str2TFStrMap _map;
|
||||
|
||||
public:
|
||||
friend class Read;
|
||||
friend class Write;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream &s,const Config::ErrVec &ev);
|
||||
|
|
|
|||
|
|
@ -26,8 +26,6 @@ CacheFiles::to_string() const
|
|||
{
|
||||
switch(_data)
|
||||
{
|
||||
case CacheFiles::ENUM::LIBFUSE:
|
||||
return "libfuse";
|
||||
case CacheFiles::ENUM::OFF:
|
||||
return "off";
|
||||
case CacheFiles::ENUM::PARTIAL:
|
||||
|
|
@ -47,9 +45,7 @@ template<>
|
|||
int
|
||||
CacheFiles::from_string(const std::string_view s_)
|
||||
{
|
||||
if(s_ == "libfuse")
|
||||
_data = CacheFiles::ENUM::LIBFUSE;
|
||||
ef(s_ == "off")
|
||||
if(s_ == "off")
|
||||
_data = CacheFiles::ENUM::OFF;
|
||||
ef(s_ == "partial")
|
||||
_data = CacheFiles::ENUM::PARTIAL;
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@
|
|||
|
||||
enum class CacheFilesEnum
|
||||
{
|
||||
LIBFUSE,
|
||||
OFF,
|
||||
PARTIAL,
|
||||
FULL,
|
||||
|
|
|
|||
24
src/config_dummy.hpp
Normal file
24
src/config_dummy.hpp
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
#include "tofrom_string.hpp"
|
||||
|
||||
class CfgDummy : public ToFromString
|
||||
{
|
||||
public:
|
||||
CfgDummy()
|
||||
{
|
||||
display = false;
|
||||
}
|
||||
|
||||
std::string
|
||||
to_string() const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
int
|
||||
from_string(const std::string_view)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
40
src/config_noforget.hpp
Normal file
40
src/config_noforget.hpp
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
#pragma once
|
||||
|
||||
#include "tofrom_string.hpp"
|
||||
#include "from_string.hpp"
|
||||
|
||||
#include "fuse_cfg.hpp"
|
||||
|
||||
|
||||
class CfgNoforget : public ToFromString
|
||||
{
|
||||
public:
|
||||
int
|
||||
from_string(const std::string_view s_)
|
||||
{
|
||||
int rv;
|
||||
bool b = false;
|
||||
|
||||
rv = str::from(s_,&b);
|
||||
if((b == true) || s_.empty())
|
||||
{
|
||||
fuse_cfg.remember_nodes = -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(rv)
|
||||
return rv;
|
||||
|
||||
fuse_cfg.remember_nodes = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string
|
||||
to_string() const
|
||||
{
|
||||
if(fuse_cfg.remember_nodes == -1)
|
||||
return "true";
|
||||
return "false";
|
||||
}
|
||||
};
|
||||
|
|
@ -16,23 +16,23 @@
|
|||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config_passthrough.hpp"
|
||||
#include "config_passthrough_io.hpp"
|
||||
#include "ef.hpp"
|
||||
#include "errno.hpp"
|
||||
|
||||
template<>
|
||||
std::string
|
||||
Passthrough::to_string() const
|
||||
PassthroughIO::to_string() const
|
||||
{
|
||||
switch(_data)
|
||||
{
|
||||
case Passthrough::ENUM::OFF:
|
||||
case PassthroughIO::ENUM::OFF:
|
||||
return "off";
|
||||
case Passthrough::ENUM::RO:
|
||||
case PassthroughIO::ENUM::RO:
|
||||
return "ro";
|
||||
case Passthrough::ENUM::WO:
|
||||
case PassthroughIO::ENUM::WO:
|
||||
return "wo";
|
||||
case Passthrough::ENUM::RW:
|
||||
case PassthroughIO::ENUM::RW:
|
||||
return "rw";
|
||||
}
|
||||
|
||||
|
|
@ -41,16 +41,16 @@ Passthrough::to_string() const
|
|||
|
||||
template<>
|
||||
int
|
||||
Passthrough::from_string(const std::string_view s_)
|
||||
PassthroughIO::from_string(const std::string_view s_)
|
||||
{
|
||||
if(s_ == "off")
|
||||
_data = Passthrough::ENUM::OFF;
|
||||
_data = PassthroughIO::ENUM::OFF;
|
||||
ef(s_ == "ro")
|
||||
_data = Passthrough::ENUM::RO;
|
||||
_data = PassthroughIO::ENUM::RO;
|
||||
ef(s_ == "wo")
|
||||
_data = Passthrough::ENUM::WO;
|
||||
_data = PassthroughIO::ENUM::WO;
|
||||
ef(s_ == "rw")
|
||||
_data = Passthrough::ENUM::RW;
|
||||
_data = PassthroughIO::ENUM::RW;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
|
|
@ -21,7 +21,7 @@
|
|||
#include "enum.hpp"
|
||||
|
||||
|
||||
enum class PassthroughEnum
|
||||
enum class PassthroughIOEnum
|
||||
{
|
||||
OFF = 0,
|
||||
RO,
|
||||
|
|
@ -29,4 +29,4 @@ enum class PassthroughEnum
|
|||
RW
|
||||
};
|
||||
|
||||
typedef Enum<PassthroughEnum> Passthrough;
|
||||
typedef Enum<PassthroughIOEnum> PassthroughIO;
|
||||
|
|
@ -46,7 +46,7 @@
|
|||
*/
|
||||
static
|
||||
void
|
||||
_tweak_flags_writeback_cache(int *flags_)
|
||||
_tweak_flags_cache_writeback(int *flags_)
|
||||
{
|
||||
if((*flags_ & O_ACCMODE) == O_WRONLY)
|
||||
*flags_ = ((*flags_ & ~O_ACCMODE) | O_RDWR);
|
||||
|
|
@ -87,11 +87,6 @@ _config_to_ffi_flags(Config &cfg_,
|
|||
{
|
||||
switch(cfg_.cache_files)
|
||||
{
|
||||
case CacheFiles::ENUM::LIBFUSE:
|
||||
ffi_->direct_io = cfg_.direct_io;
|
||||
ffi_->keep_cache = cfg_.kernel_cache;
|
||||
ffi_->auto_cache = cfg_.auto_cache;
|
||||
break;
|
||||
case CacheFiles::ENUM::OFF:
|
||||
ffi_->direct_io = 1;
|
||||
ffi_->keep_cache = 0;
|
||||
|
|
@ -215,8 +210,8 @@ _create(const Policy::Search &searchFunc_,
|
|||
constexpr
|
||||
const
|
||||
uint64_t
|
||||
_(const PassthroughEnum e_,
|
||||
const uint64_t m_)
|
||||
_(const PassthroughIOEnum e_,
|
||||
const uint64_t m_)
|
||||
{
|
||||
return ((((uint64_t)e_) << 32) | (m_ & O_ACCMODE));
|
||||
}
|
||||
|
|
@ -234,8 +229,8 @@ _create_for_insert_lambda(const fuse_req_ctx_t *ctx_,
|
|||
const ugid::Set ugid(ctx_->uid,ctx_->gid);
|
||||
|
||||
::_config_to_ffi_flags(cfg,ctx_->pid,ffi_);
|
||||
if(cfg.writeback_cache)
|
||||
::_tweak_flags_writeback_cache(&ffi_->flags);
|
||||
if(cfg.cache_writeback)
|
||||
::_tweak_flags_cache_writeback(&ffi_->flags);
|
||||
ffi_->noflush = !::_calculate_flush(cfg.flushonclose,
|
||||
ffi_->flags);
|
||||
|
||||
|
|
@ -266,13 +261,13 @@ _create_for_insert_lambda(const fuse_req_ctx_t *ctx_,
|
|||
of_->ref_count = 1;
|
||||
of_->fi = fi;
|
||||
|
||||
switch(_(cfg.passthrough,ffi_->flags))
|
||||
switch(_(cfg.passthrough_io,ffi_->flags))
|
||||
{
|
||||
case _(Passthrough::ENUM::RO,O_RDONLY):
|
||||
case _(Passthrough::ENUM::WO,O_WRONLY):
|
||||
case _(Passthrough::ENUM::RW,O_RDONLY):
|
||||
case _(Passthrough::ENUM::RW,O_WRONLY):
|
||||
case _(Passthrough::ENUM::RW,O_RDWR):
|
||||
case _(PassthroughIO::ENUM::RO,O_RDONLY):
|
||||
case _(PassthroughIO::ENUM::WO,O_WRONLY):
|
||||
case _(PassthroughIO::ENUM::RW,O_RDONLY):
|
||||
case _(PassthroughIO::ENUM::RW,O_WRONLY):
|
||||
case _(PassthroughIO::ENUM::RW,O_RDWR):
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ _want_if_capable_max_pages(fuse_conn_info *conn_,
|
|||
if(::_capable(conn_,FUSE_CAP_MAX_PAGES))
|
||||
{
|
||||
::_want(conn_,FUSE_CAP_MAX_PAGES);
|
||||
conn_->max_pages = cfg_.fuse_msg_size;
|
||||
fuse_cfg.max_pages = cfg_.fuse_msg_size;
|
||||
SysLog::info("requesting max pages size of {}",
|
||||
(uint64_t)cfg_.fuse_msg_size);
|
||||
}
|
||||
|
|
@ -204,8 +204,8 @@ FUSE::init(fuse_conn_info *conn_)
|
|||
::_want_if_capable(conn_,FUSE_CAP_PARALLEL_DIROPS);
|
||||
::_want_if_capable(conn_,FUSE_CAP_PASSTHROUGH);
|
||||
::_want_if_capable(conn_,FUSE_CAP_POSIX_ACL,&cfg.posix_acl);
|
||||
::_want_if_capable(conn_,FUSE_CAP_READDIR_PLUS,&cfg.readdirplus);
|
||||
::_want_if_capable(conn_,FUSE_CAP_WRITEBACK_CACHE,&cfg.writeback_cache);
|
||||
// ::_want_if_capable(conn_,FUSE_CAP_READDIR_PLUS,&cfg.readdirplus);
|
||||
::_want_if_capable(conn_,FUSE_CAP_WRITEBACK_CACHE,&cfg.cache_writeback);
|
||||
::_want_if_capable(conn_,FUSE_CAP_ALLOW_IDMAP,&cfg.allow_idmap);
|
||||
// ::_want_if_capable(conn_,FUSE_CAP_READDIR_PLUS_AUTO);
|
||||
::_want_if_capable_max_pages(conn_,cfg);
|
||||
|
|
@ -214,21 +214,22 @@ FUSE::init(fuse_conn_info *conn_)
|
|||
|
||||
::_spawn_thread_to_set_readahead();
|
||||
|
||||
if(!(conn_->capable & FUSE_CAP_PASSTHROUGH) && (cfg.passthrough != Passthrough::ENUM::OFF))
|
||||
if(!(conn_->capable & FUSE_CAP_PASSTHROUGH) &&
|
||||
(cfg.passthrough_io != PassthroughIO::ENUM::OFF))
|
||||
{
|
||||
SysLog::warning("passthrough enabled but not supported by kernel. disabling.");
|
||||
cfg.passthrough = Passthrough::ENUM::OFF;
|
||||
cfg.passthrough_io = PassthroughIO::ENUM::OFF;
|
||||
}
|
||||
|
||||
if((cfg.passthrough != Passthrough::ENUM::OFF) &&
|
||||
(cfg.cache_files == CacheFiles::ENUM::OFF))
|
||||
if((cfg.passthrough_io != PassthroughIO::ENUM::OFF) &&
|
||||
(cfg.cache_files == CacheFiles::ENUM::OFF))
|
||||
{
|
||||
SysLog::warning("passthrough enabled and cache.files disabled:"
|
||||
" passthrough will not function");
|
||||
}
|
||||
|
||||
if((cfg.passthrough != Passthrough::ENUM::OFF) &&
|
||||
(cfg.writeback_cache == true))
|
||||
if((cfg.passthrough_io != PassthroughIO::ENUM::OFF) &&
|
||||
(cfg.cache_writeback == true))
|
||||
{
|
||||
SysLog::warning("passthrough and cache.writeback are incompatible.");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ _nfsopenhack(const fs::path &fullpath_,
|
|||
*/
|
||||
static
|
||||
void
|
||||
_tweak_flags_writeback_cache(int *flags_)
|
||||
_tweak_flags_cache_writeback(int *flags_)
|
||||
{
|
||||
if((*flags_ & O_ACCMODE) == O_WRONLY)
|
||||
*flags_ = ((*flags_ & ~O_ACCMODE) | O_RDWR);
|
||||
|
|
@ -145,11 +145,6 @@ _config_to_ffi_flags(Config &cfg_,
|
|||
{
|
||||
switch(cfg.cache_files)
|
||||
{
|
||||
case CacheFiles::ENUM::LIBFUSE:
|
||||
ffi_->direct_io = cfg.direct_io;
|
||||
ffi_->keep_cache = cfg.kernel_cache;
|
||||
ffi_->auto_cache = cfg.auto_cache;
|
||||
break;
|
||||
case CacheFiles::ENUM::OFF:
|
||||
ffi_->direct_io = 1;
|
||||
ffi_->keep_cache = 0;
|
||||
|
|
@ -272,8 +267,8 @@ _open(const Policy::Search &searchFunc_,
|
|||
constexpr
|
||||
const
|
||||
uint64_t
|
||||
_(const PassthroughEnum e_,
|
||||
const uint64_t m_)
|
||||
_(const PassthroughIOEnum e_,
|
||||
const uint64_t m_)
|
||||
{
|
||||
return ((((uint64_t)e_) << 32) | (m_ & O_ACCMODE));
|
||||
}
|
||||
|
|
@ -291,8 +286,8 @@ _open_for_insert_lambda(const fuse_req_ctx_t *ctx_,
|
|||
|
||||
::_config_to_ffi_flags(cfg,ctx_->pid,ffi_);
|
||||
|
||||
if(cfg.writeback_cache)
|
||||
::_tweak_flags_writeback_cache(&ffi_->flags);
|
||||
if(cfg.cache_writeback)
|
||||
::_tweak_flags_cache_writeback(&ffi_->flags);
|
||||
|
||||
ffi_->noflush = !::_calculate_flush(cfg.flushonclose,
|
||||
ffi_->flags);
|
||||
|
|
@ -312,13 +307,13 @@ _open_for_insert_lambda(const fuse_req_ctx_t *ctx_,
|
|||
of_->ref_count = 1;
|
||||
of_->fi = fi;
|
||||
|
||||
switch(_(cfg.passthrough,ffi_->flags))
|
||||
switch(_(cfg.passthrough_io,ffi_->flags))
|
||||
{
|
||||
case _(Passthrough::ENUM::RO,O_RDONLY):
|
||||
case _(Passthrough::ENUM::WO,O_WRONLY):
|
||||
case _(Passthrough::ENUM::RW,O_RDONLY):
|
||||
case _(Passthrough::ENUM::RW,O_WRONLY):
|
||||
case _(Passthrough::ENUM::RW,O_RDWR):
|
||||
case _(PassthroughIO::ENUM::RO,O_RDONLY):
|
||||
case _(PassthroughIO::ENUM::WO,O_WRONLY):
|
||||
case _(PassthroughIO::ENUM::RW,O_RDONLY):
|
||||
case _(PassthroughIO::ENUM::RW,O_WRONLY):
|
||||
case _(PassthroughIO::ENUM::RW,O_RDWR):
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
|
|
@ -347,8 +342,8 @@ _open_for_update_lambda(const fuse_req_ctx_t *ctx_,
|
|||
|
||||
::_config_to_ffi_flags(cfg,ctx_->pid,ffi_);
|
||||
|
||||
if(cfg.writeback_cache)
|
||||
::_tweak_flags_writeback_cache(&ffi_->flags);
|
||||
if(cfg.cache_writeback)
|
||||
::_tweak_flags_cache_writeback(&ffi_->flags);
|
||||
|
||||
ffi_->noflush = !::_calculate_flush(cfg.flushonclose,
|
||||
ffi_->flags);
|
||||
|
|
|
|||
506
src/mergerfs.cpp
506
src/mergerfs.cpp
|
|
@ -18,6 +18,7 @@
|
|||
#include "mergerfs_fsck.hpp"
|
||||
#include "mergerfs_collect_info.hpp"
|
||||
|
||||
#include "config.hpp"
|
||||
#include "fs_path.hpp"
|
||||
#include "fs_readahead.hpp"
|
||||
#include "fs_umount2.hpp"
|
||||
|
|
@ -89,272 +90,279 @@
|
|||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <unordered_set>
|
||||
|
||||
|
||||
namespace l
|
||||
static
|
||||
void
|
||||
_get_fuse_operations(struct fuse_operations &ops_,
|
||||
const bool nullrw_)
|
||||
{
|
||||
static
|
||||
void
|
||||
get_fuse_operations(struct fuse_operations &ops_,
|
||||
const bool nullrw_)
|
||||
{
|
||||
ops_.access = FUSE::access;
|
||||
ops_.bmap = FUSE::bmap;
|
||||
ops_.chmod = FUSE::chmod;
|
||||
ops_.chown = FUSE::chown;
|
||||
ops_.copy_file_range = FUSE::copy_file_range;
|
||||
ops_.create = FUSE::create;
|
||||
ops_.destroy = FUSE::destroy;
|
||||
ops_.fallocate = FUSE::fallocate;
|
||||
ops_.fchmod = FUSE::fchmod;
|
||||
ops_.fchown = FUSE::fchown;
|
||||
ops_.fgetattr = FUSE::fgetattr;
|
||||
ops_.flock = FUSE::flock;
|
||||
ops_.flush = FUSE::flush;
|
||||
ops_.fsync = FUSE::fsync;
|
||||
ops_.fsyncdir = FUSE::fsyncdir;
|
||||
ops_.ftruncate = FUSE::ftruncate;
|
||||
ops_.futimens = FUSE::futimens;
|
||||
ops_.getattr = FUSE::getattr;
|
||||
ops_.getxattr = FUSE::getxattr;
|
||||
ops_.init = FUSE::init;
|
||||
ops_.ioctl = FUSE::ioctl;
|
||||
ops_.link = FUSE::link;
|
||||
ops_.listxattr = FUSE::listxattr;
|
||||
ops_.lock = FUSE::lock;
|
||||
ops_.mkdir = FUSE::mkdir;
|
||||
ops_.mknod = FUSE::mknod;
|
||||
ops_.open = FUSE::open;
|
||||
ops_.opendir = FUSE::opendir;
|
||||
ops_.poll = FUSE::poll;;
|
||||
ops_.read = (nullrw_ ? FUSE::read_null : FUSE::read);
|
||||
ops_.readdir = FUSE::readdir;
|
||||
ops_.readdir_plus = FUSE::readdir_plus;
|
||||
ops_.readlink = FUSE::readlink;
|
||||
ops_.release = FUSE::release;
|
||||
ops_.releasedir = FUSE::releasedir;
|
||||
ops_.removemapping = FUSE::removemapping;
|
||||
ops_.removexattr = FUSE::removexattr;
|
||||
ops_.rename = FUSE::rename;
|
||||
ops_.rmdir = FUSE::rmdir;
|
||||
ops_.setupmapping = FUSE::setupmapping;
|
||||
ops_.setxattr = FUSE::setxattr;
|
||||
ops_.statfs = FUSE::statfs;
|
||||
ops_.statx = FUSE::statx;
|
||||
ops_.statx_fh = FUSE::statx_fh;
|
||||
ops_.symlink = FUSE::symlink;
|
||||
ops_.syncfs = FUSE::syncfs;
|
||||
ops_.tmpfile = FUSE::tmpfile;
|
||||
ops_.truncate = FUSE::truncate;
|
||||
ops_.unlink = FUSE::unlink;
|
||||
ops_.utimens = FUSE::utimens;
|
||||
ops_.write = (nullrw_ ? FUSE::write_null : FUSE::write);
|
||||
ops_.access = FUSE::access;
|
||||
ops_.bmap = FUSE::bmap;
|
||||
ops_.chmod = FUSE::chmod;
|
||||
ops_.chown = FUSE::chown;
|
||||
ops_.copy_file_range = FUSE::copy_file_range;
|
||||
ops_.create = FUSE::create;
|
||||
ops_.destroy = FUSE::destroy;
|
||||
ops_.fallocate = FUSE::fallocate;
|
||||
ops_.fchmod = FUSE::fchmod;
|
||||
ops_.fchown = FUSE::fchown;
|
||||
ops_.fgetattr = FUSE::fgetattr;
|
||||
ops_.flock = FUSE::flock;
|
||||
ops_.flush = FUSE::flush;
|
||||
ops_.fsync = FUSE::fsync;
|
||||
ops_.fsyncdir = FUSE::fsyncdir;
|
||||
ops_.ftruncate = FUSE::ftruncate;
|
||||
ops_.futimens = FUSE::futimens;
|
||||
ops_.getattr = FUSE::getattr;
|
||||
ops_.getxattr = FUSE::getxattr;
|
||||
ops_.init = FUSE::init;
|
||||
ops_.ioctl = FUSE::ioctl;
|
||||
ops_.link = FUSE::link;
|
||||
ops_.listxattr = FUSE::listxattr;
|
||||
ops_.lock = FUSE::lock;
|
||||
ops_.mkdir = FUSE::mkdir;
|
||||
ops_.mknod = FUSE::mknod;
|
||||
ops_.open = FUSE::open;
|
||||
ops_.opendir = FUSE::opendir;
|
||||
ops_.poll = FUSE::poll;;
|
||||
ops_.read = (nullrw_ ? FUSE::read_null : FUSE::read);
|
||||
ops_.readdir = FUSE::readdir;
|
||||
ops_.readdir_plus = FUSE::readdir_plus;
|
||||
ops_.readlink = FUSE::readlink;
|
||||
ops_.release = FUSE::release;
|
||||
ops_.releasedir = FUSE::releasedir;
|
||||
ops_.removemapping = FUSE::removemapping;
|
||||
ops_.removexattr = FUSE::removexattr;
|
||||
ops_.rename = FUSE::rename;
|
||||
ops_.rmdir = FUSE::rmdir;
|
||||
ops_.setupmapping = FUSE::setupmapping;
|
||||
ops_.setxattr = FUSE::setxattr;
|
||||
ops_.statfs = FUSE::statfs;
|
||||
ops_.statx = FUSE::statx;
|
||||
ops_.statx_fh = FUSE::statx_fh;
|
||||
ops_.symlink = FUSE::symlink;
|
||||
ops_.syncfs = FUSE::syncfs;
|
||||
ops_.tmpfile = FUSE::tmpfile;
|
||||
ops_.truncate = FUSE::truncate;
|
||||
ops_.unlink = FUSE::unlink;
|
||||
ops_.utimens = FUSE::utimens;
|
||||
ops_.write = (nullrw_ ? FUSE::write_null : FUSE::write);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
_setup_resources(const int scheduling_priority_)
|
||||
{
|
||||
std::srand(time(NULL));
|
||||
resources::reset_umask();
|
||||
resources::maxout_rlimit_nofile();
|
||||
resources::maxout_rlimit_fsize();
|
||||
resources::setpriority(scheduling_priority_);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
_set_oom_score_adj()
|
||||
{
|
||||
int rv;
|
||||
int orig;
|
||||
int score;
|
||||
|
||||
if(!oom::has_oom_score_adj())
|
||||
return;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
setup_resources(const int scheduling_priority_)
|
||||
{
|
||||
std::srand(time(NULL));
|
||||
resources::reset_umask();
|
||||
resources::maxout_rlimit_nofile();
|
||||
resources::maxout_rlimit_fsize();
|
||||
resources::setpriority(scheduling_priority_);
|
||||
}
|
||||
score = -990;
|
||||
|
||||
static
|
||||
void
|
||||
set_oom_score_adj()
|
||||
{
|
||||
int rv;
|
||||
int orig;
|
||||
int score;
|
||||
orig = oom::get_oom_score_adj();
|
||||
rv = oom::set_oom_score_adj(score);
|
||||
|
||||
if(!oom::has_oom_score_adj())
|
||||
return;
|
||||
(void)rv;
|
||||
SysLog::info("set oom_score_adj to {}, originally {}",
|
||||
score,
|
||||
orig);
|
||||
}
|
||||
|
||||
score = -990;
|
||||
static
|
||||
bool
|
||||
_wait_for_mount()
|
||||
{
|
||||
int failures;
|
||||
std::vector<fs::path> paths;
|
||||
std::chrono::milliseconds timeout;
|
||||
|
||||
orig = oom::get_oom_score_adj();
|
||||
rv = oom::set_oom_score_adj(score);
|
||||
paths = cfg.branches->to_paths();
|
||||
|
||||
(void)rv;
|
||||
SysLog::info("set oom_score_adj to {}, originally {}",
|
||||
score,
|
||||
orig);
|
||||
}
|
||||
SysLog::info("Waiting {} seconds for {} branches to mount",
|
||||
(uint64_t)cfg.branches_mount_timeout,
|
||||
paths.size());
|
||||
|
||||
static
|
||||
bool
|
||||
wait_for_mount(const Config &cfg_)
|
||||
{
|
||||
int failures;
|
||||
std::vector<fs::path> paths;
|
||||
std::chrono::milliseconds timeout;
|
||||
|
||||
paths = cfg_.branches->to_paths();
|
||||
|
||||
SysLog::info("Waiting {} seconds for {} branches to mount",
|
||||
(uint64_t)cfg_.branches_mount_timeout,
|
||||
paths.size());
|
||||
|
||||
timeout = std::chrono::milliseconds(cfg_.branches_mount_timeout * 1000);
|
||||
failures = fs::wait_for_mount(cfg_.mountpoint,
|
||||
paths,
|
||||
timeout);
|
||||
if(failures)
|
||||
{
|
||||
if(cfg_.branches_mount_timeout_fail)
|
||||
{
|
||||
SysLog::error("{} of {} branches were not mounted"
|
||||
" within the timeout of {}s. Exiting",
|
||||
failures,
|
||||
paths.size(),
|
||||
(uint64_t)cfg_.branches_mount_timeout);
|
||||
return true;
|
||||
}
|
||||
|
||||
SysLog::warning("Continuing to mount mergerfs despite {} branches not "
|
||||
"being different from the mountpoint filesystem",
|
||||
failures);
|
||||
}
|
||||
else
|
||||
{
|
||||
SysLog::info("All {} branches are mounted",
|
||||
paths.size());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
lazy_umount(const fs::path &target_)
|
||||
{
|
||||
int rv;
|
||||
|
||||
rv = fs::umount_lazy(target_);
|
||||
switch(rv)
|
||||
{
|
||||
case 0:
|
||||
SysLog::notice("{} has been successfully lazily unmounted",
|
||||
target_.string());
|
||||
break;
|
||||
case -EINVAL:
|
||||
SysLog::notice("{} was not a mount point needing to be unmounted",
|
||||
target_.string());
|
||||
break;
|
||||
default:
|
||||
SysLog::error("Error unmounting {}: {} - {}",
|
||||
target_.string(),
|
||||
-rv,
|
||||
strerror(-rv));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
usr1_signal_handler(int signal_)
|
||||
{
|
||||
// SysLog::info("Received SIGUSR1 - invalidating all nodes");
|
||||
// fuse_invalidate_all_nodes();
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
usr2_signal_handler(int signal_)
|
||||
{
|
||||
// SysLog::info("Received SIGUSR2 - triggering thorough gc");
|
||||
// fuse_gc();
|
||||
// GIDCache::clear_all();
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
setup_signal_handlers()
|
||||
{
|
||||
std::signal(SIGUSR1,l::usr1_signal_handler);
|
||||
std::signal(SIGUSR2,l::usr2_signal_handler);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
warn_if_not_root()
|
||||
{
|
||||
uid_t uid;
|
||||
|
||||
uid = geteuid();
|
||||
if(uid == 0)
|
||||
return;
|
||||
|
||||
constexpr const char s[] = "mergerfs is not running as root and may not work correctly\n";
|
||||
fmt::print(stderr,"warning: {}",s);
|
||||
SysLog::warning(s);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc_,
|
||||
char **argv_)
|
||||
{
|
||||
int rv;
|
||||
Config::ErrVec errs;
|
||||
fuse_args args;
|
||||
fuse_operations ops;
|
||||
|
||||
SysLog::open();
|
||||
|
||||
memset(&ops,0,sizeof(fuse_operations));
|
||||
|
||||
args.argc = argc_;
|
||||
args.argv = argv_;
|
||||
args.allocated = 0;
|
||||
|
||||
options::parse(&args,&errs);
|
||||
if(errs.size())
|
||||
{
|
||||
std::cerr << errs << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(cfg.branches_mount_timeout > 0)
|
||||
{
|
||||
bool failure;
|
||||
|
||||
failure = l::wait_for_mount(cfg);
|
||||
if(failure)
|
||||
return 1;
|
||||
}
|
||||
|
||||
SysLog::info("mergerfs v{} started",MERGERFS_VERSION);
|
||||
SysLog::info("Go to https://trapexit.github.io/mergerfs/support for support");
|
||||
l::warn_if_not_root();
|
||||
|
||||
MaintenanceThread::push_job([](int count_)
|
||||
timeout = std::chrono::milliseconds(cfg.branches_mount_timeout * 1000);
|
||||
failures = fs::wait_for_mount(cfg.mountpoint,
|
||||
paths,
|
||||
timeout);
|
||||
if(failures)
|
||||
{
|
||||
if((count_ % 60) == 0)
|
||||
GIDCache::clear_unused();
|
||||
});
|
||||
l::setup_resources(cfg.scheduling_priority);
|
||||
l::setup_signal_handlers();
|
||||
l::set_oom_score_adj();
|
||||
l::get_fuse_operations(ops,cfg.nullrw);
|
||||
if(cfg.branches_mount_timeout_fail)
|
||||
{
|
||||
SysLog::error("{} of {} branches were not mounted"
|
||||
" within the timeout of {}s. Exiting",
|
||||
failures,
|
||||
paths.size(),
|
||||
(uint64_t)cfg.branches_mount_timeout);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(cfg.lazy_umount_mountpoint)
|
||||
l::lazy_umount(cfg.mountpoint);
|
||||
SysLog::warning("Continuing to mount mergerfs despite {} branches not "
|
||||
"being different from the mountpoint filesystem",
|
||||
failures);
|
||||
}
|
||||
else
|
||||
{
|
||||
SysLog::info("All {} branches are mounted",
|
||||
paths.size());
|
||||
}
|
||||
|
||||
rv = fuse_main(args.argc,
|
||||
args.argv,
|
||||
&ops);
|
||||
return false;
|
||||
}
|
||||
|
||||
SysLog::info("exiting main loop with return code {}",rv);
|
||||
static
|
||||
void
|
||||
_lazy_umount(const fs::path &target_)
|
||||
{
|
||||
int rv;
|
||||
|
||||
SysLog::close();
|
||||
rv = fs::umount_lazy(target_);
|
||||
switch(rv)
|
||||
{
|
||||
case 0:
|
||||
SysLog::notice("{} has been successfully lazily unmounted",
|
||||
target_.string());
|
||||
break;
|
||||
case -EINVAL:
|
||||
SysLog::notice("{} was not a mount point needing to be unmounted",
|
||||
target_.string());
|
||||
break;
|
||||
default:
|
||||
SysLog::error("Error unmounting {}: {} - {}",
|
||||
target_.string(),
|
||||
-rv,
|
||||
strerror(-rv));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
static
|
||||
void
|
||||
_usr1_signal_handler(int signal_)
|
||||
{
|
||||
// SysLog::info("Received SIGUSR1 - invalidating all nodes");
|
||||
// fuse_invalidate_all_nodes();
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
_usr2_signal_handler(int signal_)
|
||||
{
|
||||
// SysLog::info("Received SIGUSR2 - triggering thorough gc");
|
||||
// fuse_gc();
|
||||
// GIDCache::clear_all();
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
_setup_signal_handlers()
|
||||
{
|
||||
std::signal(SIGUSR1,::_usr1_signal_handler);
|
||||
std::signal(SIGUSR2,::_usr2_signal_handler);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
_warn_if_not_root()
|
||||
{
|
||||
uid_t uid;
|
||||
|
||||
uid = geteuid();
|
||||
if(uid == 0)
|
||||
return;
|
||||
|
||||
constexpr const char s[] =
|
||||
"mergerfs is not running as root"
|
||||
" and may not work correctly";
|
||||
fmt::println(stderr,"* WARNING: {}",s);
|
||||
SysLog::warning(s);
|
||||
}
|
||||
|
||||
int
|
||||
_main(int argc_,
|
||||
char **argv_)
|
||||
{
|
||||
int rv;
|
||||
fuse_args args;
|
||||
fuse_operations ops;
|
||||
|
||||
SysLog::open();
|
||||
|
||||
memset(&ops,0,sizeof(fuse_operations));
|
||||
|
||||
args.argc = argc_;
|
||||
args.argv = argv_;
|
||||
args.allocated = false;
|
||||
|
||||
SysLog::info("mergerfs v{} started",MERGERFS_VERSION);
|
||||
SysLog::info("Go to https://trapexit.github.io/mergerfs/support for support");
|
||||
|
||||
options::parse(&args);
|
||||
if(!cfg.errs.empty())
|
||||
{
|
||||
for(auto &err : cfg.errs)
|
||||
{
|
||||
std::string s = err.to_string();
|
||||
|
||||
SysLog::error("error: {}",s);
|
||||
fmt::println(stderr,"* ERROR: {}",s);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(cfg.branches_mount_timeout > 0)
|
||||
{
|
||||
bool failure;
|
||||
|
||||
failure = ::_wait_for_mount();
|
||||
if(failure)
|
||||
return 1;
|
||||
}
|
||||
|
||||
::_warn_if_not_root();
|
||||
|
||||
MaintenanceThread::push_job([](int count_)
|
||||
{
|
||||
if((count_ % 60) == 0)
|
||||
GIDCache::clear_unused();
|
||||
});
|
||||
::_setup_resources(cfg.scheduling_priority);
|
||||
::_setup_signal_handlers();
|
||||
::_set_oom_score_adj();
|
||||
::_get_fuse_operations(ops,cfg.nullrw);
|
||||
|
||||
if(cfg.lazy_umount_mountpoint)
|
||||
::_lazy_umount(cfg.mountpoint);
|
||||
|
||||
rv = fuse_main(args.argc,
|
||||
args.argv,
|
||||
&ops);
|
||||
|
||||
SysLog::info("exiting main loop with return code {}",rv);
|
||||
|
||||
SysLog::close();
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static
|
||||
|
|
@ -372,7 +380,7 @@ _pick_app_and_run(int argc_,
|
|||
if(appname == "mergerfs.collect-info")
|
||||
return mergerfs::collect_info::main(argc_,argv_);
|
||||
|
||||
return l::main(argc_,argv_);
|
||||
return ::_main(argc_,argv_);
|
||||
}
|
||||
|
||||
int
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@
|
|||
#include "version.hpp"
|
||||
|
||||
#include "fuse.h"
|
||||
#include "fuse_config.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <fstream>
|
||||
|
|
@ -49,10 +48,16 @@
|
|||
|
||||
enum
|
||||
{
|
||||
MERGERFS_OPT_DEBUG,
|
||||
MERGERFS_OPT_HELP,
|
||||
MERGERFS_OPT_VERSION
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
OPT_DISCARD = 0,
|
||||
OPT_KEEP = 1
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
|
|
@ -79,30 +84,19 @@ _set_kv_option(const std::string &key_,
|
|||
|
||||
static
|
||||
void
|
||||
_set_fuse_threads(Config &cfg_)
|
||||
_set_fsname(fuse_args *args_)
|
||||
{
|
||||
fuse_config_set_read_thread_count(cfg_.fuse_read_thread_count);
|
||||
fuse_config_set_process_thread_count(cfg_.fuse_process_thread_count);
|
||||
fuse_config_set_process_thread_queue_depth(cfg_.fuse_process_thread_queue_depth);
|
||||
fuse_config_set_pin_threads(cfg_.fuse_pin_threads);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
_set_fsname(Config &cfg_,
|
||||
fuse_args *args_)
|
||||
{
|
||||
if(cfg_.fsname->empty())
|
||||
if(cfg.fsname->empty())
|
||||
{
|
||||
std::vector<std::string> paths;
|
||||
|
||||
cfg_.branches->to_paths(paths);
|
||||
cfg.branches->to_paths(paths);
|
||||
|
||||
if(paths.size() > 0)
|
||||
cfg_.fsname = str::remove_common_prefix_and_join(paths,':');
|
||||
cfg.fsname = str::remove_common_prefix_and_join(paths,':');
|
||||
}
|
||||
|
||||
::_set_kv_option("fsname",cfg_.fsname,args_);
|
||||
::_set_kv_option("fsname",cfg.fsname,args_);
|
||||
}
|
||||
|
||||
static
|
||||
|
|
@ -114,10 +108,9 @@ _set_subtype(fuse_args *args_)
|
|||
|
||||
static
|
||||
void
|
||||
_set_default_options(fuse_args *args_,
|
||||
Config &cfg_)
|
||||
_set_default_options(fuse_args *args_)
|
||||
{
|
||||
if(cfg_.kernel_permissions_check)
|
||||
if(cfg.kernel_permissions_check)
|
||||
::_set_option("default_permissions",args_);
|
||||
|
||||
if(geteuid() == 0)
|
||||
|
|
@ -126,84 +119,28 @@ _set_default_options(fuse_args *args_,
|
|||
SysLog::notice("not auto setting allow_other since not running as root");
|
||||
}
|
||||
|
||||
static
|
||||
bool
|
||||
_should_ignore(const std::string &key_)
|
||||
{
|
||||
constexpr const std::array<std::string_view,13> ignored_keys =
|
||||
{
|
||||
"atomic_o_trunc",
|
||||
"big_writes",
|
||||
"cache.open",
|
||||
"defaults",
|
||||
"hard_remove",
|
||||
"no_splice_move",
|
||||
"no_splice_read",
|
||||
"no_splice_write",
|
||||
"nonempty",
|
||||
"splice_move",
|
||||
"splice_read",
|
||||
"splice_write",
|
||||
"use_ino",
|
||||
};
|
||||
|
||||
for(const auto &key : ignored_keys)
|
||||
{
|
||||
if(key == key_)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
_parse_and_process_kv_arg(Config &cfg_,
|
||||
Config::ErrVec *errs_,
|
||||
const std::string &key_,
|
||||
_parse_and_process_kv_arg(const std::string &key_,
|
||||
const std::string &val_)
|
||||
{
|
||||
int rv;
|
||||
std::string key(key_);
|
||||
std::string val(val_);
|
||||
|
||||
rv = 0;
|
||||
if(key == "config")
|
||||
return ((cfg_.from_file(val_,errs_) < 0) ? 1 : 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(::_should_ignore(key_))
|
||||
return 0;
|
||||
|
||||
if(cfg_.has_key(key) == false)
|
||||
if(cfg.has_key(key) == false)
|
||||
return 1;
|
||||
|
||||
rv = cfg_.set_raw(key,val);
|
||||
if(rv)
|
||||
errs_->push_back({rv,key+'='+val});
|
||||
rv = cfg.set(key,val);
|
||||
if(rv < 0)
|
||||
cfg.errs.push_back({-rv,key+'='+val});
|
||||
|
||||
return 0;
|
||||
return OPT_DISCARD;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
_process_opt(Config &cfg_,
|
||||
Config::ErrVec *errs_,
|
||||
const std::string &arg_)
|
||||
_process_opt(const std::string &arg_)
|
||||
{
|
||||
std::string key;
|
||||
std::string val;
|
||||
|
|
@ -212,68 +149,7 @@ _process_opt(Config &cfg_,
|
|||
key = str::trim(key);
|
||||
val = str::trim(val);
|
||||
|
||||
return ::_parse_and_process_kv_arg(cfg_,errs_,key,val);
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
_process_branches(Config &cfg_,
|
||||
Config::ErrVec *errs_,
|
||||
const char *arg_)
|
||||
{
|
||||
int rv;
|
||||
std::string arg;
|
||||
|
||||
arg = arg_;
|
||||
rv = cfg_.set_raw("branches",arg);
|
||||
if(rv)
|
||||
errs_->push_back({rv,"branches="+arg});
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
_process_mount(Config &cfg_,
|
||||
Config::ErrVec *errs_,
|
||||
const char *arg_)
|
||||
{
|
||||
int rv;
|
||||
std::string arg;
|
||||
|
||||
arg = arg_;
|
||||
rv = cfg_.set_raw("mount",arg);
|
||||
if(rv)
|
||||
errs_->push_back({rv,"mount="+arg});
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
_postprocess_passthrough(Config &cfg_)
|
||||
{
|
||||
if(cfg_.passthrough == Passthrough::ENUM::OFF)
|
||||
return;
|
||||
|
||||
if(cfg_.cache_files == CacheFiles::ENUM::OFF)
|
||||
{
|
||||
SysLog::warning("'cache.files' can not be 'off' when using 'passthrough'."
|
||||
" Setting 'cache.files=full'");
|
||||
cfg_.cache_files = CacheFiles::ENUM::FULL;
|
||||
}
|
||||
|
||||
if(cfg_.writeback_cache == true)
|
||||
{
|
||||
SysLog::warning("'cache.writeback' can not be enabled when using 'passthrough'."
|
||||
" Setting 'cache.writeback=false'");
|
||||
cfg_.writeback_cache = false;
|
||||
}
|
||||
|
||||
if(cfg_.moveonenospc.enabled == true)
|
||||
{
|
||||
SysLog::warning("`moveonenospc` will not function when `passthrough` is enabled");
|
||||
}
|
||||
return ::_parse_and_process_kv_arg(key,val);
|
||||
}
|
||||
|
||||
static
|
||||
|
|
@ -285,6 +161,36 @@ _usage(void)
|
|||
"Visit https://trapexit.github.io/mergerfs for options.\n\n");
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
_version(void)
|
||||
{
|
||||
fmt::print("mergerfs v{}\n\n"
|
||||
"https://github.com/trapexit/mergerfs\n"
|
||||
"https://trapexit.github.io/mergerfs\n"
|
||||
"https://github.com/trapexit/support\n"
|
||||
"\n"
|
||||
"ISC License (ISC)\n"
|
||||
"\n"
|
||||
"Copyright 2025, Antonio SJ Musumeci <trapexit@spawn.link>\n"
|
||||
"\n"
|
||||
"Permission to use, copy, modify, and/or distribute this software for\n"
|
||||
"any purpose with or without fee is hereby granted, provided that the\n"
|
||||
"above copyright notice and this permission notice appear in all\n"
|
||||
"copies.\n"
|
||||
"\n"
|
||||
"THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL\n"
|
||||
"WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED\n"
|
||||
"WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE\n"
|
||||
"AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL\n"
|
||||
"DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR\n"
|
||||
"PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER\n"
|
||||
"TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\n"
|
||||
"PERFORMANCE OF THIS SOFTWARE.\n\n"
|
||||
,
|
||||
(MERGERFS_VERSION[0] ? MERGERFS_VERSION : "unknown"));
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
_option_processor(void *data_,
|
||||
|
|
@ -292,44 +198,38 @@ _option_processor(void *data_,
|
|||
int key_,
|
||||
fuse_args *outargs_)
|
||||
{
|
||||
Config::ErrVec *errs = (Config::ErrVec*)data_;
|
||||
std::string &prev_nonopt =
|
||||
*reinterpret_cast<std::string*>(data_);
|
||||
|
||||
switch(key_)
|
||||
{
|
||||
case FUSE_OPT_KEY_OPT:
|
||||
return ::_process_opt(cfg,errs,arg_);
|
||||
return ::_process_opt(arg_);
|
||||
|
||||
case FUSE_OPT_KEY_NONOPT:
|
||||
if(cfg.branches->empty())
|
||||
return ::_process_branches(cfg,errs,arg_);
|
||||
else
|
||||
return ::_process_mount(cfg,errs,arg_);
|
||||
{
|
||||
if(prev_nonopt.empty())
|
||||
{
|
||||
prev_nonopt = arg_;
|
||||
}
|
||||
else
|
||||
{
|
||||
cfg.branches.from_string("+>" + prev_nonopt);
|
||||
prev_nonopt = arg_;
|
||||
}
|
||||
}
|
||||
return OPT_DISCARD;
|
||||
|
||||
case MERGERFS_OPT_DEBUG:
|
||||
fuse_cfg.debug = true;
|
||||
return OPT_DISCARD;
|
||||
|
||||
case MERGERFS_OPT_HELP:
|
||||
::_usage();
|
||||
exit(0);
|
||||
|
||||
case MERGERFS_OPT_VERSION:
|
||||
fmt::print("mergerfs v{}\n\n"
|
||||
"https://github.com/trapexit/mergerfs\n"
|
||||
"https://trapexit.github.io/mergerfs\n"
|
||||
"https://github.com/trapexit/support\n\n"
|
||||
"ISC License (ISC)\n\n"
|
||||
"Copyright 2025, Antonio SJ Musumeci <trapexit@spawn.link>\n\n"
|
||||
"Permission to use, copy, modify, and/or distribute this software for\n"
|
||||
"any purpose with or without fee is hereby granted, provided that the\n"
|
||||
"above copyright notice and this permission notice appear in all\n"
|
||||
"copies.\n\n"
|
||||
"THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL\n"
|
||||
"WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED\n"
|
||||
"WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE\n"
|
||||
"AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL\n"
|
||||
"DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR\n"
|
||||
"PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER\n"
|
||||
"TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\n"
|
||||
"PERFORMANCE OF THIS SOFTWARE.\n\n"
|
||||
,
|
||||
(MERGERFS_VERSION[0] ? MERGERFS_VERSION : "unknown"));
|
||||
::_version();
|
||||
exit(0);
|
||||
|
||||
default:
|
||||
|
|
@ -341,15 +241,14 @@ _option_processor(void *data_,
|
|||
|
||||
static
|
||||
void
|
||||
_check_for_mount_loop(Config &cfg_,
|
||||
Config::ErrVec *errs_)
|
||||
_check_for_mount_loop()
|
||||
{
|
||||
fs::path mount;
|
||||
std::vector<fs::path> branches;
|
||||
std::error_code ec;
|
||||
|
||||
mount = *cfg_.mountpoint;
|
||||
branches = cfg_.branches->to_paths();
|
||||
mount = cfg.mountpoint;
|
||||
branches = cfg.branches->to_paths();
|
||||
for(const auto &branch : branches)
|
||||
{
|
||||
if(std::filesystem::equivalent(branch,mount,ec))
|
||||
|
|
@ -358,54 +257,66 @@ _check_for_mount_loop(Config &cfg_,
|
|||
|
||||
errstr = fmt::format("branches can not include the mountpoint: {}",
|
||||
branch.string());
|
||||
errs_->push_back({0,errstr});
|
||||
cfg.errs.push_back({0,errstr});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
_print_warnings(Config &cfg_)
|
||||
_postprocess_and_print_warnings()
|
||||
{
|
||||
if(cfg_.passthrough != Passthrough::ENUM::OFF)
|
||||
if(fuse_cfg.uid != FUSE_CFG_INVALID_ID)
|
||||
SysLog::warning("overwriting 'uid' is untested and unsupported,"
|
||||
" use at your own risk");
|
||||
if(fuse_cfg.gid != FUSE_CFG_INVALID_ID)
|
||||
SysLog::warning("overwriting 'gid' is untested and unsupported,"
|
||||
" use at your own risk");
|
||||
if(fuse_cfg.umask != FUSE_CFG_INVALID_UMASK)
|
||||
SysLog::warning("overwriting 'umask' is untested and unsupported,"
|
||||
" use at your own risk");
|
||||
|
||||
if(cfg.passthrough_io != PassthroughIO::ENUM::OFF)
|
||||
{
|
||||
if(cfg_.cache_files == CacheFiles::ENUM::OFF)
|
||||
if(cfg.cache_files == CacheFiles::ENUM::OFF)
|
||||
{
|
||||
SysLog::warning("'cache.files' can not be 'off' when using 'passthrough'."
|
||||
" Setting 'cache.files=auto-full'");
|
||||
cfg_.cache_files = CacheFiles::ENUM::AUTO_FULL;
|
||||
cfg.cache_files = CacheFiles::ENUM::AUTO_FULL;
|
||||
}
|
||||
|
||||
if(cfg_.writeback_cache == true)
|
||||
if(cfg.cache_writeback == true)
|
||||
{
|
||||
SysLog::warning("'cache.writeback' can not be enabled when using 'passthrough'."
|
||||
" Setting 'cache.writeback=false'");
|
||||
cfg_.writeback_cache = false;
|
||||
cfg.cache_writeback = false;
|
||||
}
|
||||
|
||||
if(cfg_.moveonenospc.enabled == true)
|
||||
if(cfg.moveonenospc.enabled == true)
|
||||
{
|
||||
SysLog::warning("`moveonenospc` will not function when `passthrough` is enabled");
|
||||
SysLog::warning("'moveonenospc' will not function when 'passthrough' is enabled");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
_cleanup_options(Config &cfg_)
|
||||
_cleanup_options()
|
||||
{
|
||||
if(!cfg_.symlinkify)
|
||||
cfg_.symlinkify_timeout = -1;
|
||||
if(!cfg.symlinkify)
|
||||
cfg.symlinkify_timeout = -1;
|
||||
}
|
||||
|
||||
namespace options
|
||||
{
|
||||
void
|
||||
parse(fuse_args *args_,
|
||||
Config::ErrVec *errs_)
|
||||
parse(fuse_args *args_)
|
||||
|
||||
{
|
||||
std::string prev_nonopt;
|
||||
const struct fuse_opt opts[] =
|
||||
{
|
||||
FUSE_OPT_KEY("-d",MERGERFS_OPT_DEBUG),
|
||||
FUSE_OPT_KEY("-h",MERGERFS_OPT_HELP),
|
||||
FUSE_OPT_KEY("--help",MERGERFS_OPT_HELP),
|
||||
FUSE_OPT_KEY("-v",MERGERFS_OPT_VERSION),
|
||||
|
|
@ -415,23 +326,34 @@ namespace options
|
|||
};
|
||||
|
||||
fuse_opt_parse(args_,
|
||||
errs_,
|
||||
&prev_nonopt,
|
||||
opts,
|
||||
::_option_processor);
|
||||
|
||||
if(cfg.branches->empty())
|
||||
errs_->push_back({0,"branches not set"});
|
||||
if(cfg.mountpoint->empty())
|
||||
errs_->push_back({0,"mountpoint not set"});
|
||||
if(!prev_nonopt.empty())
|
||||
{
|
||||
if(cfg.mountpoint.empty())
|
||||
cfg.mountpoint = prev_nonopt;
|
||||
else
|
||||
cfg.branches.from_string("+>" + prev_nonopt);
|
||||
}
|
||||
|
||||
::_postprocess_passthrough(cfg);
|
||||
::_check_for_mount_loop(cfg,errs_);
|
||||
::_set_default_options(args_,cfg);
|
||||
::_set_fsname(cfg,args_);
|
||||
if(cfg.branches->empty())
|
||||
cfg.errs.push_back({EINVAL,"branches not set"});
|
||||
if(cfg.mountpoint.empty())
|
||||
cfg.errs.push_back({EINVAL,"mountpoint not set"});
|
||||
|
||||
if(!cfg.errs.empty())
|
||||
return;
|
||||
|
||||
fuse_opt_add_arg(args_,cfg.mountpoint.c_str());
|
||||
|
||||
::_postprocess_and_print_warnings();
|
||||
::_check_for_mount_loop();
|
||||
::_set_default_options(args_);
|
||||
::_set_fsname(args_);
|
||||
::_set_subtype(args_);
|
||||
::_set_fuse_threads(cfg);
|
||||
::_print_warnings(cfg);
|
||||
::_cleanup_options(cfg);
|
||||
::_cleanup_options();
|
||||
|
||||
cfg.finish_initializing();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,14 +16,11 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "config.hpp"
|
||||
|
||||
#include "fuse.h"
|
||||
|
||||
|
||||
namespace options
|
||||
{
|
||||
void
|
||||
parse(fuse_args *args,
|
||||
Config::ErrVec *errs);
|
||||
parse(fuse_args *args);
|
||||
}
|
||||
|
|
|
|||
16
src/str.cpp
16
src/str.cpp
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include "str.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <set>
|
||||
|
|
@ -331,3 +332,18 @@ str::startswith(const char *s_,
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string
|
||||
str::replace(const std::string &s_,
|
||||
const char src_,
|
||||
const char dst_)
|
||||
{
|
||||
std::string s(s_);
|
||||
|
||||
std::replace(s.begin(),
|
||||
s.end(),
|
||||
src_,
|
||||
dst_);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -101,4 +101,9 @@ namespace str
|
|||
bool
|
||||
eq(const char *s0,
|
||||
const char *s1);
|
||||
|
||||
std::string
|
||||
replace(const std::string &s,
|
||||
const char src,
|
||||
const char dst);
|
||||
}
|
||||
|
|
|
|||
39
src/tofrom_ref.hpp
Normal file
39
src/tofrom_ref.hpp
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
|
||||
#include "to_string.hpp"
|
||||
#include "from_string.hpp"
|
||||
#include "tofrom_string.hpp"
|
||||
|
||||
|
||||
template<typename T>
|
||||
class TFSRef : public ToFromString
|
||||
{
|
||||
public:
|
||||
int
|
||||
from_string(const std::string_view s_) final
|
||||
{
|
||||
return str::from(s_,&_data);
|
||||
}
|
||||
|
||||
std::string
|
||||
to_string(void) const final
|
||||
{
|
||||
return str::to(_data);
|
||||
}
|
||||
|
||||
public:
|
||||
TFSRef(T &data_)
|
||||
: _data(data_)
|
||||
{
|
||||
}
|
||||
|
||||
TFSRef(T &data_,
|
||||
const T val_)
|
||||
: _data(data_)
|
||||
{
|
||||
_data = val_;
|
||||
}
|
||||
|
||||
private:
|
||||
T &_data;
|
||||
};
|
||||
|
|
@ -26,4 +26,6 @@ class ToFromString
|
|||
public:
|
||||
virtual std::string to_string() const = 0;
|
||||
virtual int from_string(const std::string_view) = 0;
|
||||
bool display = true;
|
||||
bool ro = false;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ public:
|
|||
int
|
||||
from_string(const std::string_view s_) final
|
||||
{
|
||||
return -EINVAL;
|
||||
return -EROFS;
|
||||
}
|
||||
|
||||
std::string
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue