mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 10:31:33 -04:00
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>
This commit is contained in:
@@ -606,13 +606,11 @@ static inline bool fuse_is_inode_dax_mode(enum fuse_dax_mode mode)
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct fuse_fs_context {
|
struct fuse_fs_context {
|
||||||
int fd;
|
struct fuse_dev *fud;
|
||||||
struct file *file;
|
|
||||||
unsigned int rootmode;
|
unsigned int rootmode;
|
||||||
kuid_t user_id;
|
kuid_t user_id;
|
||||||
kgid_t group_id;
|
kgid_t group_id;
|
||||||
bool is_bdev:1;
|
bool is_bdev:1;
|
||||||
bool fd_present:1;
|
|
||||||
bool rootmode_present:1;
|
bool rootmode_present:1;
|
||||||
bool user_id_present:1;
|
bool user_id_present:1;
|
||||||
bool group_id_present:1;
|
bool group_id_present:1;
|
||||||
|
|||||||
@@ -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)
|
static int fuse_parse_param(struct fs_context *fsc, struct fs_parameter *param)
|
||||||
{
|
{
|
||||||
struct fs_parse_result result;
|
struct fs_parse_result result;
|
||||||
@@ -842,9 +865,7 @@ static int fuse_parse_param(struct fs_context *fsc, struct fs_parameter *param)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
case OPT_FD:
|
case OPT_FD:
|
||||||
ctx->fd = result.uint_32;
|
return fuse_opt_fd(fsc, result.uint_32);
|
||||||
ctx->fd_present = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OPT_ROOTMODE:
|
case OPT_ROOTMODE:
|
||||||
if (!fuse_valid_type(result.uint_32))
|
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;
|
struct fuse_fs_context *ctx = fsc->fs_private;
|
||||||
|
|
||||||
if (ctx) {
|
if (ctx) {
|
||||||
|
if (ctx->fud)
|
||||||
|
fuse_dev_put(ctx->fud);
|
||||||
kfree(ctx->subtype);
|
kfree(ctx->subtype);
|
||||||
kfree(ctx);
|
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)
|
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_mount *fm = get_fuse_mount_super(sb);
|
||||||
struct fuse_conn *fc = fm->fc;
|
struct fuse_conn *fc = fm->fc;
|
||||||
struct inode *root;
|
struct inode *root;
|
||||||
@@ -1950,18 +1973,10 @@ static int fuse_fill_super(struct super_block *sb, struct fs_context *fsc)
|
|||||||
struct fuse_mount *fm;
|
struct fuse_mount *fm;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (!ctx->file || !ctx->rootmode_present ||
|
if (!ctx->fud || !ctx->rootmode_present ||
|
||||||
!ctx->user_id_present || !ctx->group_id_present)
|
!ctx->user_id_present || !ctx->group_id_present)
|
||||||
return -EINVAL;
|
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);
|
err = fuse_fill_super_common(sb, ctx);
|
||||||
if (err)
|
if (err)
|
||||||
return 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)
|
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)
|
static int fuse_get_tree(struct fs_context *fsc)
|
||||||
{
|
{
|
||||||
struct fuse_fs_context *ctx = fsc->fs_private;
|
struct fuse_fs_context *ctx = fsc->fs_private;
|
||||||
struct fuse_dev *fud;
|
|
||||||
struct fuse_conn *fc;
|
struct fuse_conn *fc;
|
||||||
struct fuse_mount *fm;
|
struct fuse_mount *fm;
|
||||||
struct super_block *sb;
|
struct super_block *sb;
|
||||||
@@ -2010,9 +2025,6 @@ static int fuse_get_tree(struct fs_context *fsc)
|
|||||||
|
|
||||||
fsc->s_fs_info = fm;
|
fsc->s_fs_info = fm;
|
||||||
|
|
||||||
if (ctx->fd_present)
|
|
||||||
ctx->file = fget(ctx->fd);
|
|
||||||
|
|
||||||
if (IS_ENABLED(CONFIG_BLOCK) && ctx->is_bdev) {
|
if (IS_ENABLED(CONFIG_BLOCK) && ctx->is_bdev) {
|
||||||
err = get_tree_bdev(fsc, fuse_fill_super);
|
err = get_tree_bdev(fsc, fuse_fill_super);
|
||||||
goto out;
|
goto out;
|
||||||
@@ -2022,16 +2034,15 @@ static int fuse_get_tree(struct fs_context *fsc)
|
|||||||
* (found by device name), normal fuse mounts can't
|
* (found by device name), normal fuse mounts can't
|
||||||
*/
|
*/
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
if (!ctx->file)
|
if (!ctx->fud)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Allow creating a fuse mount with an already initialized fuse
|
* Allow creating a fuse mount with an already initialized fuse
|
||||||
* connection
|
* connection
|
||||||
*/
|
*/
|
||||||
fud = __fuse_get_dev(ctx->file);
|
if (fuse_dev_fc_get(ctx->fud)) {
|
||||||
if (ctx->file->f_op == &fuse_dev_operations && fud) {
|
fsc->sget_key = ctx->fud;
|
||||||
fsc->sget_key = fud->fc;
|
|
||||||
sb = sget_fc(fsc, fuse_test_super, fuse_set_no_super);
|
sb = sget_fc(fsc, fuse_test_super, fuse_set_no_super);
|
||||||
err = PTR_ERR_OR_ZERO(sb);
|
err = PTR_ERR_OR_ZERO(sb);
|
||||||
if (!IS_ERR(sb))
|
if (!IS_ERR(sb))
|
||||||
@@ -2042,8 +2053,6 @@ static int fuse_get_tree(struct fs_context *fsc)
|
|||||||
out:
|
out:
|
||||||
if (fsc->s_fs_info)
|
if (fsc->s_fs_info)
|
||||||
fuse_mount_destroy(fm);
|
fuse_mount_destroy(fm);
|
||||||
if (ctx->file)
|
|
||||||
fput(ctx->file);
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user