On Thu, Oct 1, 2026 at 7:07 PM Amir Goldstein <[email protected]> wrote: > > On Thu, Oct 1, 2026 at 5:09 PM Miklos Szeredi <[email protected]> wrote: > > > > Add support for server allocated 64-bit backing IDs alongside the > > existing kernel allocated 32-bit IDs. > > > > Opened with FUSE_DEV_IOC_BACKING_OPEN, but the backing ID is sent via > > fuse_backing_map.backing_id, with FUSE_BACKING_ID_64 being set in .flags. > > Description outdated. > > > > > Closed with FUSE_NOTIFY_BACKING_REMOVE. Since close only provides the > > backing ID, not the file descriptor, it doesn't have to be done with an > > ioctl. > > > > All 64 bit values are valid. > > Can you reserve id 0? or have a reason to need it? > > > > > Signed-off-by: Miklos Szeredi <[email protected]> > > --- > > fs/fuse/backing.c | 180 ++++++++++++++++++++++++++++---------- > > fs/fuse/dev.c | 21 +++++ > > fs/fuse/dev.h | 3 + > > fs/fuse/fuse_i.h | 21 ++++- > > fs/fuse/inode.c | 6 +- > > fs/fuse/notify.c | 28 ++++++ > > fs/fuse/passthrough.c | 3 + > > include/uapi/linux/fuse.h | 22 ++++- > > 8 files changed, 231 insertions(+), 53 deletions(-) > > > > diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c > > index 433fa3098d71..3c879df7989c 100644 > > --- a/fs/fuse/backing.c > > +++ b/fs/fuse/backing.c > > @@ -9,6 +9,7 @@ > > #include "fuse_i.h" > > > > #include <linux/file.h> > > +#include <linux/rhashtable.h> > > > > static struct fuse_backing *fuse_backing_get(struct fuse_backing *fb) > > { > > @@ -48,6 +49,8 @@ static int fuse_backing_id_alloc(struct fuse_conn *fc, > > struct fuse_backing *fb) > > id = idr_alloc_cyclic(&fc->backing_files_map, fb, 1, 0, GFP_ATOMIC); > > spin_unlock(&fc->lock); > > idr_preload_end(); > > + if (id > 0) > > + fb->backing_id = id; > > > > WARN_ON_ONCE(id == 0); > > return id; > > @@ -61,81 +64,131 @@ static struct fuse_backing > > *fuse_backing_id_remove(struct fuse_conn *fc, > > spin_lock(&fc->lock); > > fb = idr_remove(&fc->backing_files_map, id); > > spin_unlock(&fc->lock); > > + if (fb) > > + fb->backing_id = 0; > > > > return fb; > > } > > > > -static int fuse_backing_id_free(int id, void *p, void *data) > > -{ > > - struct fuse_backing *fb = p; > > +static const struct rhashtable_params fuse_backing_prm = { > > + .head_offset = offsetof(struct fuse_backing, hash_node), > > + .key_offset = offsetof(struct fuse_backing, backing_id), > > + .key_len = sizeof_field(struct fuse_backing, backing_id), > > +}; > > > > - WARN_ON_ONCE(refcount_read(&fb->count) != 1); > > - fuse_backing_free(fb); > > - return 0; > > +static int fuse_backing_add_64(struct fuse_conn *fc, struct fuse_backing > > *fb) > > +{ > > + return rhashtable_insert_fast(&fc->backing_64_ht, &fb->hash_node, > > fuse_backing_prm); > > } > > > > -void fuse_backing_files_free(struct fuse_conn *fc) > > +int fuse_backing_close_64(struct fuse_conn *fc, u64 backing_id) > > { > > - idr_for_each(&fc->backing_files_map, fuse_backing_id_free, NULL); > > - idr_destroy(&fc->backing_files_map); > > + struct fuse_backing *fb; > > + int err; > > + > > + if (!fc->backing_id_64) > > + return -EINVAL; > > + > > + scoped_guard(spinlock, &fc->lock) { > > + fb = rhashtable_lookup_fast(&fc->backing_64_ht, > > &backing_id, fuse_backing_prm); > > + if (!fb) > > + return -ENOENT; > > + > > + err = rhashtable_remove_fast(&fc->backing_64_ht, > > &fb->hash_node, fuse_backing_prm); > > + WARN_ON(err); > > + } > > + fb->backing_id = 0; > > + fuse_backing_put(fb); > > + > > + return 0; > > } > > > > -int fuse_backing_open(struct fuse_conn *fc, struct fuse_backing_map *map) > > +static struct fuse_backing *fuse_backing_new(struct fuse_conn *fc, int fd) > > { > > - struct file *file; > > + struct fuse_backing *fb; > > struct super_block *backing_sb; > > - struct fuse_backing *fb = NULL; > > - int res; > > - > > - pr_debug("%s: fd=%d flags=0x%x\n", __func__, map->fd, map->flags); > > + struct file *file; > > > > /* TODO: relax CAP_SYS_ADMIN once backing files are visible to lsof > > */ > > - res = -EPERM; > > if (!fc->passthrough || !capable(CAP_SYS_ADMIN)) > > - goto out; > > + return ERR_PTR(-EPERM); > > > > - res = -EINVAL; > > - if (map->flags || map->padding) > > - goto out; > > + CLASS(fd_raw, f)(fd); > > + if (fd_empty(f)) > > + return ERR_PTR(-EBADF); > > > > - file = fget_raw(map->fd); > > - res = -EBADF; > > - if (!file) > > - goto out; > > + file = fd_file(f); > > > > /* read/write/splice/mmap passthrough only relevant for regular > > files */ > > - res = d_is_dir(file->f_path.dentry) ? -EISDIR : -EINVAL; > > if (!d_is_reg(file->f_path.dentry)) > > - goto out_fput; > > + return d_is_dir(file->f_path.dentry) ? ERR_PTR(-EISDIR) : > > ERR_PTR(-EINVAL); > > > > backing_sb = file_inode(file)->i_sb; > > - res = -ELOOP; > > if (backing_sb->s_stack_depth >= fc->max_stack_depth) > > - goto out_fput; > > + return ERR_PTR(-ELOOP); > > > > fb = kmalloc_obj(struct fuse_backing); > > - res = -ENOMEM; > > if (!fb) > > - goto out_fput; > > + return ERR_PTR(-ENOMEM); > > > > - fb->file = file; > > + fb->file = get_file(file); > > fb->cred = get_current_cred(); > > refcount_set(&fb->count, 1); > > > > - res = fuse_backing_id_alloc(fc, fb); > > - if (res < 0) { > > - fuse_backing_free(fb); > > - fb = NULL; > > + return fb; > > +} > > + > > +int fuse_backing_open_64(struct fuse_conn *fc, struct > > fuse_backing_create_in *map) > > +{ > > + struct fuse_backing *fb; > > + int res; > > + > > + res = -EINVAL; > > + if (map->padding || map->spare[0] || map->spare[1]) > > Please reserve backing_id 0 > > > + goto out; > > + > > + if (!fc->backing_id_64) > > + goto out; > > + > > + fb = fuse_backing_new(fc, map->fd); > > + res = PTR_ERR(fb); > > + if (!IS_ERR(fb)) { > > + fb->backing_id = map->backing_id; > > + res = fuse_backing_add_64(fc, fb); > > + if (res < 0) > > + fuse_backing_free(fb); > > } > > +out: > > + return res; > > +} > > + > > +int fuse_backing_open(struct fuse_conn *fc, struct fuse_backing_map *map) > > +{ > > + struct fuse_backing *fb = NULL; > > + int res; > > + > > + pr_debug("%s: fd=%d flags=0x%x\n", __func__, map->fd, map->flags); > > > > + res = -EINVAL; > > + if (map->flags || map->padding) > > + goto out; > > + > > + if (fc->backing_id_64) > > + goto out; > > + > > + fb = fuse_backing_new(fc, map->fd); > > + res = PTR_ERR(fb); > > + if (!IS_ERR(fb)) { > > + res = fuse_backing_id_alloc(fc, fb); > > + if (res < 0) { > > + fuse_backing_free(fb); > > + fb = NULL; > > + } > > + } > > out: > > pr_debug("%s: fb=0x%p, ret=%i\n", __func__, fb, res); > > > > return res; > > - > > -out_fput: > > - fput(file); > > - goto out; > > } > > > > int fuse_backing_close(struct fuse_conn *fc, int backing_id) > > @@ -145,6 +198,9 @@ int fuse_backing_close(struct fuse_conn *fc, int > > backing_id) > > > > pr_debug("%s: backing_id=%d\n", __func__, backing_id); > > > > + if (fc->backing_id_64) > > + return -EINVAL; > > + > > /* TODO: relax CAP_SYS_ADMIN once backing files are visible to lsof > > */ > > err = -EPERM; > > if (!fc->passthrough || !capable(CAP_SYS_ADMIN)) > > @@ -167,14 +223,48 @@ int fuse_backing_close(struct fuse_conn *fc, int > > backing_id) > > return err; > > } > > > > -struct fuse_backing *fuse_backing_lookup(struct fuse_conn *fc, int > > backing_id) > > +struct fuse_backing *fuse_backing_lookup(struct fuse_conn *fc, u64 > > backing_id) > > { > > struct fuse_backing *fb; > > > > - rcu_read_lock(); > > - fb = idr_find(&fc->backing_files_map, backing_id); > > - fb = fuse_backing_get(fb); > > - rcu_read_unlock(); > > + guard(rcu)(); > > + if (!fc->backing_id_64) > > + fb = idr_find(&fc->backing_files_map, backing_id); > > + else > > + fb = rhashtable_lookup(&fc->backing_64_ht, &backing_id, > > fuse_backing_prm); > > > > - return fb; > > + return fuse_backing_get(fb); > > +} > > + > > +static void fuse_backing_check_free(struct fuse_backing *fb) > > +{ > > + WARN_ON_ONCE(refcount_read(&fb->count) != 1); > > + fuse_backing_free(fb); > > +} > > + > > +static int fuse_backing_idr_free(int id, void *p, void *data) > > +{ > > + fuse_backing_check_free(p); > > + return 0; > > +} > > + > > +static void fuse_backing_rht_free(void *p, void *data) > > +{ > > + fuse_backing_check_free(p); > > +} > > + > > +void fuse_backing_files_free(struct fuse_conn *fc) > > +{ > > + if (fc->backing_id_64) { > > + rhashtable_free_and_destroy(&fc->backing_64_ht, > > fuse_backing_rht_free, NULL); > > + } else { > > + idr_for_each(&fc->backing_files_map, fuse_backing_idr_free, > > NULL); > > + idr_destroy(&fc->backing_files_map); > > + } > > +} > > + > > +void fuse_backing_files_init_64(struct fuse_conn *fc) > > +{ > > + rhashtable_init(&fc->backing_64_ht, &fuse_backing_prm); > > + fc->backing_id_64 = true; > > } > > diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c > > index 2d7ee5498f1c..8b63a08a26b6 100644 > > --- a/fs/fuse/dev.c > > +++ b/fs/fuse/dev.c > > @@ -2339,6 +2339,24 @@ static long fuse_dev_ioctl_backing_open(struct file > > *file, > > return fuse_backing_open(fud->chan->conn, &map); > > } > > > > +static long fuse_dev_ioctl_backing_create(struct file *file, > > + struct fuse_backing_create_in > > __user *argp) > > +{ > > + struct fuse_dev *fud = fuse_get_dev(file); > > + struct fuse_backing_create_in map; > > + > > + if (IS_ERR(fud)) > > + return PTR_ERR(fud); > > + > > + if (!IS_ENABLED(CONFIG_FUSE_PASSTHROUGH)) > > + return -EOPNOTSUPP; > > + > > + if (copy_from_user(&map, argp, sizeof(map))) > > + return -EFAULT; > > + > > + return fuse_backing_open_64(fud->chan->conn, &map); > > +} > > + > > static long fuse_dev_ioctl_backing_close(struct file *file, __u32 __user > > *argp) > > { > > struct fuse_dev *fud = fuse_get_dev(file); > > @@ -2379,6 +2397,9 @@ static long fuse_dev_ioctl(struct file *file, > > unsigned int cmd, > > case FUSE_DEV_IOC_BACKING_OPEN: > > return fuse_dev_ioctl_backing_open(file, argp); > > > > + case FUSE_DEV_IOC_BACKING_CREATE: > > + return fuse_dev_ioctl_backing_create(file, argp); > > + > > case FUSE_DEV_IOC_BACKING_CLOSE: > > return fuse_dev_ioctl_backing_close(file, argp); > > > > diff --git a/fs/fuse/dev.h b/fs/fuse/dev.h > > index f6c47ae0395b..dbffd5bed2f5 100644 > > --- a/fs/fuse/dev.h > > +++ b/fs/fuse/dev.h > > @@ -14,6 +14,7 @@ struct fuse_dev; > > struct fuse_args; > > struct fuse_copy_state; > > struct fuse_backing_map; > > +struct fuse_backing_create_in; > > struct file; > > struct folio; > > enum fuse_notify_code; > > @@ -87,6 +88,8 @@ int fuse_notify(struct fuse_conn *fc, enum > > fuse_notify_code code, > > > > int fuse_backing_open(struct fuse_conn *fc, struct fuse_backing_map *map); > > int fuse_backing_close(struct fuse_conn *fc, int backing_id); > > +int fuse_backing_open_64(struct fuse_conn *fc, struct > > fuse_backing_create_in *map); > > +int fuse_backing_close_64(struct fuse_conn *fc, u64 backing_id); > > > > int fuse_copy_one(struct fuse_copy_state *cs, void *val, unsigned size); > > int fuse_copy_folio(struct fuse_copy_state *cs, struct folio **foliop, > > diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h > > index 3dd4dff24c50..003ef3c35d7a 100644 > > --- a/fs/fuse/fuse_i.h > > +++ b/fs/fuse/fuse_i.h > > @@ -32,6 +32,7 @@ > > #include <linux/pid_namespace.h> > > #include <linux/refcount.h> > > #include <linux/user_namespace.h> > > +#include <linux/rhashtable-types.h> > > > > /** Default max number of pages that can be used in a single read request > > */ > > #define FUSE_DEFAULT_MAX_PAGES_PER_REQ 32 > > @@ -92,7 +93,8 @@ struct fuse_submount_lookup { > > struct fuse_backing { > > struct file *file; > > const struct cred *cred; > > - > > + u64 backing_id; > > + struct rhash_head hash_node; > > /* refcount */ > > refcount_t count; > > struct rcu_head rcu; > > @@ -689,6 +691,9 @@ struct fuse_conn { > > /** @init_security: Initialize security xattrs when creating a new > > inode */ > > unsigned int init_security:1; > > > > + /** Backing ID is 64 bit and allocated by the server */ > > + bool backing_id_64:1; > > + > > /** > > * @create_supp_group: Add supplementary group info when creating > > * a new inode > > @@ -770,8 +775,14 @@ struct fuse_conn { > > struct fuse_sync_bucket __rcu *curr_bucket; > > > > #ifdef CONFIG_FUSE_PASSTHROUGH > > - /** @backing_files_map: IDR for backing files ids */ > > - struct idr backing_files_map; > > + /* Selected by backing_id_64 */ > > + union { > > + /** @backing_files_map: IDR for backing files ids */ > > + struct idr backing_files_map; > > + > > + /** @backing_64_ht: 64 bit ID lookup hash table */ > > + struct rhashtable backing_64_ht; > > + }; > > #endif > > }; > > > > @@ -1270,7 +1281,7 @@ void fuse_file_release(struct inode *inode, struct > > fuse_file *ff, > > /* backing.c */ > > #ifdef CONFIG_FUSE_PASSTHROUGH > > void fuse_backing_put(struct fuse_backing *fb); > > -struct fuse_backing *fuse_backing_lookup(struct fuse_conn *fc, int > > backing_id); > > + > > #else > > > > static inline void fuse_backing_put(struct fuse_backing *fb) > > @@ -1278,7 +1289,9 @@ static inline void fuse_backing_put(struct > > fuse_backing *fb) > > } > > #endif > > > > +struct fuse_backing *fuse_backing_lookup(struct fuse_conn *fc, u64 > > backing_id); > > void fuse_backing_files_init(struct fuse_conn *fc); > > +void fuse_backing_files_init_64(struct fuse_conn *fc); > > void fuse_backing_files_free(struct fuse_conn *fc); > > > > /* passthrough.c */ > > diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c > > index cbb10e19e7e8..bb76bddfc241 100644 > > --- a/fs/fuse/inode.c > > +++ b/fs/fuse/inode.c > > @@ -1408,13 +1408,15 @@ static void process_init_reply(struct fuse_args > > *args, int error) > > * them together. > > */ > > if (IS_ENABLED(CONFIG_FUSE_PASSTHROUGH) && > > - (flags & FUSE_PASSTHROUGH) && > > + (flags & (FUSE_PASSTHROUGH | > > FUSE_PASSTHROUGH_V2)) && > > arg->max_stack_depth > 0 && > > arg->max_stack_depth <= > > FILESYSTEM_MAX_STACK_DEPTH && > > !(flags & FUSE_WRITEBACK_CACHE)) { > > fc->passthrough = 1; > > fc->max_stack_depth = arg->max_stack_depth; > > fm->sb->s_stack_depth = > > arg->max_stack_depth; > > + if (flags & FUSE_PASSTHROUGH_V2) > > + fuse_backing_files_init_64(fc); > > It would feel a little less hacky if we do: > else > fuse_backing_files_init_32(fc); > > and set > fc->passthrough = 1; > > inside both of them to gate against fuse_backing_files_free() > called without processing init response, > or without either FUSE_PASSTHROUGH in the init response.
Also we should not enable passthrough if rhashtable_init has failed. Thanks, Amir.
