Commit 266eb3f2 authored by Greg Kurz's avatar Greg Kurz Committed by Miklos Szeredi
Browse files

fuse: Call vfs_get_tree() for submounts



We recently fixed an infinite loop by setting the SB_BORN flag on
submounts along with the write barrier needed by super_cache_count().
This is the job of vfs_get_tree() and FUSE shouldn't have to care
about the barrier at all.

Split out some code from fuse_dentry_automount() to the dedicated
fuse_get_tree_submount() handler for submounts and call vfs_get_tree().

Signed-off-by: default avatarGreg Kurz <groug@kaod.org>
Reviewed-by: default avatarMax Reitz <mreitz@redhat.com>
Signed-off-by: default avatarMiklos Szeredi <mszeredi@redhat.com>
parent fe0a7bd8
Loading
Loading
Loading
Loading
+5 −48
Original line number Diff line number Diff line
@@ -309,12 +309,8 @@ static int fuse_dentry_delete(const struct dentry *dentry)
static struct vfsmount *fuse_dentry_automount(struct path *path)
{
	struct fs_context *fsc;
	struct fuse_mount *parent_fm = get_fuse_mount_super(path->mnt->mnt_sb);
	struct fuse_conn *fc = parent_fm->fc;
	struct fuse_mount *fm;
	struct vfsmount *mnt;
	struct fuse_inode *mp_fi = get_fuse_inode(d_inode(path->dentry));
	struct super_block *sb;
	int err;

	fsc = fs_context_for_submount(path->mnt->mnt_sb->s_type, path->dentry);
@@ -323,48 +319,15 @@ static struct vfsmount *fuse_dentry_automount(struct path *path)
		goto out;
	}

	err = -ENOMEM;
	fm = kzalloc(sizeof(struct fuse_mount), GFP_KERNEL);
	if (!fm)
		goto out_put_fsc;
	/* Pass the FUSE inode of the mount for fuse_get_tree_submount() */
	fsc->fs_private = mp_fi;

	fsc->s_fs_info = fm;
	sb = sget_fc(fsc, NULL, set_anon_super_fc);
	if (IS_ERR(sb)) {
		err = PTR_ERR(sb);
		kfree(fm);
	err = vfs_get_tree(fsc);
	if (err)
		goto out_put_fsc;
	}
	fm->fc = fuse_conn_get(fc);

	/* Initialize superblock, making @mp_fi its root */
	err = fuse_fill_super_submount(sb, mp_fi);
	if (err) {
		fuse_conn_put(fc);
		kfree(fm);
		sb->s_fs_info = NULL;
		goto out_put_sb;
	}

	down_write(&fc->killsb);
	list_add_tail(&fm->fc_entry, &fc->mounts);
	up_write(&fc->killsb);

	sb->s_flags |= SB_ACTIVE;
	fsc->root = dget(sb->s_root);

	/*
	 * FIXME: setting SB_BORN requires a write barrier for
	 *        super_cache_count(). We should actually come
	 *        up with a proper ->get_tree() implementation
	 *        for submounts and call vfs_get_tree() to take
	 *        care of the write barrier.
	 */
	smp_wmb();
	sb->s_flags |= SB_BORN;

	/* We are done configuring the superblock, so unlock it */
	up_write(&sb->s_umount);
	up_write(&fsc->root->d_sb->s_umount);

	/* Create the submount */
	mnt = vfs_create_mount(fsc);
@@ -376,12 +339,6 @@ static struct vfsmount *fuse_dentry_automount(struct path *path)
	put_fs_context(fsc);
	return mnt;

out_put_sb:
	/*
	 * Only jump here when fsc->root is NULL and sb is still locked
	 * (otherwise put_fs_context() will put the superblock)
	 */
	deactivate_locked_super(sb);
out_put_fsc:
	put_fs_context(fsc);
out:
+36 −0
Original line number Diff line number Diff line
@@ -1353,8 +1353,44 @@ int fuse_fill_super_submount(struct super_block *sb,
	return 0;
}

/* Filesystem context private data holds the FUSE inode of the mount point */
static int fuse_get_tree_submount(struct fs_context *fsc)
{
	struct fuse_mount *fm;
	struct fuse_inode *mp_fi = fsc->fs_private;
	struct fuse_conn *fc = get_fuse_conn(&mp_fi->inode);
	struct super_block *sb;
	int err;

	fm = kzalloc(sizeof(struct fuse_mount), GFP_KERNEL);
	if (!fm)
		return -ENOMEM;

	fsc->s_fs_info = fm;
	sb = sget_fc(fsc, NULL, set_anon_super_fc);
	if (IS_ERR(sb)) {
		kfree(fm);
		return PTR_ERR(sb);
	}
	fm->fc = fuse_conn_get(fc);

	/* Initialize superblock, making @mp_fi its root */
	err = fuse_fill_super_submount(sb, mp_fi);
	if (err) {
		fuse_conn_put(fc);
		kfree(fm);
		sb->s_fs_info = NULL;
		deactivate_locked_super(sb);
		return err;
	}

	down_write(&fc->killsb);
	list_add_tail(&fm->fc_entry, &fc->mounts);
	up_write(&fc->killsb);

	sb->s_flags |= SB_ACTIVE;
	fsc->root = dget(sb->s_root);

	return 0;
}