Add support for file io passthrough

If using Linux 6.9 or above and enabled (passthrough=true) files
opened or created will use the FUSE passthrough feature.

If direct-io=true / cache.files=off it will override passthrough. If
direct-io-allow-mmap is enabled only mmap will passthrough.

HANDLE_KILLPRIV and V2 are enabled now by default to remove the
kernel's need to issue getattr and getxattr requests.

moveonenospc will not work when leveraging passthrough.
This commit is contained in:
Antonio SJ Musumeci 2024-03-24 23:32:26 -05:00
parent b21172c609
commit 07e7d76b7c
18 changed files with 1673 additions and 20 deletions

View File

@ -167,7 +167,10 @@ These options are the same regardless of whether you use them with the
longer need the data and it can drop its cache. Recommended when longer need the data and it can drop its cache. Recommended when
**cache.files=partial|full|auto-full|per-process** to limit double **cache.files=partial|full|auto-full|per-process** to limit double
caching. (default: false) caching. (default: false)
* **direct-io-allow-mmap=BOOL**: On newer kernels (>= 6.6) it is * **passthrough=off|ro|wo|rw**: On Linux 6.9 and above passthrough allows for
near native IO performance. See below for more details. (default:
off)
* **direct-io-allow-mmap=BOOL**: On Linux 6.6 and above it is
possible to disable file page caching while still allowing for possible to disable file page caching while still allowing for
shared mmap support. mergerfs will enable this feature if available shared mmap support. mergerfs will enable this feature if available
but an option is provided to turn it off for testing and debugging but an option is provided to turn it off for testing and debugging
@ -411,6 +414,32 @@ fact it will not be automatically included.
**fuse** package. **fuse** package.
### passthrough
With Linux 6.9 and above there is a feature in FUSE called
"passthrough" which can improve performance by allowing a union or
overlay filesystem (like mergerfs) to pass the file descriptor of a
file opened on an underlying filesystem to the kernel to work on
directly rather than calls always having to go to the FUSE server
(mergerfs). As of Linux 6.9 the functions that can be passthroughed
are regular file read/write IO and mmap. Other functions may be added
in the future.
Passthrough requires `cache.files` to be enabled. `cache.files=off`
(which enables FUSE direct-io feature) will override `passthrough` and
read/write requests will still be sent to mergerfs.
If `cache.files=off` and `direct-io-allow-mmap=true` (the default)
then regular read/write IO will be handled by mergerfs but mmap will
be passthroughed.
NOTE: `moveonenospc` will not work when using `passthrough` as it
removes mergerfs entirely from the read/write process. If an ENOSPC
error occurs it will be returned to the caller app immediately. Since
mergerfs won't be handling the IO it therefore won't be able to handle
any error returned by said IO.
### inodecalc ### inodecalc
Inodes (st_ino) are unique identifiers within a filesystem. Each Inodes (st_ino) are unique identifiers within a filesystem. Each
@ -1664,6 +1693,8 @@ over the suggestions below (including the benchmarking section.)
NOTE: be sure to read about these features before changing them to NOTE: be sure to read about these features before changing them to
understand what behaviors it may impact understand what behaviors it may impact
* test theoretical performance using `nullrw` or mounting a ram disk
* enable `passthrough`
* disable `security_capability` and/or `xattr` * disable `security_capability` and/or `xattr`
* increase cache timeouts `cache.attr`, `cache.entry`, `cache.negative_entry` * increase cache timeouts `cache.attr`, `cache.entry`, `cache.negative_entry`
* enable (or disable) page caching (`cache.files`) * enable (or disable) page caching (`cache.files`)
@ -1675,7 +1706,6 @@ understand what behaviors it may impact
* change the number of worker threads * change the number of worker threads
* disable `posix_acl` * disable `posix_acl`
* disable `async_read` * disable `async_read`
* test theoretical performance using `nullrw` or mounting a ram disk
* use `symlinkify` if your data is largely static and read-only * use `symlinkify` if your data is largely static and read-only
* use tiered cache devices * use tiered cache devices
* use LVM and LVM cache to place a SSD in front of your HDDs * use LVM and LVM cache to place a SSD in front of your HDDs
@ -1879,7 +1909,7 @@ more details.
#### rtorrent fails with ENODEV (No such device) #### rtorrent fails with ENODEV (No such device)
Be sure to set Be sure to set
`cache.files=partial|full|auto-full|per-processe`. rtorrent and some `cache.files=partial|full|auto-full|per-process`. rtorrent and some
other applications use [mmap](http://linux.die.net/man/2/mmap) to read other applications use [mmap](http://linux.die.net/man/2/mmap) to read
and write to files and offer no fallback to traditional methods. FUSE and write to files and offer no fallback to traditional methods. FUSE
does not currently support mmap while using `direct_io`. There may be does not currently support mmap while using `direct_io`. There may be

View File

@ -667,6 +667,8 @@ int fuse_loop_mt(struct fuse *f);
*/ */
struct fuse_context *fuse_get_context(void); struct fuse_context *fuse_get_context(void);
int fuse_get_dev_fuse_fd(const struct fuse_context *);
/** /**
* Check if the current request has already been interrupted * Check if the current request has already been interrupted
* *
@ -779,6 +781,11 @@ void fuse_gc1();
void fuse_gc(); void fuse_gc();
void fuse_invalidate_all_nodes(); void fuse_invalidate_all_nodes();
int fuse_passthrough_open(const struct fuse_context *fc,
const int fd);
int fuse_passthrough_close(const struct fuse_context *fc,
const int backing_id);
EXTERN_C_END EXTERN_C_END
#endif /* _FUSE_H_ */ #endif /* _FUSE_H_ */

View File

@ -88,26 +88,18 @@ struct fuse_file_info_t
uint32_t noflush:1; uint32_t noflush:1;
uint32_t passthrough:1;
/** File handle. May be filled in by filesystem in open(). /** File handle. May be filled in by filesystem in open().
Available in all other file operations */ Available in all other file operations */
uint64_t fh; uint64_t fh;
uint32_t backing_id;
/** Lock owner id. Available in locking operations and flush */ /** Lock owner id. Available in locking operations and flush */
uint64_t lock_owner; uint64_t lock_owner;
}; };
/**
* Capability bits for 'fuse_conn_info.capable' and 'fuse_conn_info.want'
*
* FUSE_CAP_ASYNC_READ: filesystem supports asynchronous read requests
* FUSE_CAP_POSIX_LOCKS: filesystem supports "remote" locking
* FUSE_CAP_ATOMIC_O_TRUNC: filesystem handles the O_TRUNC open flag
* FUSE_CAP_EXPORT_SUPPORT: filesystem handles lookups of "." and ".."
* FUSE_CAP_BIG_WRITES: filesystem can handle write size larger than 4kB
* FUSE_CAP_DONT_MASK: don't apply umask to file mode on create operations
* FUSE_CAP_IOCTL_DIR: ioctl support on directories
* FUSE_CAP_CACHE_SYMLINKS: cache READLINK responses
*/
#define FUSE_CAP_ASYNC_READ (1ULL << 0) #define FUSE_CAP_ASYNC_READ (1ULL << 0)
#define FUSE_CAP_POSIX_LOCKS (1ULL << 1) #define FUSE_CAP_POSIX_LOCKS (1ULL << 1)
#define FUSE_CAP_ATOMIC_O_TRUNC (1ULL << 3) #define FUSE_CAP_ATOMIC_O_TRUNC (1ULL << 3)
@ -127,6 +119,9 @@ struct fuse_file_info_t
#define FUSE_CAP_SETXATTR_EXT (1ULL << 22) #define FUSE_CAP_SETXATTR_EXT (1ULL << 22)
#define FUSE_CAP_DIRECT_IO_ALLOW_MMAP (1ULL << 23) #define FUSE_CAP_DIRECT_IO_ALLOW_MMAP (1ULL << 23)
#define FUSE_CAP_CREATE_SUPP_GROUP (1ULL << 24) #define FUSE_CAP_CREATE_SUPP_GROUP (1ULL << 24)
#define FUSE_CAP_PASSTHROUGH (1ULL << 25)
#define FUSE_CAP_HANDLE_KILLPRIV (1ULL << 26)
#define FUSE_CAP_HANDLE_KILLPRIV_V2 (1ULL << 27)
/** /**

View File

@ -211,6 +211,12 @@
* 7.39 * 7.39
* - add FUSE_DIRECT_IO_ALLOW_MMAP * - add FUSE_DIRECT_IO_ALLOW_MMAP
* - add FUSE_STATX and related structures * - add FUSE_STATX and related structures
*
* 7.40
* - add max_stack_depth to fuse_init_out, add FUSE_PASSTHROUGH init flag
* - add backing_id to fuse_open_out, add FOPEN_PASSTHROUGH open flag
* - add FUSE_NO_EXPORT_SUPPORT init flag
* - add FUSE_NOTIFY_RESEND, add FUSE_HAS_RESEND init flag
*/ */
#ifndef _LINUX_FUSE_H #ifndef _LINUX_FUSE_H
@ -246,7 +252,7 @@
#define FUSE_KERNEL_VERSION 7 #define FUSE_KERNEL_VERSION 7
/** Minor version number of this interface */ /** Minor version number of this interface */
#define FUSE_KERNEL_MINOR_VERSION 39 #define FUSE_KERNEL_MINOR_VERSION 40
/** The node ID of the root inode */ /** The node ID of the root inode */
#define FUSE_ROOT_ID 1 #define FUSE_ROOT_ID 1
@ -353,6 +359,7 @@ struct fuse_file_lock {
* FOPEN_STREAM: the file is stream-like (no file position at all) * FOPEN_STREAM: the file is stream-like (no file position at all)
* FOPEN_NOFLUSH: don't flush data cache on close (unless FUSE_WRITEBACK_CACHE) * FOPEN_NOFLUSH: don't flush data cache on close (unless FUSE_WRITEBACK_CACHE)
* FOPEN_PARALLEL_DIRECT_WRITES: Allow concurrent direct writes on the same inode * FOPEN_PARALLEL_DIRECT_WRITES: Allow concurrent direct writes on the same inode
* FOPEN_PASSTHROUGH: passthrough read/write io for this open file
*/ */
#define FOPEN_DIRECT_IO (1 << 0) #define FOPEN_DIRECT_IO (1 << 0)
#define FOPEN_KEEP_CACHE (1 << 1) #define FOPEN_KEEP_CACHE (1 << 1)
@ -361,6 +368,7 @@ struct fuse_file_lock {
#define FOPEN_STREAM (1 << 4) #define FOPEN_STREAM (1 << 4)
#define FOPEN_NOFLUSH (1 << 5) #define FOPEN_NOFLUSH (1 << 5)
#define FOPEN_PARALLEL_DIRECT_WRITES (1 << 6) #define FOPEN_PARALLEL_DIRECT_WRITES (1 << 6)
#define FOPEN_PASSTHROUGH (1 << 7)
/** /**
* INIT request/reply flags * INIT request/reply flags
@ -410,6 +418,9 @@ struct fuse_file_lock {
* symlink and mknod (single group that matches parent) * symlink and mknod (single group that matches parent)
* FUSE_HAS_EXPIRE_ONLY: kernel supports expiry-only entry invalidation * FUSE_HAS_EXPIRE_ONLY: kernel supports expiry-only entry invalidation
* FUSE_DIRECT_IO_ALLOW_MMAP: allow shared mmap in FOPEN_DIRECT_IO mode. * FUSE_DIRECT_IO_ALLOW_MMAP: allow shared mmap in FOPEN_DIRECT_IO mode.
* FUSE_NO_EXPORT_SUPPORT: explicitly disable export support
* FUSE_HAS_RESEND: kernel supports resending pending requests, and the high bit
* of the request ID indicates resend requests
*/ */
#define FUSE_ASYNC_READ (1 << 0) #define FUSE_ASYNC_READ (1 << 0)
#define FUSE_POSIX_LOCKS (1 << 1) #define FUSE_POSIX_LOCKS (1 << 1)
@ -449,6 +460,9 @@ struct fuse_file_lock {
#define FUSE_CREATE_SUPP_GROUP (1ULL << 34) #define FUSE_CREATE_SUPP_GROUP (1ULL << 34)
#define FUSE_HAS_EXPIRE_ONLY (1ULL << 35) #define FUSE_HAS_EXPIRE_ONLY (1ULL << 35)
#define FUSE_DIRECT_IO_ALLOW_MMAP (1ULL << 36) #define FUSE_DIRECT_IO_ALLOW_MMAP (1ULL << 36)
#define FUSE_PASSTHROUGH (1ULL << 37)
#define FUSE_NO_EXPORT_SUPPORT (1ULL << 38)
#define FUSE_HAS_RESEND (1ULL << 39)
/* Obsolete alias for FUSE_DIRECT_IO_ALLOW_MMAP */ /* Obsolete alias for FUSE_DIRECT_IO_ALLOW_MMAP */
#define FUSE_DIRECT_IO_RELAX FUSE_DIRECT_IO_ALLOW_MMAP #define FUSE_DIRECT_IO_RELAX FUSE_DIRECT_IO_ALLOW_MMAP
@ -635,6 +649,7 @@ enum fuse_notify_code {
FUSE_NOTIFY_STORE = 4, FUSE_NOTIFY_STORE = 4,
FUSE_NOTIFY_RETRIEVE = 5, FUSE_NOTIFY_RETRIEVE = 5,
FUSE_NOTIFY_DELETE = 6, FUSE_NOTIFY_DELETE = 6,
FUSE_NOTIFY_RESEND = 7,
FUSE_NOTIFY_CODE_MAX, FUSE_NOTIFY_CODE_MAX,
}; };
@ -761,7 +776,7 @@ struct fuse_create_in {
struct fuse_open_out { struct fuse_open_out {
uint64_t fh; uint64_t fh;
uint32_t open_flags; uint32_t open_flags;
uint32_t padding; int32_t backing_id;
}; };
struct fuse_release_in { struct fuse_release_in {
@ -877,7 +892,8 @@ struct fuse_init_out {
uint16_t max_pages; uint16_t max_pages;
uint16_t map_alignment; uint16_t map_alignment;
uint32_t flags2; uint32_t flags2;
uint32_t unused[7]; uint32_t max_stack_depth;
uint32_t unused[6];
}; };
#define CUSE_INIT_INFO_MAX 4096 #define CUSE_INIT_INFO_MAX 4096
@ -960,6 +976,14 @@ struct fuse_fallocate_in {
uint32_t padding; uint32_t padding;
}; };
/**
* FUSE request unique ID flag
*
* Indicates whether this is a resend request. The receiver should handle this
* request accordingly.
*/
#define FUSE_UNIQUE_RESEND (1ULL << 63)
struct fuse_in_header { struct fuse_in_header {
uint32_t len; uint32_t len;
uint32_t opcode; uint32_t opcode;
@ -1049,9 +1073,18 @@ struct fuse_notify_retrieve_in {
uint64_t dummy4; uint64_t dummy4;
}; };
struct fuse_backing_map {
int32_t fd;
uint32_t flags;
uint64_t padding;
};
/* Device ioctls: */ /* Device ioctls: */
#define FUSE_DEV_IOC_MAGIC 229 #define FUSE_DEV_IOC_MAGIC 229
#define FUSE_DEV_IOC_CLONE _IOR(FUSE_DEV_IOC_MAGIC, 0, uint32_t) #define FUSE_DEV_IOC_CLONE _IOR(FUSE_DEV_IOC_MAGIC, 0, uint32_t)
#define FUSE_DEV_IOC_BACKING_OPEN _IOW(FUSE_DEV_IOC_MAGIC, 1, \
struct fuse_backing_map)
#define FUSE_DEV_IOC_BACKING_CLOSE _IOW(FUSE_DEV_IOC_MAGIC, 2, uint32_t)
struct fuse_lseek_in { struct fuse_lseek_in {
uint64_t fh; uint64_t fh;

View File

@ -199,6 +199,7 @@ fuse_flag_to_str(const uint64_t offset_)
FUSE_INIT_FLAG_CASE(CREATE_SUPP_GROUP); FUSE_INIT_FLAG_CASE(CREATE_SUPP_GROUP);
FUSE_INIT_FLAG_CASE(HAS_EXPIRE_ONLY); FUSE_INIT_FLAG_CASE(HAS_EXPIRE_ONLY);
FUSE_INIT_FLAG_CASE(DIRECT_IO_ALLOW_MMAP); FUSE_INIT_FLAG_CASE(DIRECT_IO_ALLOW_MMAP);
FUSE_INIT_FLAG_CASE(PASSTHROUGH);
} }
return NULL; return NULL;
@ -804,13 +805,15 @@ debug_fuse_init_out(const uint64_t unique_,
" time_gran=%u;" " time_gran=%u;"
" max_pages=%u;" " max_pages=%u;"
" map_alignment=%u;" " map_alignment=%u;"
" max_stack_depth=%u;"
"\n", "\n",
arg->max_background, arg->max_background,
arg->congestion_threshold, arg->congestion_threshold,
arg->max_write, arg->max_write,
arg->time_gran, arg->time_gran,
arg->max_pages, arg->max_pages,
arg->map_alignment); arg->map_alignment,
arg->max_stack_depth);
} }
static static

View File

@ -47,6 +47,7 @@
#include <syslog.h> #include <syslog.h>
#include <time.h> #include <time.h>
#include <unistd.h> #include <unistd.h>
#include <sys/ioctl.h>
#ifdef HAVE_MALLOC_TRIM #ifdef HAVE_MALLOC_TRIM
#include <malloc.h> #include <malloc.h>
@ -3682,6 +3683,12 @@ fuse_get_context(void)
return &fuse_get_context_internal()->ctx; return &fuse_get_context_internal()->ctx;
} }
int
fuse_get_dev_fuse_fd(const struct fuse_context *fc_)
{
return fuse_chan_fd(fc_->fuse->se->ch);
}
enum { enum {
KEY_HELP, KEY_HELP,
}; };
@ -4156,3 +4163,30 @@ fuse_log_metrics_get(void)
{ {
return g_LOG_METRICS; return g_LOG_METRICS;
} }
int
fuse_passthrough_open(const struct fuse_context *fc_,
const int fd_)
{
int rv;
int dev_fuse_fd;
struct fuse_backing_map bm = {0};
dev_fuse_fd = fuse_get_dev_fuse_fd(fc_);
bm.fd = fd_;
rv = ioctl(dev_fuse_fd,FUSE_DEV_IOC_BACKING_OPEN,&bm);
return rv;
}
int
fuse_passthrough_close(const struct fuse_context *fc_,
const int backing_id_)
{
int dev_fuse_fd;
dev_fuse_fd = fuse_get_dev_fuse_fd(fc_);
return ioctl(dev_fuse_fd, FUSE_DEV_IOC_BACKING_CLOSE, &backing_id_);
}

View File

@ -277,6 +277,11 @@ fill_open(struct fuse_open_out *arg_,
arg_->open_flags |= FOPEN_PARALLEL_DIRECT_WRITES; arg_->open_flags |= FOPEN_PARALLEL_DIRECT_WRITES;
if(ffi_->noflush) if(ffi_->noflush)
arg_->open_flags |= FOPEN_NOFLUSH; arg_->open_flags |= FOPEN_NOFLUSH;
if(ffi_->passthrough)
{
arg_->open_flags |= FOPEN_PASSTHROUGH;
arg_->backing_id = ffi_->backing_id;
}
} }
int int
@ -1175,6 +1180,12 @@ do_init(fuse_req_t req,
f->conn.capable |= FUSE_CAP_DIRECT_IO_ALLOW_MMAP; f->conn.capable |= FUSE_CAP_DIRECT_IO_ALLOW_MMAP;
if(inargflags & FUSE_CREATE_SUPP_GROUP) if(inargflags & FUSE_CREATE_SUPP_GROUP)
f->conn.capable |= FUSE_CAP_CREATE_SUPP_GROUP; f->conn.capable |= FUSE_CAP_CREATE_SUPP_GROUP;
if(inargflags & FUSE_PASSTHROUGH)
f->conn.capable |= FUSE_CAP_PASSTHROUGH;
if(inargflags & FUSE_HANDLE_KILLPRIV)
f->conn.capable |= FUSE_CAP_HANDLE_KILLPRIV;
if(inargflags & FUSE_HANDLE_KILLPRIV_V2)
f->conn.capable |= FUSE_CAP_HANDLE_KILLPRIV_V2;
} }
else else
{ {
@ -1248,6 +1259,15 @@ do_init(fuse_req_t req,
outargflags |= FUSE_CREATE_SUPP_GROUP; outargflags |= FUSE_CREATE_SUPP_GROUP;
if(f->conn.want & FUSE_CAP_DIRECT_IO_ALLOW_MMAP) if(f->conn.want & FUSE_CAP_DIRECT_IO_ALLOW_MMAP)
outargflags |= FUSE_DIRECT_IO_ALLOW_MMAP; outargflags |= FUSE_DIRECT_IO_ALLOW_MMAP;
if(f->conn.want & FUSE_CAP_HANDLE_KILLPRIV)
outargflags |= FUSE_HANDLE_KILLPRIV;
if(f->conn.want & FUSE_CAP_HANDLE_KILLPRIV_V2)
outargflags |= FUSE_HANDLE_KILLPRIV_V2;
if(f->conn.want & FUSE_CAP_PASSTHROUGH)
{
outargflags |= FUSE_PASSTHROUGH;
outarg.max_stack_depth = 2;
}
if(inargflags & FUSE_INIT_EXT) if(inargflags & FUSE_INIT_EXT)
{ {

1328
src/better_enum.hpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -62,6 +62,8 @@ namespace l
IFERT("export-support"); IFERT("export-support");
IFERT("fsname"); IFERT("fsname");
IFERT("fuse_msg_size"); IFERT("fuse_msg_size");
IFERT("handle-killpriv");
IFERT("handle-killpriv-v2");
IFERT("mount"); IFERT("mount");
IFERT("nullrw"); IFERT("nullrw");
IFERT("pid"); IFERT("pid");
@ -103,6 +105,8 @@ Config::Config()
fsname(), fsname(),
func(), func(),
fuse_msg_size(FUSE_MAX_MAX_PAGES), fuse_msg_size(FUSE_MAX_MAX_PAGES),
handle_killpriv(true),
handle_killpriv_v2(true),
ignorepponrename(false), ignorepponrename(false),
inodecalc("hybrid-hash"), inodecalc("hybrid-hash"),
lazy_umount_mountpoint(false), lazy_umount_mountpoint(false),
@ -114,6 +118,7 @@ Config::Config()
nfsopenhack(NFSOpenHack::ENUM::OFF), nfsopenhack(NFSOpenHack::ENUM::OFF),
nullrw(false), nullrw(false),
parallel_direct_writes(false), parallel_direct_writes(false),
passthrough(PassthroughEnum::off),
posix_acl(false), posix_acl(false),
readahead(0), readahead(0),
readdir("seq"), readdir("seq"),
@ -180,6 +185,8 @@ Config::Config()
_map["func.unlink"] = &func.unlink; _map["func.unlink"] = &func.unlink;
_map["func.utimens"] = &func.utimens; _map["func.utimens"] = &func.utimens;
_map["fuse_msg_size"] = &fuse_msg_size; _map["fuse_msg_size"] = &fuse_msg_size;
_map["handle-killpriv"] = &handle_killpriv;
_map["handle-killpriv-v2"] = &handle_killpriv_v2;
_map["ignorepponrename"] = &ignorepponrename; _map["ignorepponrename"] = &ignorepponrename;
_map["inodecalc"] = &inodecalc; _map["inodecalc"] = &inodecalc;
_map["kernel_cache"] = &kernel_cache; _map["kernel_cache"] = &kernel_cache;
@ -194,6 +201,7 @@ Config::Config()
_map["nullrw"] = &nullrw; _map["nullrw"] = &nullrw;
_map["pid"] = &pid; _map["pid"] = &pid;
_map["parallel-direct-writes"] = &parallel_direct_writes; _map["parallel-direct-writes"] = &parallel_direct_writes;
_map["passthrough"] = &passthrough;
_map["pin-threads"] = &fuse_pin_threads; _map["pin-threads"] = &fuse_pin_threads;
_map["posix_acl"] = &posix_acl; _map["posix_acl"] = &posix_acl;
_map["readahead"] = &readahead; _map["readahead"] = &readahead;

View File

@ -21,12 +21,13 @@
#include "config_cachefiles.hpp" #include "config_cachefiles.hpp"
#include "config_flushonclose.hpp" #include "config_flushonclose.hpp"
#include "config_follow_symlinks.hpp" #include "config_follow_symlinks.hpp"
#include "config_pid.hpp"
#include "config_inodecalc.hpp" #include "config_inodecalc.hpp"
#include "config_link_exdev.hpp" #include "config_link_exdev.hpp"
#include "config_log_metrics.hpp" #include "config_log_metrics.hpp"
#include "config_moveonenospc.hpp" #include "config_moveonenospc.hpp"
#include "config_nfsopenhack.hpp" #include "config_nfsopenhack.hpp"
#include "config_passthrough.hpp"
#include "config_pid.hpp"
#include "config_rename_exdev.hpp" #include "config_rename_exdev.hpp"
#include "config_set.hpp" #include "config_set.hpp"
#include "config_statfs.hpp" #include "config_statfs.hpp"
@ -125,6 +126,8 @@ public:
ConfigSTR fsname; ConfigSTR fsname;
Funcs func; Funcs func;
ConfigUINT64 fuse_msg_size; ConfigUINT64 fuse_msg_size;
ConfigBOOL handle_killpriv;
ConfigBOOL handle_killpriv_v2;
ConfigBOOL ignorepponrename; ConfigBOOL ignorepponrename;
InodeCalc inodecalc; InodeCalc inodecalc;
ConfigBOOL kernel_cache; ConfigBOOL kernel_cache;
@ -137,6 +140,7 @@ public:
NFSOpenHack nfsopenhack; NFSOpenHack nfsopenhack;
ConfigBOOL nullrw; ConfigBOOL nullrw;
ConfigBOOL parallel_direct_writes; ConfigBOOL parallel_direct_writes;
Passthrough passthrough;
ConfigGetPid pid; ConfigGetPid pid;
ConfigBOOL posix_acl; ConfigBOOL posix_acl;
ConfigUINT64 readahead; ConfigUINT64 readahead;

View File

@ -0,0 +1,43 @@
#/*
ISC License
Copyright (c) 2024, 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 "config_passthrough.hpp"
#include "ef.hpp"
#include "errno.hpp"
template<>
std::string
Passthrough::to_string() const
{
return _data._to_string();
}
template<>
int
Passthrough::from_string(const std::string &s_)
{
better_enums::optional<PassthroughEnum> e;
e = PassthroughEnum::_from_string_nothrow(s_.c_str());
if(!e)
return -EINVAL;
_data = *e;
return 0;
}

View File

@ -0,0 +1,28 @@
/*
ISC License
Copyright (c) 2024, 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 "better_enum.hpp"
#include "enum.hpp"
BETTER_ENUM(PassthroughEnum, uint8_t, off, r, w, ro, wo, rw);
typedef Enum<PassthroughEnum> Passthrough;

View File

@ -60,6 +60,12 @@ public:
return _data; return _data;
} }
ENUM
operator+() const
{
return _data;
}
public: public:
bool operator==(const ENUM data_) const bool operator==(const ENUM data_) const
{ {

View File

@ -31,12 +31,14 @@ public:
bool const direct_io_) bool const direct_io_)
: FH(fusepath_), : FH(fusepath_),
fd(fd_), fd(fd_),
backing_id(0),
direct_io(direct_io_) direct_io(direct_io_)
{ {
} }
public: public:
int fd; int fd;
int backing_id;
uint32_t direct_io:1; uint32_t direct_io:1;
std::mutex mutex; std::mutex mutex;
}; };

View File

@ -170,6 +170,29 @@ namespace l
return 0; return 0;
} }
static
int
passthrough(const fuse_context *fc_,
fuse_file_info_t *ffi_)
{
int backing_id;
FileInfo *fi;
const ugid::SetRootGuard ugid;
fi = reinterpret_cast<FileInfo*>(ffi_->fh);
backing_id = fuse_passthrough_open(fc_,fi->fd);
if(backing_id <= 0)
return 0;
ffi_->passthrough = true;
ffi_->keep_cache = false;
fi->backing_id = backing_id;
ffi_->backing_id = backing_id;
return 0;
}
static static
int int
create(const Policy::Search &searchFunc_, create(const Policy::Search &searchFunc_,
@ -208,6 +231,15 @@ namespace l
} }
} }
constexpr
const
uint64_t
_(const PassthroughEnum e_,
const uint64_t m_)
{
return ((((uint64_t)e_) << 32) | (m_ & O_ACCMODE));
}
namespace FUSE namespace FUSE
{ {
int int
@ -247,6 +279,23 @@ namespace FUSE
fc->umask); fc->umask);
} }
if(rv != 0)
return rv;
switch(_(cfg->passthrough,ffi_->flags))
{
case _(PassthroughEnum::r, O_RDONLY):
case _(PassthroughEnum::r, O_RDWR):
case _(PassthroughEnum::w, O_WRONLY):
case _(PassthroughEnum::w, O_RDWR):
case _(PassthroughEnum::ro,O_RDONLY):
case _(PassthroughEnum::wo,O_WRONLY):
case _(PassthroughEnum::rw,O_RDONLY):
case _(PassthroughEnum::rw,O_WRONLY):
case _(PassthroughEnum::rw,O_RDWR):
return l::passthrough(fc,ffi_);
}
return rv; return rv;
} }
} }

View File

@ -147,8 +147,11 @@ namespace FUSE
l::want_if_capable(conn_,FUSE_CAP_DIRECT_IO_ALLOW_MMAP,&cfg->direct_io_allow_mmap); l::want_if_capable(conn_,FUSE_CAP_DIRECT_IO_ALLOW_MMAP,&cfg->direct_io_allow_mmap);
l::want_if_capable(conn_,FUSE_CAP_DONT_MASK); l::want_if_capable(conn_,FUSE_CAP_DONT_MASK);
l::want_if_capable(conn_,FUSE_CAP_EXPORT_SUPPORT,&cfg->export_support); l::want_if_capable(conn_,FUSE_CAP_EXPORT_SUPPORT,&cfg->export_support);
l::want_if_capable(conn_,FUSE_CAP_HANDLE_KILLPRIV,&cfg->handle_killpriv);
l::want_if_capable(conn_,FUSE_CAP_HANDLE_KILLPRIV_V2,&cfg->handle_killpriv_v2);
l::want_if_capable(conn_,FUSE_CAP_IOCTL_DIR); l::want_if_capable(conn_,FUSE_CAP_IOCTL_DIR);
l::want_if_capable(conn_,FUSE_CAP_PARALLEL_DIROPS); l::want_if_capable(conn_,FUSE_CAP_PARALLEL_DIROPS);
l::want_if_capable(conn_,FUSE_CAP_PASSTHROUGH);
l::want_if_capable(conn_,FUSE_CAP_POSIX_ACL,&cfg->posix_acl); l::want_if_capable(conn_,FUSE_CAP_POSIX_ACL,&cfg->posix_acl);
l::want_if_capable(conn_,FUSE_CAP_READDIR_PLUS,&cfg->readdirplus); l::want_if_capable(conn_,FUSE_CAP_READDIR_PLUS,&cfg->readdirplus);
l::want_if_capable(conn_,FUSE_CAP_WRITEBACK_CACHE,&cfg->writeback_cache); l::want_if_capable(conn_,FUSE_CAP_WRITEBACK_CACHE,&cfg->writeback_cache);

View File

@ -218,6 +218,29 @@ namespace l
return 0; return 0;
} }
static
int
passthrough(const fuse_context *fc_,
fuse_file_info_t *ffi_)
{
int backing_id;
FileInfo *fi;
const ugid::SetRootGuard ugid;
fi = reinterpret_cast<FileInfo*>(ffi_->fh);
backing_id = fuse_passthrough_open(fc_,fi->fd);
if(backing_id <= 0)
return 0;
ffi_->passthrough = true;
ffi_->keep_cache = true;
fi->backing_id = backing_id;
ffi_->backing_id = backing_id;
return 0;
}
static static
int int
open(const Policy::Search &searchFunc_, open(const Policy::Search &searchFunc_,
@ -238,6 +261,15 @@ namespace l
} }
} }
constexpr
const
uint64_t
_(const PassthroughEnum e_,
const uint64_t m_)
{
return ((((uint64_t)e_) << 32) | (m_ & O_ACCMODE));
}
namespace FUSE namespace FUSE
{ {
int int
@ -264,6 +296,23 @@ namespace FUSE
cfg->link_cow, cfg->link_cow,
cfg->nfsopenhack); cfg->nfsopenhack);
if(rv != 0)
return rv;
switch(_(cfg->passthrough,ffi_->flags))
{
case _(PassthroughEnum::r, O_RDONLY):
case _(PassthroughEnum::r, O_RDWR):
case _(PassthroughEnum::w, O_WRONLY):
case _(PassthroughEnum::w, O_RDWR):
case _(PassthroughEnum::ro,O_RDONLY):
case _(PassthroughEnum::wo,O_WRONLY):
case _(PassthroughEnum::rw,O_RDONLY):
case _(PassthroughEnum::rw,O_WRONLY):
case _(PassthroughEnum::rw,O_RDWR):
return l::passthrough(fc,ffi_);
}
return rv; return rv;
} }
} }

View File

@ -19,6 +19,7 @@
#include "fileinfo.hpp" #include "fileinfo.hpp"
#include "fs_close.hpp" #include "fs_close.hpp"
#include "fs_fadvise.hpp" #include "fs_fadvise.hpp"
#include "ugid.hpp"
#include "fuse.h" #include "fuse.h"
@ -40,6 +41,16 @@ namespace l
fs::fadvise_dontneed(fi_->fd); fs::fadvise_dontneed(fi_->fd);
} }
if(fi_->backing_id)
{
const fuse_context *fc;
const ugid::SetRootGuard ugid;
fc = fuse_get_context();
fuse_passthrough_close(fc,fi_->backing_id);
}
fs::close(fi_->fd); fs::close(fi_->fd);
delete fi_; delete fi_;