fuse: allow sharing existing sb
authorMiklos Szeredi <mszeredi@redhat.com>
Thu, 5 Aug 2021 03:57:27 +0000 (05:57 +0200)
committerMiklos Szeredi <mszeredi@redhat.com>
Thu, 5 Aug 2021 03:57:27 +0000 (05:57 +0200)
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>
fs/fuse/inode.c

index 3d64a68..a3e7fb4 100644 (file)
@@ -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);