diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2011-11-16 21:43:59 -0500 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2011-11-16 22:00:34 -0500 |
commit | ea441d1104cf1efb471fa81bc91e9fd1e6ae29fd (patch) | |
tree | 32b7c4f7c78af47936a604e3f4e13e8e61f834a0 /fs | |
parent | c13344958780b4046305ee6235d686c846535529 (diff) |
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')
-rw-r--r-- | fs/btrfs/super.c | 35 | ||||
-rw-r--r-- | fs/namespace.c | 28 | ||||
-rw-r--r-- | fs/nfs/super.c | 30 |
3 files changed, 40 insertions, 53 deletions
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index cfbedd7755b0..17ee7fc5e64e 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c | |||
@@ -825,13 +825,9 @@ static char *setup_root_args(char *args) | |||
825 | static struct dentry *mount_subvol(const char *subvol_name, int flags, | 825 | static struct dentry *mount_subvol(const char *subvol_name, int flags, |
826 | const char *device_name, char *data) | 826 | const char *device_name, char *data) |
827 | { | 827 | { |
828 | struct super_block *s; | ||
829 | struct dentry *root; | 828 | struct dentry *root; |
830 | struct vfsmount *mnt; | 829 | struct vfsmount *mnt; |
831 | struct mnt_namespace *ns_private; | ||
832 | char *newargs; | 830 | char *newargs; |
833 | struct path path; | ||
834 | int error; | ||
835 | 831 | ||
836 | newargs = setup_root_args(data); | 832 | newargs = setup_root_args(data); |
837 | if (!newargs) | 833 | if (!newargs) |
@@ -842,36 +838,17 @@ static struct dentry *mount_subvol(const char *subvol_name, int flags, | |||
842 | if (IS_ERR(mnt)) | 838 | if (IS_ERR(mnt)) |
843 | return ERR_CAST(mnt); | 839 | return ERR_CAST(mnt); |
844 | 840 | ||
845 | ns_private = create_mnt_ns(mnt); | 841 | root = mount_subtree(mnt, subvol_name); |
846 | if (IS_ERR(ns_private)) | ||
847 | return ERR_CAST(ns_private); | ||
848 | |||
849 | /* | ||
850 | * This will trigger the automount of the subvol so we can just | ||
851 | * drop the mnt we have here and return the dentry that we | ||
852 | * found. | ||
853 | */ | ||
854 | error = vfs_path_lookup(mnt->mnt_root, mnt, subvol_name, | ||
855 | LOOKUP_FOLLOW, &path); | ||
856 | put_mnt_ns(ns_private); | ||
857 | if (error) | ||
858 | return ERR_PTR(error); | ||
859 | 842 | ||
860 | if (!is_subvolume_inode(path.dentry->d_inode)) { | 843 | if (!IS_ERR(root) && !is_subvolume_inode(root->d_inode)) { |
861 | path_put(&path); | 844 | struct super_block *s = root->d_sb; |
862 | error = -EINVAL; | 845 | dput(root); |
846 | root = ERR_PTR(-EINVAL); | ||
847 | deactivate_locked_super(s); | ||
863 | printk(KERN_ERR "btrfs: '%s' is not a valid subvolume\n", | 848 | printk(KERN_ERR "btrfs: '%s' is not a valid subvolume\n", |
864 | subvol_name); | 849 | subvol_name); |
865 | return ERR_PTR(-EINVAL); | ||
866 | } | 850 | } |
867 | 851 | ||
868 | /* Get a ref to the sb and the dentry we found and return it */ | ||
869 | s = path.mnt->mnt_sb; | ||
870 | atomic_inc(&s->s_active); | ||
871 | root = dget(path.dentry); | ||
872 | path_put(&path); | ||
873 | down_write(&s->s_umount); | ||
874 | |||
875 | return root; | 852 | return root; |
876 | } | 853 | } |
877 | 854 | ||
diff --git a/fs/namespace.c b/fs/namespace.c index aea4b7689840..50ee30345b4f 100644 --- a/fs/namespace.c +++ b/fs/namespace.c | |||
@@ -2490,6 +2490,34 @@ struct mnt_namespace *create_mnt_ns(struct vfsmount *mnt) | |||
2490 | } | 2490 | } |
2491 | EXPORT_SYMBOL(create_mnt_ns); | 2491 | EXPORT_SYMBOL(create_mnt_ns); |
2492 | 2492 | ||
2493 | struct dentry *mount_subtree(struct vfsmount *mnt, const char *name) | ||
2494 | { | ||
2495 | struct mnt_namespace *ns; | ||
2496 | struct path path; | ||
2497 | int err; | ||
2498 | |||
2499 | ns = create_mnt_ns(mnt); | ||
2500 | if (IS_ERR(ns)) | ||
2501 | return ERR_CAST(ns); | ||
2502 | |||
2503 | err = vfs_path_lookup(mnt->mnt_root, mnt, | ||
2504 | name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &path); | ||
2505 | |||
2506 | put_mnt_ns(ns); | ||
2507 | |||
2508 | if (err) | ||
2509 | return ERR_PTR(err); | ||
2510 | |||
2511 | /* trade a vfsmount reference for active sb one */ | ||
2512 | atomic_inc(&path.mnt->mnt_sb->s_active); | ||
2513 | mntput(path.mnt); | ||
2514 | /* lock the sucker */ | ||
2515 | down_write(&path.mnt->mnt_sb->s_umount); | ||
2516 | /* ... and return the root of (sub)tree on it */ | ||
2517 | return path.dentry; | ||
2518 | } | ||
2519 | EXPORT_SYMBOL(mount_subtree); | ||
2520 | |||
2493 | SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name, | 2521 | SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name, |
2494 | char __user *, type, unsigned long, flags, void __user *, data) | 2522 | char __user *, type, unsigned long, flags, void __user *, data) |
2495 | { | 2523 | { |
diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 46d69f38fd55..134777406ee3 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c | |||
@@ -2787,35 +2787,17 @@ static void nfs_referral_loop_unprotect(void) | |||
2787 | static struct dentry *nfs_follow_remote_path(struct vfsmount *root_mnt, | 2787 | static struct dentry *nfs_follow_remote_path(struct vfsmount *root_mnt, |
2788 | const char *export_path) | 2788 | const char *export_path) |
2789 | { | 2789 | { |
2790 | struct mnt_namespace *ns_private; | ||
2791 | struct super_block *s; | ||
2792 | struct dentry *dentry; | 2790 | struct dentry *dentry; |
2793 | struct path path; | 2791 | int ret = nfs_referral_loop_protect(); |
2794 | int ret; | ||
2795 | 2792 | ||
2796 | ns_private = create_mnt_ns(root_mnt); | 2793 | if (ret) { |
2797 | if (IS_ERR(ns_private)) | 2794 | mntput(root_mnt); |
2798 | return ERR_CAST(ns_private); | ||
2799 | |||
2800 | ret = nfs_referral_loop_protect(); | ||
2801 | if (ret == 0) { | ||
2802 | ret = vfs_path_lookup(root_mnt->mnt_root, root_mnt, | ||
2803 | export_path, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, | ||
2804 | &path); | ||
2805 | nfs_referral_loop_unprotect(); | ||
2806 | } | ||
2807 | |||
2808 | put_mnt_ns(ns_private); | ||
2809 | |||
2810 | if (ret != 0) | ||
2811 | return ERR_PTR(ret); | 2795 | return ERR_PTR(ret); |
2796 | } | ||
2812 | 2797 | ||
2813 | s = path.mnt->mnt_sb; | 2798 | dentry = mount_subtree(root_mnt, export_path); |
2814 | atomic_inc(&s->s_active); | 2799 | nfs_referral_loop_unprotect(); |
2815 | dentry = dget(path.dentry); | ||
2816 | 2800 | ||
2817 | path_put(&path); | ||
2818 | down_write(&s->s_umount); | ||
2819 | return dentry; | 2801 | return dentry; |
2820 | } | 2802 | } |
2821 | 2803 | ||