diff options
author | Miklos Szeredi <mszeredi@redhat.com> | 2021-08-05 05:57:27 +0200 |
---|---|---|
committer | Miklos Szeredi <mszeredi@redhat.com> | 2021-08-05 05:57:27 +0200 |
commit | 5d5b74aa9c766f0dd37d5cc1a2a7a94586130501 (patch) | |
tree | 9d81da8d4eef764714c301fdfa940db1544f498b /fs/fuse | |
parent | 62dd1fc8cc6b22e3e568be46ebdb817e66f5d6a5 (diff) | |
download | linux-5d5b74aa9c766f0dd37d5cc1a2a7a94586130501.tar.bz2 |
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.c | 38 |
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); |