diff --git a/libfuse/include/fuse.h b/libfuse/include/fuse.h index 2d615223..7bbcbcbd 100644 --- a/libfuse/include/fuse.h +++ b/libfuse/include/fuse.h @@ -667,6 +667,8 @@ int fuse_loop_mt(struct fuse *f); */ 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 * @@ -779,6 +781,11 @@ void fuse_gc1(); void fuse_gc(); 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 #endif /* _FUSE_H_ */ diff --git a/libfuse/include/fuse_common.h b/libfuse/include/fuse_common.h index 48607d05..877c5971 100644 --- a/libfuse/include/fuse_common.h +++ b/libfuse/include/fuse_common.h @@ -88,26 +88,18 @@ struct fuse_file_info_t uint32_t noflush:1; + uint32_t passthrough:1; + /** File handle. May be filled in by filesystem in open(). Available in all other file operations */ uint64_t fh; + uint32_t backing_id; + /** Lock owner id. Available in locking operations and flush */ 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_POSIX_LOCKS (1ULL << 1) #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_DIRECT_IO_ALLOW_MMAP (1ULL << 23) #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) /** diff --git a/libfuse/include/fuse_kernel.h b/libfuse/include/fuse_kernel.h index e7418d15..d08b99d6 100644 --- a/libfuse/include/fuse_kernel.h +++ b/libfuse/include/fuse_kernel.h @@ -211,6 +211,12 @@ * 7.39 * - add FUSE_DIRECT_IO_ALLOW_MMAP * - 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 @@ -246,7 +252,7 @@ #define FUSE_KERNEL_VERSION 7 /** 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 */ #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_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_PASSTHROUGH: passthrough read/write io for this open file */ #define FOPEN_DIRECT_IO (1 << 0) #define FOPEN_KEEP_CACHE (1 << 1) @@ -361,6 +368,7 @@ struct fuse_file_lock { #define FOPEN_STREAM (1 << 4) #define FOPEN_NOFLUSH (1 << 5) #define FOPEN_PARALLEL_DIRECT_WRITES (1 << 6) +#define FOPEN_PASSTHROUGH (1 << 7) /** * INIT request/reply flags @@ -410,6 +418,9 @@ struct fuse_file_lock { * symlink and mknod (single group that matches parent) * FUSE_HAS_EXPIRE_ONLY: kernel supports expiry-only entry invalidation * 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_POSIX_LOCKS (1 << 1) @@ -449,6 +460,9 @@ struct fuse_file_lock { #define FUSE_CREATE_SUPP_GROUP (1ULL << 34) #define FUSE_HAS_EXPIRE_ONLY (1ULL << 35) #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 */ #define FUSE_DIRECT_IO_RELAX FUSE_DIRECT_IO_ALLOW_MMAP @@ -635,6 +649,7 @@ enum fuse_notify_code { FUSE_NOTIFY_STORE = 4, FUSE_NOTIFY_RETRIEVE = 5, FUSE_NOTIFY_DELETE = 6, + FUSE_NOTIFY_RESEND = 7, FUSE_NOTIFY_CODE_MAX, }; @@ -761,7 +776,7 @@ struct fuse_create_in { struct fuse_open_out { uint64_t fh; uint32_t open_flags; - uint32_t padding; + int32_t backing_id; }; struct fuse_release_in { @@ -877,7 +892,8 @@ struct fuse_init_out { uint16_t max_pages; uint16_t map_alignment; uint32_t flags2; - uint32_t unused[7]; + uint32_t max_stack_depth; + uint32_t unused[6]; }; #define CUSE_INIT_INFO_MAX 4096 @@ -960,6 +976,14 @@ struct fuse_fallocate_in { 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 { uint32_t len; uint32_t opcode; @@ -1049,9 +1073,18 @@ struct fuse_notify_retrieve_in { uint64_t dummy4; }; +struct fuse_backing_map { + int32_t fd; + uint32_t flags; + uint64_t padding; +}; + /* Device ioctls: */ #define FUSE_DEV_IOC_MAGIC 229 #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 { uint64_t fh; diff --git a/libfuse/lib/debug.c b/libfuse/lib/debug.c index ddb53155..2fb0a951 100644 --- a/libfuse/lib/debug.c +++ b/libfuse/lib/debug.c @@ -199,6 +199,7 @@ fuse_flag_to_str(const uint64_t offset_) FUSE_INIT_FLAG_CASE(CREATE_SUPP_GROUP); FUSE_INIT_FLAG_CASE(HAS_EXPIRE_ONLY); FUSE_INIT_FLAG_CASE(DIRECT_IO_ALLOW_MMAP); + FUSE_INIT_FLAG_CASE(PASSTHROUGH); } return NULL; diff --git a/libfuse/lib/fuse.c b/libfuse/lib/fuse.c index 3cb3b5ad..8eb41788 100644 --- a/libfuse/lib/fuse.c +++ b/libfuse/lib/fuse.c @@ -47,6 +47,7 @@ #include #include #include +#include #ifdef HAVE_MALLOC_TRIM #include @@ -3682,6 +3683,12 @@ fuse_get_context(void) 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 { KEY_HELP, }; @@ -4156,3 +4163,30 @@ fuse_log_metrics_get(void) { 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_); +} diff --git a/libfuse/lib/fuse_lowlevel.c b/libfuse/lib/fuse_lowlevel.c index 9dca4c93..d80b0009 100644 --- a/libfuse/lib/fuse_lowlevel.c +++ b/libfuse/lib/fuse_lowlevel.c @@ -277,6 +277,11 @@ fill_open(struct fuse_open_out *arg_, arg_->open_flags |= FOPEN_PARALLEL_DIRECT_WRITES; if(ffi_->noflush) arg_->open_flags |= FOPEN_NOFLUSH; + if(ffi_->passthrough) + { + arg_->open_flags |= FOPEN_PASSTHROUGH; + arg_->backing_id = ffi_->backing_id; + } } int @@ -1175,6 +1180,12 @@ do_init(fuse_req_t req, f->conn.capable |= FUSE_CAP_DIRECT_IO_ALLOW_MMAP; if(inargflags & FUSE_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 { @@ -1248,6 +1259,15 @@ do_init(fuse_req_t req, outargflags |= FUSE_CREATE_SUPP_GROUP; if(f->conn.want & FUSE_CAP_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) { diff --git a/src/config.cpp b/src/config.cpp index 255c9d79..ce6dd492 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -62,6 +62,8 @@ namespace l IFERT("export-support"); IFERT("fsname"); IFERT("fuse_msg_size"); + IFERT("handle-killpriv"); + IFERT("handle-killpriv-v2"); IFERT("mount"); IFERT("nullrw"); IFERT("pid"); @@ -103,6 +105,8 @@ Config::Config() fsname(), func(), fuse_msg_size(FUSE_MAX_MAX_PAGES), + handle_killpriv(true), + handle_killpriv_v2(true), ignorepponrename(false), inodecalc("hybrid-hash"), lazy_umount_mountpoint(false), @@ -114,6 +118,7 @@ Config::Config() nfsopenhack(NFSOpenHack::ENUM::OFF), nullrw(false), parallel_direct_writes(false), + passthrough(false), posix_acl(false), readahead(0), readdir("seq"), @@ -180,6 +185,8 @@ Config::Config() _map["func.unlink"] = &func.unlink; _map["func.utimens"] = &func.utimens; _map["fuse_msg_size"] = &fuse_msg_size; + _map["handle-killpriv"] = &handle_killpriv; + _map["handle-killpriv-v2"] = &handle_killpriv_v2; _map["ignorepponrename"] = &ignorepponrename; _map["inodecalc"] = &inodecalc; _map["kernel_cache"] = &kernel_cache; @@ -194,6 +201,7 @@ Config::Config() _map["nullrw"] = &nullrw; _map["pid"] = &pid; _map["parallel-direct-writes"] = ¶llel_direct_writes; + _map["passthrough"] = &passthrough; _map["pin-threads"] = &fuse_pin_threads; _map["posix_acl"] = &posix_acl; _map["readahead"] = &readahead; diff --git a/src/config.hpp b/src/config.hpp index c56a303c..be242ff3 100644 --- a/src/config.hpp +++ b/src/config.hpp @@ -125,6 +125,8 @@ public: ConfigSTR fsname; Funcs func; ConfigUINT64 fuse_msg_size; + ConfigBOOL handle_killpriv; + ConfigBOOL handle_killpriv_v2; ConfigBOOL ignorepponrename; InodeCalc inodecalc; ConfigBOOL kernel_cache; @@ -137,6 +139,7 @@ public: NFSOpenHack nfsopenhack; ConfigBOOL nullrw; ConfigBOOL parallel_direct_writes; + ConfigBOOL passthrough; ConfigGetPid pid; ConfigBOOL posix_acl; ConfigUINT64 readahead; diff --git a/src/fileinfo.hpp b/src/fileinfo.hpp index 4c8beaba..6230a067 100644 --- a/src/fileinfo.hpp +++ b/src/fileinfo.hpp @@ -31,12 +31,14 @@ public: bool const direct_io_) : FH(fusepath_), fd(fd_), + backing_id(0), direct_io(direct_io_) { } public: int fd; + int backing_id; uint32_t direct_io:1; std::mutex mutex; }; diff --git a/src/fuse_create.cpp b/src/fuse_create.cpp index 45ee5c68..a1bbcb7e 100644 --- a/src/fuse_create.cpp +++ b/src/fuse_create.cpp @@ -170,6 +170,28 @@ namespace l return 0; } + static + int + passthrough(const fuse_context *fc_, + fuse_file_info_t *ffi_) + { + int backing_id; + FileInfo *fi; + + fi = reinterpret_cast(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 int create(const Policy::Search &searchFunc_, @@ -247,6 +269,9 @@ namespace FUSE fc->umask); } - return rv; + if((rv != 0) || (cfg->passthrough == false)) + return rv; + + return l::passthrough(fc,ffi_); } } diff --git a/src/fuse_init.cpp b/src/fuse_init.cpp index cea9513d..3699e28e 100644 --- a/src/fuse_init.cpp +++ b/src/fuse_init.cpp @@ -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_DONT_MASK); 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_PARALLEL_DIROPS); + l::want_if_capable(conn_,FUSE_CAP_PASSTHROUGH,&cfg->passthrough); 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_WRITEBACK_CACHE,&cfg->writeback_cache); diff --git a/src/fuse_open.cpp b/src/fuse_open.cpp index d49ed797..41fff404 100644 --- a/src/fuse_open.cpp +++ b/src/fuse_open.cpp @@ -218,6 +218,28 @@ namespace l return 0; } + static + int + passthrough(const fuse_context *fc_, + fuse_file_info_t *ffi_) + { + int backing_id; + FileInfo *fi; + + fi = reinterpret_cast(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 int open(const Policy::Search &searchFunc_, @@ -264,6 +286,9 @@ namespace FUSE cfg->link_cow, cfg->nfsopenhack); - return rv; + if((rv != 0) || (cfg->passthrough == false)) + return rv; + + return l::passthrough(fc,ffi_); } } diff --git a/src/fuse_release.cpp b/src/fuse_release.cpp index 03b6dcdb..50592a70 100644 --- a/src/fuse_release.cpp +++ b/src/fuse_release.cpp @@ -40,6 +40,15 @@ namespace l fs::fadvise_dontneed(fi_->fd); } + if(fi_->backing_id) + { + const fuse_context *fc; + + fc = fuse_get_context(); + + fuse_passthrough_close(fc,fi_->backing_id); + } + fs::close(fi_->fd); delete fi_;