]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
fuse: don't require /dev/fuse fd to be kept open during mount
authorMiklos Szeredi <mszeredi@redhat.com>
Wed, 11 Mar 2026 21:27:44 +0000 (22:27 +0100)
committerMiklos Szeredi <mszeredi@redhat.com>
Thu, 2 Apr 2026 18:43:25 +0000 (20:43 +0200)
With the new mount API the sequence of syscalls would be:

        fs_fd = fsopen("fuse", 0);
snprintf(opt, sizeof(opt), "%i", devfd);
fsconfig(fs_fd, FSCONFIG_SET_STRING, "fd", opt, 0);
/* ... */
fsconfig(fs_fd, FSCONFIG_CMD_CREATE, 0, 0, 0);

Current mount code just stores the value of devfd in the fs_context and
uses it in during FSCONFIG_CMD_CREATE, which is inelegant.

Instead grab a reference to the underlying fuse_dev, and use that during
the filesystem creation.

Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
fs/fuse/fuse_i.h
fs/fuse/inode.c

index 339e57a901592fb172cce48c7b9e5bec9c9ecf1f..6fafda68f4f1dcb217fcf49d450e87449b889538 100644 (file)
@@ -606,13 +606,11 @@ static inline bool fuse_is_inode_dax_mode(enum fuse_dax_mode mode)
 }
 
 struct fuse_fs_context {
-       int fd;
-       struct file *file;
+       struct fuse_dev *fud;
        unsigned int rootmode;
        kuid_t user_id;
        kgid_t group_id;
        bool is_bdev:1;
-       bool fd_present:1;
        bool rootmode_present:1;
        bool user_id_present:1;
        bool group_id_present:1;
index fe72ef2a416c7ee35fad5749a9a185919fd398fb..173c1ee11550d042d2a81bc2d3fb56b47fc5d3b5 100644 (file)
@@ -803,6 +803,29 @@ static const struct fs_parameter_spec fuse_fs_parameters[] = {
        {}
 };
 
+static int fuse_opt_fd(struct fs_context *fsc, int fd)
+{
+       struct fuse_fs_context *ctx = fsc->fs_private;
+       struct file *file __free(fput) = fget(fd);
+
+       if (!file)
+               return -EBADF;
+
+       if (file->f_op != &fuse_dev_operations)
+               return invalfc(fsc, "fd is not a fuse device");
+       /*
+        * Require mount to happen from the same user namespace which
+        * opened /dev/fuse to prevent potential attacks.
+        */
+       if (file->f_cred->user_ns != fsc->user_ns)
+               return invalfc(fsc, "wrong user namespace for fuse device");
+
+       ctx->fud = file->private_data;
+       refcount_inc(&ctx->fud->ref);
+
+       return 0;
+}
+
 static int fuse_parse_param(struct fs_context *fsc, struct fs_parameter *param)
 {
        struct fs_parse_result result;
@@ -842,9 +865,7 @@ static int fuse_parse_param(struct fs_context *fsc, struct fs_parameter *param)
                return 0;
 
        case OPT_FD:
-               ctx->fd = result.uint_32;
-               ctx->fd_present = true;
-               break;
+               return fuse_opt_fd(fsc, result.uint_32);
 
        case OPT_ROOTMODE:
                if (!fuse_valid_type(result.uint_32))
@@ -907,6 +928,8 @@ static void fuse_free_fsc(struct fs_context *fsc)
        struct fuse_fs_context *ctx = fsc->fs_private;
 
        if (ctx) {
+               if (ctx->fud)
+                       fuse_dev_put(ctx->fud);
                kfree(ctx->subtype);
                kfree(ctx);
        }
@@ -1849,7 +1872,7 @@ EXPORT_SYMBOL_GPL(fuse_init_fs_context_submount);
 
 int fuse_fill_super_common(struct super_block *sb, struct fuse_fs_context *ctx)
 {
-       struct fuse_dev *fud = ctx->file ? fuse_file_to_fud(ctx->file) : NULL;
+       struct fuse_dev *fud = ctx->fud;
        struct fuse_mount *fm = get_fuse_mount_super(sb);
        struct fuse_conn *fc = fm->fc;
        struct inode *root;
@@ -1950,18 +1973,10 @@ static int fuse_fill_super(struct super_block *sb, struct fs_context *fsc)
        struct fuse_mount *fm;
        int err;
 
-       if (!ctx->file || !ctx->rootmode_present ||
+       if (!ctx->fud || !ctx->rootmode_present ||
            !ctx->user_id_present || !ctx->group_id_present)
                return -EINVAL;
 
-       /*
-        * Require mount to happen from the same user namespace which
-        * opened /dev/fuse to prevent potential attacks.
-        */
-       if ((ctx->file->f_op != &fuse_dev_operations) ||
-           (ctx->file->f_cred->user_ns != sb->s_user_ns))
-               return -EINVAL;
-
        err = fuse_fill_super_common(sb, ctx);
        if (err)
                return err;
@@ -1982,14 +1997,14 @@ static int fuse_set_no_super(struct super_block *sb, struct fs_context *fsc)
 
 static int fuse_test_super(struct super_block *sb, struct fs_context *fsc)
 {
+       struct fuse_dev *fud = fsc->sget_key;
 
-       return fsc->sget_key == get_fuse_conn_super(sb);
+       return fuse_dev_fc_get(fud) == get_fuse_conn_super(sb);
 }
 
 static int fuse_get_tree(struct fs_context *fsc)
 {
        struct fuse_fs_context *ctx = fsc->fs_private;
-       struct fuse_dev *fud;
        struct fuse_conn *fc;
        struct fuse_mount *fm;
        struct super_block *sb;
@@ -2010,9 +2025,6 @@ static int fuse_get_tree(struct fs_context *fsc)
 
        fsc->s_fs_info = fm;
 
-       if (ctx->fd_present)
-               ctx->file = fget(ctx->fd);
-
        if (IS_ENABLED(CONFIG_BLOCK) && ctx->is_bdev) {
                err = get_tree_bdev(fsc, fuse_fill_super);
                goto out;
@@ -2022,16 +2034,15 @@ static int fuse_get_tree(struct fs_context *fsc)
         * (found by device name), normal fuse mounts can't
         */
        err = -EINVAL;
-       if (!ctx->file)
+       if (!ctx->fud)
                goto out;
 
        /*
         * Allow creating a fuse mount with an already initialized fuse
         * connection
         */
-       fud = __fuse_get_dev(ctx->file);
-       if (ctx->file->f_op == &fuse_dev_operations && fud) {
-               fsc->sget_key = fud->fc;
+       if (fuse_dev_fc_get(ctx->fud)) {
+               fsc->sget_key = ctx->fud;
                sb = sget_fc(fsc, fuse_test_super, fuse_set_no_super);
                err = PTR_ERR_OR_ZERO(sb);
                if (!IS_ERR(sb))
@@ -2042,8 +2053,6 @@ static int fuse_get_tree(struct fs_context *fsc)
 out:
        if (fsc->s_fs_info)
                fuse_mount_destroy(fm);
-       if (ctx->file)
-               fput(ctx->file);
        return err;
 }