From: Miklos Szeredi Date: Wed, 11 Mar 2026 21:27:44 +0000 (+0100) Subject: fuse: don't require /dev/fuse fd to be kept open during mount X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=d42eb23b2ef9fbe66ea7fd4b3153c3244818d47c;p=thirdparty%2Flinux.git fuse: don't require /dev/fuse fd to be kept open during mount 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 --- diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 339e57a901592..6fafda68f4f1d 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -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; diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index fe72ef2a416c7..173c1ee11550d 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -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; }