summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/fuse/inode.c38
1 files changed, 37 insertions, 1 deletions
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index 3d64a68c52f7..a3e7fb484938 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -1559,9 +1559,26 @@ static int fuse_fill_super(struct super_block *sb, struct fs_context *fsc)
return err;
}
+/*
+ * This is the path where user supplied an already initialized fuse dev. In
+ * this case never create a new super if the old one is gone.
+ */
+static int fuse_set_no_super(struct super_block *sb, struct fs_context *fsc)
+{
+ return -ENOTCONN;
+}
+
+static int fuse_test_super(struct super_block *sb, struct fs_context *fsc)
+{
+
+ return fsc->sget_key == 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 super_block *sb;
int err;
if (ctx->fd_present)
@@ -1571,8 +1588,27 @@ static int fuse_get_tree(struct fs_context *fsc)
err = get_tree_bdev(fsc, fuse_fill_super);
goto out_fput;
}
+ /*
+ * While block dev mount can be initialized with a dummy device fd
+ * (found by device name), normal fuse mounts can't
+ */
+ if (!ctx->file)
+ return -EINVAL;
- err = get_tree_nodev(fsc, fuse_fill_super);
+ /*
+ * Allow creating a fuse mount with an already initialized fuse
+ * connection
+ */
+ fud = READ_ONCE(ctx->file->private_data);
+ if (ctx->file->f_op == &fuse_dev_operations && fud) {
+ fsc->sget_key = fud->fc;
+ sb = sget_fc(fsc, fuse_test_super, fuse_set_no_super);
+ err = PTR_ERR_OR_ZERO(sb);
+ if (!IS_ERR(sb))
+ fsc->root = dget(sb->s_root);
+ } else {
+ err = get_tree_nodev(fsc, fuse_fill_super);
+ }
out_fput:
if (ctx->file)
fput(ctx->file);