summaryrefslogtreecommitdiff
path: root/fs/fuse
diff options
context:
space:
mode:
authorMiklos Szeredi <mszeredi@redhat.com>2021-08-05 06:57:27 +0300
committerMiklos Szeredi <mszeredi@redhat.com>2021-08-05 06:57:27 +0300
commit5d5b74aa9c766f0dd37d5cc1a2a7a94586130501 (patch)
tree9d81da8d4eef764714c301fdfa940db1544f498b /fs/fuse
parent62dd1fc8cc6b22e3e568be46ebdb817e66f5d6a5 (diff)
downloadlinux-5d5b74aa9c766f0dd37d5cc1a2a7a94586130501.tar.xz
fuse: allow sharing existing sb
Make it possible to create a new mount from a already working server. Here's a detailed description of the problem from Jakob: "The background for this question is occasional problems we see with our fuse filesystem [1] and mount namespaces. On a usual client, we have system-wide, autofs managed mountpoints. When a new mount namespace is created (which can be done unprivileged in combination with user namespaces), it can happen that a mountpoint is used inside the new namespace but idle in the root mount namespace. So autofs unmounts the parent, system-wide mountpoint. But the fuse module stays active and still serves mountpoint in the child mount namespace. Because the fuse daemon also blocks other system wide resources corresponding to the mountpoint, this situation effectively prevents new mounts until the child mount namespaces closes. [1] https://github.com/cvmfs/cvmfs" Reported-by: Jakob Blomer <jblomer@cern.ch> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
Diffstat (limited to 'fs/fuse')
-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);