Submounts have their own superblock, which needs to be initialized. However, they do not have a fuse_fs_context associated with them, and the root node's attributes should be taken from the mountpoint's node.
Extend fuse_fill_super_common() to work for submounts by making the @ctx parameter optional, and by adding a @submount_finode parameter. (There is a plain "unsigned" in an existing code block that is being indented by this commit. Extend it to "unsigned int" so checkpatch does not complain.) Signed-off-by: Max Reitz <[email protected]> --- fs/fuse/fuse_i.h | 10 ++-- fs/fuse/inode.c | 129 +++++++++++++++++++++++++++++++++++--------- fs/fuse/virtio_fs.c | 2 +- 3 files changed, 112 insertions(+), 29 deletions(-) diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 8afe3972471a..2e400b479e19 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1030,11 +1030,15 @@ void fuse_dev_free(struct fuse_dev *fud); void fuse_send_init(struct fuse_mount *fm); /** - * Fill in superblock and initialize fuse connection + * Fill in superblock and initialize fuse connection for root mounts. * @sb: partially-initialized superblock to fill in - * @ctx: mount context + * @ctx: mount context (for root mounts) + * @submount_finode: For submounts: The fuse_inode of the parent + * filesystem where this submount is mounted + * (NULL for root mounts) */ -int fuse_fill_super_common(struct super_block *sb, struct fuse_fs_context *ctx); +int fuse_fill_super_common(struct super_block *sb, struct fuse_fs_context *ctx, + struct fuse_inode *submount_finode); /** * Disassociate fuse connection from superblock and kill the superblock diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 3ecce4255030..22179cf55c08 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -1187,22 +1187,55 @@ void fuse_dev_free(struct fuse_dev *fud) } EXPORT_SYMBOL_GPL(fuse_dev_free); -int fuse_fill_super_common(struct super_block *sb, struct fuse_fs_context *ctx) +static void fuse_fill_attr_from_inode(struct fuse_attr *attr, + const struct fuse_inode *fi) +{ + *attr = (struct fuse_attr){ + .ino = fi->inode.i_ino, + .size = fi->inode.i_size, + .blocks = fi->inode.i_blocks, + .atime = fi->inode.i_atime.tv_sec, + .mtime = fi->inode.i_mtime.tv_sec, + .ctime = fi->inode.i_ctime.tv_sec, + .atimensec = fi->inode.i_atime.tv_nsec, + .mtimensec = fi->inode.i_mtime.tv_nsec, + .ctimensec = fi->inode.i_ctime.tv_nsec, + .mode = fi->inode.i_mode, + .nlink = fi->inode.i_nlink, + .uid = fi->inode.i_uid.val, + .gid = fi->inode.i_gid.val, + .rdev = fi->inode.i_rdev, + .blksize = 1u << fi->inode.i_blkbits, + }; +} + +int fuse_fill_super_common(struct super_block *sb, struct fuse_fs_context *ctx, + struct fuse_inode *submount_finode) { struct fuse_dev *fud = NULL; struct fuse_mount *fm = get_fuse_mount_super(sb); + struct fuse_mount *fm_iter; struct fuse_conn *fc = fm->fc; + struct backing_dev_info *fm_bdi; struct inode *root; struct dentry *root_dentry; int err; err = -EINVAL; + /* One must be NULL, the other must be non-NULL */ + if (!ctx == !submount_finode) + goto err; + + /* ctx must be given if and only if this is about a root mount */ + if ((bool)ctx != fm->root) + goto err; + if (sb->s_flags & SB_MANDLOCK) goto err; sb->s_flags &= ~(SB_NOSEC | SB_I_VERSION); - if (ctx->is_bdev) { + if (ctx && ctx->is_bdev) { #ifdef CONFIG_BLOCK err = -EINVAL; if (!sb_set_blocksize(sb, ctx->blksize)) @@ -1213,8 +1246,12 @@ int fuse_fill_super_common(struct super_block *sb, struct fuse_fs_context *ctx) sb->s_blocksize_bits = PAGE_SHIFT; } - sb->s_subtype = ctx->subtype; - ctx->subtype = NULL; + if (ctx) { + sb->s_subtype = ctx->subtype; + ctx->subtype = NULL; + } else + sb->s_subtype = NULL; + sb->s_magic = FUSE_SUPER_MAGIC; sb->s_op = &fuse_super_operations; sb->s_xattr = fuse_xattr_handlers; @@ -1232,7 +1269,7 @@ int fuse_fill_super_common(struct super_block *sb, struct fuse_fs_context *ctx) if (sb->s_user_ns != &init_user_ns) sb->s_xattr = fuse_no_acl_xattr_handlers; - if (ctx->fudptr) { + if (ctx && ctx->fudptr) { err = -ENOMEM; fud = fuse_dev_alloc_install(fc); if (!fud) @@ -1241,27 +1278,66 @@ int fuse_fill_super_common(struct super_block *sb, struct fuse_fs_context *ctx) fm->dev = sb->s_dev; fm->sb = sb; - err = fuse_bdi_init(fm, sb); - if (err) - goto err_dev_free; + + /* Look for whether there already is a BDI we can share */ + fm_bdi = sb->s_bdi; + sb->s_bdi = NULL; + + list_for_each_entry(fm_iter, &fc->mounts, fc_entry) { + if (fm_iter->sb && fm_iter->sb->s_bdi) { + sb->s_bdi = bdi_get(fm_iter->sb->s_bdi); + break; + } + } + + if (sb->s_bdi) { + if (sb->s_bdev) + bdi_put(fm_bdi); + } else { + /* There is no BDI to share yet */ + sb->s_bdi = fm_bdi; + err = fuse_bdi_init(fm, sb); + if (err) + goto err_dev_free; + } /* Handle umasking inside the fuse code */ if (sb->s_flags & SB_POSIXACL) fc->dont_mask = 1; sb->s_flags |= SB_POSIXACL; - fc->default_permissions = ctx->default_permissions; - fc->allow_other = ctx->allow_other; - fc->user_id = ctx->user_id; - fc->group_id = ctx->group_id; - fc->max_read = max_t(unsigned, 4096, ctx->max_read); - fc->destroy = ctx->destroy; - fc->no_control = ctx->no_control; - fc->no_force_umount = ctx->no_force_umount; - fc->no_mount_options = ctx->no_mount_options; + if (ctx) { + fc->default_permissions = ctx->default_permissions; + fc->allow_other = ctx->allow_other; + fc->user_id = ctx->user_id; + fc->group_id = ctx->group_id; + fc->max_read = max_t(unsigned int, 4096, ctx->max_read); + fc->destroy = ctx->destroy; + fc->no_control = ctx->no_control; + fc->no_force_umount = ctx->no_force_umount; + fc->no_mount_options = ctx->no_mount_options; + } err = -ENOMEM; - root = fuse_get_root_inode(sb, ctx->rootmode); + if (ctx) { + root = fuse_get_root_inode(sb, ctx->rootmode); + } else { + struct fuse_attr root_attr; + struct fuse_inode *root_fi; + + fuse_fill_attr_from_inode(&root_attr, submount_finode); + root = fuse_iget(sb, submount_finode->nodeid, 0, &root_attr, 0, 0); + + /* + * This inode is just a duplicate, so it is not looked up and + * its nlookup should not be incremented. fuse_iget() does + * that, though, so undo it here. + */ + root_fi = get_fuse_inode(root); + spin_lock(&root_fi->lock); + root_fi->nlookup--; + spin_unlock(&root_fi->lock); + } sb->s_d_op = &fuse_root_dentry_operations; root_dentry = d_make_root(root); if (!root_dentry) @@ -1271,16 +1347,19 @@ int fuse_fill_super_common(struct super_block *sb, struct fuse_fs_context *ctx) mutex_lock(&fuse_mutex); err = -EINVAL; - if (ctx->fudptr && *ctx->fudptr) + if (ctx && ctx->fudptr && *ctx->fudptr) goto err_unlock; - err = fuse_ctl_add_conn(fc); - if (err) - goto err_unlock; + if (ctx) { + err = fuse_ctl_add_conn(fc); + if (err) + goto err_unlock; + + list_add_tail(&fc->entry, &fuse_conn_list); + } - list_add_tail(&fc->entry, &fuse_conn_list); sb->s_root = root_dentry; - if (ctx->fudptr) + if (ctx && ctx->fudptr) *ctx->fudptr = fud; mutex_unlock(&fuse_mutex); return 0; @@ -1336,7 +1415,7 @@ static int fuse_fill_super(struct super_block *sb, struct fs_context *fsc) fuse_conn_put(fc); sb->s_fs_info = fm; - err = fuse_fill_super_common(sb, ctx); + err = fuse_fill_super_common(sb, ctx, NULL); if (err) goto err_put_conn; /* diff --git a/fs/fuse/virtio_fs.c b/fs/fuse/virtio_fs.c index 3dd5a6cf6a6d..761ecccb1c36 100644 --- a/fs/fuse/virtio_fs.c +++ b/fs/fuse/virtio_fs.c @@ -1112,7 +1112,7 @@ static int virtio_fs_fill_super(struct super_block *sb) /* virtiofs allocates and installs its own fuse devices */ ctx.fudptr = NULL; - err = fuse_fill_super_common(sb, &ctx); + err = fuse_fill_super_common(sb, &ctx, NULL); if (err < 0) goto err_free_fuse_devs; -- 2.26.2 _______________________________________________ Virtio-fs mailing list [email protected] https://www.redhat.com/mailman/listinfo/virtio-fs
