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.
Unified · Split
| @@ -825,13 +825,9 @@ static char *setup_root_args(char *args) · 824 unmodified lines | ||
| 825 | 825 | static struct dentry *mount_subvol(const char *subvol_name, int flags, |
| 826 | 826 | const char *device_name, char *data) |
| 827 | 827 | { |
| 828 | − struct super_block *s; | |
| 829 | 828 | struct dentry *root; |
| 830 | 829 | struct vfsmount *mnt; |
| 831 | − struct mnt_namespace *ns_private; | |
| 832 | 830 | char *newargs; |
| 833 | − struct path path; | |
| 834 | − int error; | |
| 835 | 831 | |
| 836 | 832 | newargs = setup_root_args(data); |
| 837 | 833 | if (!newargs) |
| @@ -842,36 +838,17 @@ static struct dentry *mount_subvol(const char *subvol_name, int flags, · 4 unmodified lines | ||
| 842 | 838 | if (IS_ERR(mnt)) |
| 843 | 839 | return ERR_CAST(mnt); |
| 844 | 840 | |
| 845 | − ns_private = create_mnt_ns(mnt); | |
| 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); | |
| 841 | + root = mount_subtree(mnt, subvol_name); | |
| 859 | 842 | |
| 860 | − if (!is_subvolume_inode(path.dentry->d_inode)) { | |
| 861 | − path_put(&path); | |
| 862 | − error = -EINVAL; | |
| 843 | + if (!IS_ERR(root) && !is_subvolume_inode(root->d_inode)) { | |
| 844 | + struct super_block *s = root->d_sb; | |
| 845 | + dput(root); | |
| 846 | + root = ERR_PTR(-EINVAL); | |
| 847 | + deactivate_locked_super(s); | |
| 863 | 848 | printk(KERN_ERR "btrfs: '%s' is not a valid subvolume\n", |
| 864 | 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 | 852 | return root; |
| 876 | 853 | } |
| 877 | 854 | |
| @@ -2490,6 +2490,34 @@ struct mnt_namespace *create_mnt_ns(struct vfsmount *mnt) · 2489 unmodified lines | ||
| 2490 | 2490 | } |
| 2491 | 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 | 2521 | SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name, |
| 2494 | 2522 | char __user *, type, unsigned long, flags, void __user *, data) |
| 2495 | 2523 | { |
| @@ -2787,35 +2787,17 @@ static void nfs_referral_loop_unprotect(void) · 2786 unmodified lines | ||
| 2787 | 2787 | static struct dentry *nfs_follow_remote_path(struct vfsmount *root_mnt, |
| 2788 | 2788 | const char *export_path) |
| 2789 | 2789 | { |
| 2790 | − struct mnt_namespace *ns_private; | |
| 2791 | − struct super_block *s; | |
| 2792 | 2790 | struct dentry *dentry; |
| 2793 | − struct path path; | |
| 2794 | − int ret; | |
| 2791 | + int ret = nfs_referral_loop_protect(); | |
| 2795 | 2792 | |
| 2796 | − ns_private = create_mnt_ns(root_mnt); | |
| 2797 | − if (IS_ERR(ns_private)) | |
| 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) | |
| 2793 | + if (ret) { | |
| 2794 | + mntput(root_mnt); | |
| 2811 | 2795 | return ERR_PTR(ret); |
| 2796 | + } | |
| 2812 | 2797 | |
| 2813 | − s = path.mnt->mnt_sb; | |
| 2814 | − atomic_inc(&s->s_active); | |
| 2815 | − dentry = dget(path.dentry); | |
| 2798 | + dentry = mount_subtree(root_mnt, export_path); | |
| 2799 | + nfs_referral_loop_unprotect(); | |
| 2816 | 2800 | |
| 2817 | − path_put(&path); | |
| 2818 | − down_write(&s->s_umount); | |
| 2819 | 2801 | return dentry; |
| 2820 | 2802 | } |
| 2821 | 2803 | |
| @@ -1886,6 +1886,7 @@ extern struct dentry *mount_single(struct file_system_type *fs_type, · 1885 unmodified lines | ||
| 1886 | 1886 | extern struct dentry *mount_nodev(struct file_system_type *fs_type, |
| 1887 | 1887 | int flags, void *data, |
| 1888 | 1888 | int (*fill_super)(struct super_block *, void *, int)); |
| 1889 | +extern struct dentry *mount_subtree(struct vfsmount *mnt, const char *path); | |
| 1889 | 1890 | void generic_shutdown_super(struct super_block *sb); |
| 1890 | 1891 | void kill_block_super(struct super_block *sb); |
| 1891 | 1892 | void kill_anon_super(struct super_block *sb); |