aboutsummaryrefslogtreecommitdiffstats
path: root/fs/fuse/inode.c
diff options
context:
space:
mode:
authorMax Reitz <mreitz@redhat.com>2020-04-21 14:47:15 +0200
committerMiklos Szeredi <mszeredi@redhat.com>2020-10-09 16:33:47 +0200
commitbf109c64040f5b6bfe8a7044667e11d62dff6d91 (patch)
tree92e8c2520d36e92b500e82294d0e4259a0a77d8d /fs/fuse/inode.c
parentfuse: Allow fuse_fill_super_common() for submounts (diff)
downloadlinux-dev-bf109c64040f5b6bfe8a7044667e11d62dff6d91.tar.xz
linux-dev-bf109c64040f5b6bfe8a7044667e11d62dff6d91.zip
fuse: implement crossmounts
FUSE servers can indicate crossmount points by setting FUSE_ATTR_SUBMOUNT in fuse_attr.flags. The inode will then be marked as S_AUTOMOUNT, and the .d_automount implementation creates a new submount at that location, so that the submount gets a distinct st_dev value. Note that all submounts get a distinct superblock and a distinct st_dev value, so for virtio-fs, even if the same filesystem is mounted more than once on the host, none of its mount points will have the same st_dev. We need distinct superblocks because the superblock points to the root node, but the different host mounts may show different trees (e.g. due to submounts in some of them, but not in others). Right now, this behavior is only enabled when fuse_conn.auto_submounts is set, which is the case only for virtio-fs. Signed-off-by: Max Reitz <mreitz@redhat.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
Diffstat (limited to '')
-rw-r--r--fs/fuse/inode.c26
1 files changed, 24 insertions, 2 deletions
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index da5f03f910cf..7eceb0f20c93 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -309,7 +309,26 @@ struct inode *fuse_iget(struct super_block *sb, u64 nodeid,
struct fuse_inode *fi;
struct fuse_conn *fc = get_fuse_conn_super(sb);
- retry:
+ /*
+ * Auto mount points get their node id from the submount root, which is
+ * not a unique identifier within this filesystem.
+ *
+ * To avoid conflicts, do not place submount points into the inode hash
+ * table.
+ */
+ if (fc->auto_submounts && (attr->flags & FUSE_ATTR_SUBMOUNT) &&
+ S_ISDIR(attr->mode)) {
+ inode = new_inode(sb);
+ if (!inode)
+ return NULL;
+
+ fuse_init_inode(inode, attr);
+ get_fuse_inode(inode)->nodeid = nodeid;
+ inode->i_flags |= S_AUTOMOUNT;
+ goto done;
+ }
+
+retry:
inode = iget5_locked(sb, nodeid, fuse_inode_eq, fuse_inode_set, &nodeid);
if (!inode)
return NULL;
@@ -327,7 +346,7 @@ struct inode *fuse_iget(struct super_block *sb, u64 nodeid,
iput(inode);
goto retry;
}
-
+done:
fi = get_fuse_inode(inode);
spin_lock(&fi->lock);
fi->nlookup++;
@@ -1083,6 +1102,9 @@ void fuse_send_init(struct fuse_mount *fm)
if (fm->fc->dax)
ia->in.flags |= FUSE_MAP_ALIGNMENT;
#endif
+ if (fm->fc->auto_submounts)
+ ia->in.flags |= FUSE_SUBMOUNTS;
+
ia->args.opcode = FUSE_INIT;
ia->args.in_numargs = 1;
ia->args.in_args[0].size = sizeof(ia->in);