diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2011-11-17 02:43:59 (GMT) |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2011-11-17 03:00:34 (GMT) |
commit | ea441d1104cf1efb471fa81bc91e9fd1e6ae29fd (patch) | |
tree | 32b7c4f7c78af47936a604e3f4e13e8e61f834a0 /fs/namespace.c | |
parent | c13344958780b4046305ee6235d686c846535529 (diff) | |
download | linux-ea441d1104cf1efb471fa81bc91e9fd1e6ae29fd.tar.xz |
new helper: mount_subtree()
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>
Diffstat (limited to 'fs/namespace.c')
-rw-r--r-- | fs/namespace.c | 28 |
1 files changed, 28 insertions, 0 deletions
diff --git a/fs/namespace.c b/fs/namespace.c index aea4b768..50ee303 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -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) { |