From 2ac8b6785b29fedca2fd479234c1fa9c35e8893c Mon Sep 17 00:00:00 2001 From: a Date: Mon, 10 Nov 2025 22:43:43 -0600 Subject: [PATCH 1/5] ok --- src/etcfs/etcfs.c | 145 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 139 insertions(+), 6 deletions(-) diff --git a/src/etcfs/etcfs.c b/src/etcfs/etcfs.c index 64c86510..81765c5e 100644 --- a/src/etcfs/etcfs.c +++ b/src/etcfs/etcfs.c @@ -34,6 +34,14 @@ #include #include +/* + * for FUSE_PASSTHROUGH support. + * Requires Linux 6.9+ kernel headers. + * Enabled at runtime via ETCFS_ENABLE_PASSTHROUGH environment variable. + */ +#include +#include + #define ARRAY_LEN(x) (sizeof(x) / sizeof(x[0])) #define MIN(x, y) (x < y ? x : y) @@ -271,6 +279,12 @@ pthread_rwlock_t cfg_lock; */ int debug = 0; +/* + * Tracks whether FUSE_PASSTHROUGH is enabled at runtime. + * Set during initialization based on kernel capability. + */ +int use_fuse_passthrough = 0; + /* * Set the thread's euid, egid, and grouplist to that of the * process calling a given FUSE filesystem call. @@ -1150,10 +1164,57 @@ static int cfg_read(char *buf, size_t size, off_t offset) return rv; } -static void *m_init(struct fuse_conn_info *conn, struct fuse_config *cfg) +/* + * File descriptor for /dev/fuse, used for passthrough ioctls. + * Initialized during mount. + */ +static int fuse_dev_fd = -1; + +/* + * Register a backing file descriptor with the kernel for passthrough mode. + * Returns a backing_id on success, or -1 on failure. + */ +static int register_backing_fd(int fd) { - (void)conn; + if (fuse_dev_fd < 0) { + DEBUG("register_backing_fd failed", "fuse_dev_fd not initialized"); + return -1; + } + struct fuse_backing_map map = { + .fd = fd, + .flags = 0, + .padding = 0, + }; + + int backing_id = ioctl(fuse_dev_fd, FUSE_DEV_IOC_BACKING_OPEN, &map); + if (backing_id < 0) { + DEBUG("register_backing_fd failed", "ioctl failed"); + return -1; + } + DEBUG("register_backing_fd success", ""); + return backing_id; +} + +/* + * Unregister a backing file descriptor from the kernel. + */ +static int unregister_backing_id(int backing_id) +{ + if (fuse_dev_fd < 0 || backing_id < 0) { + return -1; + } + + uint32_t id = (uint32_t)backing_id; + int rv = ioctl(fuse_dev_fd, FUSE_DEV_IOC_BACKING_CLOSE, &id); + if (rv < 0) { + DEBUG("unregister_backing_id failed", ""); + } + + return rv; +} +static void *m_init(struct fuse_conn_info *conn, struct fuse_config *cfg) +{ /* * Do not allow requests to be interrupted. */ @@ -1176,6 +1237,35 @@ static void *m_init(struct fuse_conn_info *conn, struct fuse_config *cfg) cfg->attr_timeout = 0; cfg->negative_timeout = 0; + /* + * Check if FUSE_PASSTHROUGH should be enabled. + * Requires ETCFS_ENABLE_PASSTHROUGH environment variable to be set. + */ + const char *enable_passthrough = getenv("ETCFS_ENABLE_PASSTHROUGH"); + // check if we want to enable passthrough or not + if (enable_passthrough && (strcmp(enable_passthrough, "1") == 0 || strcmp(enable_passthrough, "yes") == 0)) { + // check if the kernel fuse implementation supports passthrough. if we do, begin negotiation + if (conn->capable & FUSE_CAP_PASSTHROUGH) { + // add FUSE_CAP_PASSTHROUGH to the list of wanted capabilities + conn->want |= FUSE_CAP_PASSTHROUGH; + // grab the fuse context + struct fuse_context *context = fuse_get_context(); + if (context && context->fuse) { + struct fuse_session *session = fuse_get_session(context->fuse); + if (session) { + fuse_dev_fd = fuse_session_fd(session); + if (fuse_dev_fd >= 0) { + use_fuse_passthrough = 1; + DEBUG("FUSE_PASSTHROUGH", "succesfully enabled"); + } + } + } + } + if(use_fuse_passthrough == 0) { + DEBUG("FUSE_PASSTHROUGH", "requested but could not be enabled"); + } + } + return NULL; } @@ -1702,7 +1792,21 @@ static int m_create(const char *path, mode_t mode, struct fuse_file_info *fi) rv = -1; } else { rv = 0; - fi->fh = fd; + if (use_fuse_passthrough) { + int backing_id = register_backing_fd(fd); + if (backing_id >= 0) { + fi->backing_id = backing_id; + fi->flags |= FOPEN_PASSTHROUGH; + fi->fh = fd; /* Keep fd for fallback, kernel uses backing_id */ + DEBUG("m_create: using passthrough", path); + } else { + /* Passthrough registration failed, use traditional mode */ + fi->fh = fd; + DEBUG("m_create: passthrough failed, using traditional", path); + } + } else { + fi->fh = fd; + } } FS_IMP_RETURN(rv); @@ -1729,7 +1833,22 @@ static int m_open(const char *path, struct fuse_file_info *fi) fi->fh = -1; } else { rv = 0; - fi->fh = fd; + + if (use_fuse_passthrough) { + int backing_id = register_backing_fd(fd); + if (backing_id >= 0) { + fi->backing_id = backing_id; + fi->flags |= FOPEN_PASSTHROUGH; + fi->fh = fd; /* Keep fd for fallback, kernel uses backing_id */ + DEBUG("m_open: using passthrough", path); + } else { + /* Passthrough registration failed, use traditional mode */ + fi->fh = fd; + DEBUG("m_open: passthrough failed, using traditional", path); + } + } else { + fi->fh = fd; + } } } @@ -1835,7 +1954,12 @@ static int m_flush(const char *path, struct fuse_file_info *fi) DEBUG("m_flush", path); FS_IMP_SETUP_FD(fi->fh, path, 0); - rv = close(dup(fi->fh)); + /* + * In passthrough mode, the kernel handles flushing directly, so we only have to do anything here in the non passthrough mode + */ + if (!(fi->flags & FOPEN_PASSTHROUGH)) { + rv = close(dup(fi->fh)); + } FS_IMP_RETURN(rv); } @@ -1848,7 +1972,16 @@ static int m_release(const char *path, struct fuse_file_info *fi) DEBUG("m_release", path); FS_IMP_SETUP_FD(fi->fh, path, 0); - rv = close(fi->fh); + /* + * In passthrough mode, fi->backing_id contains the backing_id that needs + * to be unregistered, and fi->fh still has the fd that needs closing. + */ + if (fi->flags & FOPEN_PASSTHROUGH) { + unregister_backing_id(fi->backing_id); + rv = close(fi->fh); + } else { + rv = close(fi->fh); + } FS_IMP_RETURN(rv); } From f18775173d868b7df9ddc493c069197a8f61ecc3 Mon Sep 17 00:00:00 2001 From: a Date: Mon, 10 Nov 2025 22:48:17 -0600 Subject: [PATCH 2/5] ok --- src/etcfs/etcfs.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/etcfs/etcfs.c b/src/etcfs/etcfs.c index 81765c5e..dab819c3 100644 --- a/src/etcfs/etcfs.c +++ b/src/etcfs/etcfs.c @@ -1209,7 +1209,6 @@ static int unregister_backing_id(int backing_id) if (rv < 0) { DEBUG("unregister_backing_id failed", ""); } - return rv; } @@ -1237,23 +1236,23 @@ static void *m_init(struct fuse_conn_info *conn, struct fuse_config *cfg) cfg->attr_timeout = 0; cfg->negative_timeout = 0; - /* - * Check if FUSE_PASSTHROUGH should be enabled. - * Requires ETCFS_ENABLE_PASSTHROUGH environment variable to be set. - */ + const char *enable_passthrough = getenv("ETCFS_ENABLE_PASSTHROUGH"); // check if we want to enable passthrough or not if (enable_passthrough && (strcmp(enable_passthrough, "1") == 0 || strcmp(enable_passthrough, "yes") == 0)) { - // check if the kernel fuse implementation supports passthrough. if we do, begin negotiation + // if we do, check if the kernel fuse implementation supports passthrough. if (conn->capable & FUSE_CAP_PASSTHROUGH) { // add FUSE_CAP_PASSTHROUGH to the list of wanted capabilities conn->want |= FUSE_CAP_PASSTHROUGH; - // grab the fuse context + // now we need to use the fuse context to grab a sessino to grab ths ession fd struct fuse_context *context = fuse_get_context(); if (context && context->fuse) { struct fuse_session *session = fuse_get_session(context->fuse); + // open a session if (session) { + // grab the session_fd from the fuse session fuse_dev_fd = fuse_session_fd(session); + // and if its a valid fd, enable passthrough if (fuse_dev_fd >= 0) { use_fuse_passthrough = 1; DEBUG("FUSE_PASSTHROUGH", "succesfully enabled"); From 9e0f6287bff90f0550a4c53377dc818f18391af8 Mon Sep 17 00:00:00 2001 From: a Date: Mon, 10 Nov 2025 22:49:42 -0600 Subject: [PATCH 3/5] a comment --- src/etcfs/etcfs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/etcfs/etcfs.c b/src/etcfs/etcfs.c index dab819c3..ec00aa15 100644 --- a/src/etcfs/etcfs.c +++ b/src/etcfs/etcfs.c @@ -1954,7 +1954,8 @@ static int m_flush(const char *path, struct fuse_file_info *fi) FS_IMP_SETUP_FD(fi->fh, path, 0); /* - * In passthrough mode, the kernel handles flushing directly, so we only have to do anything here in the non passthrough mode + * In passthrough mode, the kernel handles flushing directly + * from what i understand, this would only flush userspace writes, and we would never have any, so we dont need to ever call this in passthrough mode. */ if (!(fi->flags & FOPEN_PASSTHROUGH)) { rv = close(dup(fi->fh)); From a7cad01dbda49108fcde65a31252cf984c18e8d0 Mon Sep 17 00:00:00 2001 From: a Date: Tue, 11 Nov 2025 14:44:14 -0600 Subject: [PATCH 4/5] comment --- src/etcfs/etcfs.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/etcfs/etcfs.c b/src/etcfs/etcfs.c index ec00aa15..10fc2b1f 100644 --- a/src/etcfs/etcfs.c +++ b/src/etcfs/etcfs.c @@ -1797,6 +1797,8 @@ static int m_create(const char *path, mode_t mode, struct fuse_file_info *fi) fi->backing_id = backing_id; fi->flags |= FOPEN_PASSTHROUGH; fi->fh = fd; /* Keep fd for fallback, kernel uses backing_id */ + // when we open in passthrough mode, we need to drop the old page cache, according to libfuse https://re/blob/master/example/passthrough_hp.cc + fi->keep_cache = false; DEBUG("m_create: using passthrough", path); } else { /* Passthrough registration failed, use traditional mode */ @@ -1839,6 +1841,8 @@ static int m_open(const char *path, struct fuse_file_info *fi) fi->backing_id = backing_id; fi->flags |= FOPEN_PASSTHROUGH; fi->fh = fd; /* Keep fd for fallback, kernel uses backing_id */ + // when we open in passthrough mode, we need to drop the old page cache, according to libfuse https://re/blob/master/example/passthrough_hp.cc + fi->keep_cache = false; DEBUG("m_open: using passthrough", path); } else { /* Passthrough registration failed, use traditional mode */ From 7b5e39357448e7d2678e1b25522605fcca4954ab Mon Sep 17 00:00:00 2001 From: a Date: Tue, 11 Nov 2025 14:57:35 -0600 Subject: [PATCH 5/5] hmm i think we need to always flush --- src/etcfs/etcfs.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/etcfs/etcfs.c b/src/etcfs/etcfs.c index 10fc2b1f..68d62160 100644 --- a/src/etcfs/etcfs.c +++ b/src/etcfs/etcfs.c @@ -1957,13 +1957,8 @@ static int m_flush(const char *path, struct fuse_file_info *fi) DEBUG("m_flush", path); FS_IMP_SETUP_FD(fi->fh, path, 0); - /* - * In passthrough mode, the kernel handles flushing directly - * from what i understand, this would only flush userspace writes, and we would never have any, so we dont need to ever call this in passthrough mode. - */ - if (!(fi->flags & FOPEN_PASSTHROUGH)) { - rv = close(dup(fi->fh)); - } + // thinking about it, i believe that we do need to call flush even in passthrough, since i think the kernel will have the same page cache for both + rv = close(dup(fi->fh)); FS_IMP_RETURN(rv); }