summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiklos Szeredi <mszeredi@redhat.com>2026-03-11 22:27:44 +0100
committerMiklos Szeredi <mszeredi@redhat.com>2026-04-02 20:43:25 +0200
commitd42eb23b2ef9fbe66ea7fd4b3153c3244818d47c (patch)
tree024a13ffcc37b1e14f39b1f216ce24e0f00d8345
parente9bf38500ed9aec7cfdf9219c75d353645b41168 (diff)
downloadlinux-d42eb23b2ef9fbe66ea7fd4b3153c3244818d47c.tar.gz
linux-d42eb23b2ef9fbe66ea7fd4b3153c3244818d47c.zip
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 <mszeredi@redhat.com>
-rw-r--r--fs/fuse/fuse_i.h4
-rw-r--r--fs/fuse/inode.c57
2 files changed, 34 insertions, 27 deletions
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 339e57a90159..6fafda68f4f1 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 fe72ef2a416c..173c1ee11550 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;
}