Skip to content

Commit

Permalink
new helper: mount_subtree()
Browse files Browse the repository at this point in the history
takes vfsmount and relative path, does lookup within that vfsmount
(possibly triggering automounts) and returns the result as root
of subtree suitable for return by ->mount() (i.e. a reference to
dentry and an active reference to its superblock grabbed, superblock
locked exclusive).

btrfs and nfs switched to it instead of open-coding the sucker.

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
  • Loading branch information
Al Viro committed Nov 17, 2011
1 parent c133449 commit ea441d1
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 53 deletions.
35 changes: 6 additions & 29 deletions fs/btrfs/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -825,13 +825,9 @@ static char *setup_root_args(char *args)
static struct dentry *mount_subvol(const char *subvol_name, int flags,
const char *device_name, char *data)
{
struct super_block *s;
struct dentry *root;
struct vfsmount *mnt;
struct mnt_namespace *ns_private;
char *newargs;
struct path path;
int error;

newargs = setup_root_args(data);
if (!newargs)
Expand All @@ -842,36 +838,17 @@ static struct dentry *mount_subvol(const char *subvol_name, int flags,
if (IS_ERR(mnt))
return ERR_CAST(mnt);

ns_private = create_mnt_ns(mnt);
if (IS_ERR(ns_private))
return ERR_CAST(ns_private);

/*
* This will trigger the automount of the subvol so we can just
* drop the mnt we have here and return the dentry that we
* found.
*/
error = vfs_path_lookup(mnt->mnt_root, mnt, subvol_name,
LOOKUP_FOLLOW, &path);
put_mnt_ns(ns_private);
if (error)
return ERR_PTR(error);
root = mount_subtree(mnt, subvol_name);

if (!is_subvolume_inode(path.dentry->d_inode)) {
path_put(&path);
error = -EINVAL;
if (!IS_ERR(root) && !is_subvolume_inode(root->d_inode)) {
struct super_block *s = root->d_sb;
dput(root);
root = ERR_PTR(-EINVAL);
deactivate_locked_super(s);
printk(KERN_ERR "btrfs: '%s' is not a valid subvolume\n",
subvol_name);
return ERR_PTR(-EINVAL);
}

/* Get a ref to the sb and the dentry we found and return it */
s = path.mnt->mnt_sb;
atomic_inc(&s->s_active);
root = dget(path.dentry);
path_put(&path);
down_write(&s->s_umount);

return root;
}

Expand Down
28 changes: 28 additions & 0 deletions fs/namespace.c
Original file line number Diff line number Diff line change
Expand Up @@ -2490,6 +2490,34 @@ struct mnt_namespace *create_mnt_ns(struct vfsmount *mnt)
}
EXPORT_SYMBOL(create_mnt_ns);

struct dentry *mount_subtree(struct vfsmount *mnt, const char *name)
{
struct mnt_namespace *ns;
struct path path;
int err;

ns = create_mnt_ns(mnt);
if (IS_ERR(ns))
return ERR_CAST(ns);

err = vfs_path_lookup(mnt->mnt_root, mnt,
name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &path);

put_mnt_ns(ns);

if (err)
return ERR_PTR(err);

/* trade a vfsmount reference for active sb one */
atomic_inc(&path.mnt->mnt_sb->s_active);
mntput(path.mnt);
/* lock the sucker */
down_write(&path.mnt->mnt_sb->s_umount);
/* ... and return the root of (sub)tree on it */
return path.dentry;
}
EXPORT_SYMBOL(mount_subtree);

SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name,
char __user *, type, unsigned long, flags, void __user *, data)
{
Expand Down
30 changes: 6 additions & 24 deletions fs/nfs/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -2787,35 +2787,17 @@ static void nfs_referral_loop_unprotect(void)
static struct dentry *nfs_follow_remote_path(struct vfsmount *root_mnt,
const char *export_path)
{
struct mnt_namespace *ns_private;
struct super_block *s;
struct dentry *dentry;
struct path path;
int ret;
int ret = nfs_referral_loop_protect();

ns_private = create_mnt_ns(root_mnt);
if (IS_ERR(ns_private))
return ERR_CAST(ns_private);

ret = nfs_referral_loop_protect();
if (ret == 0) {
ret = vfs_path_lookup(root_mnt->mnt_root, root_mnt,
export_path, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT,
&path);
nfs_referral_loop_unprotect();
}

put_mnt_ns(ns_private);

if (ret != 0)
if (ret) {
mntput(root_mnt);
return ERR_PTR(ret);
}

s = path.mnt->mnt_sb;
atomic_inc(&s->s_active);
dentry = dget(path.dentry);
dentry = mount_subtree(root_mnt, export_path);
nfs_referral_loop_unprotect();

path_put(&path);
down_write(&s->s_umount);
return dentry;
}

Expand Down
1 change: 1 addition & 0 deletions include/linux/fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -1886,6 +1886,7 @@ extern struct dentry *mount_single(struct file_system_type *fs_type,
extern struct dentry *mount_nodev(struct file_system_type *fs_type,
int flags, void *data,
int (*fill_super)(struct super_block *, void *, int));
extern struct dentry *mount_subtree(struct vfsmount *mnt, const char *path);
void generic_shutdown_super(struct super_block *sb);
void kill_block_super(struct super_block *sb);
void kill_anon_super(struct super_block *sb);
Expand Down

0 comments on commit ea441d1

Please sign in to comment.