diff options
Diffstat (limited to 'fs/cifs/connect.c')
-rw-r--r-- | fs/cifs/connect.c | 105 |
1 files changed, 69 insertions, 36 deletions
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 2b511991187a..b6c6a3629b07 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c | |||
@@ -2745,6 +2745,57 @@ build_unc_path_to_root(const struct smb_vol *volume_info, | |||
2745 | full_path[unc_len + cifs_sb->prepathlen] = 0; /* add trailing null */ | 2745 | full_path[unc_len + cifs_sb->prepathlen] = 0; /* add trailing null */ |
2746 | return full_path; | 2746 | return full_path; |
2747 | } | 2747 | } |
2748 | |||
2749 | /* | ||
2750 | * Perform a dfs referral query for a share and (optionally) prefix | ||
2751 | * | ||
2752 | * If a referral is found, mount_data will be set to point at a newly | ||
2753 | * allocated string containing updated options for the submount. | ||
2754 | * Otherwise it will be left untouched. | ||
2755 | * | ||
2756 | * Returns the rc from get_dfs_path to the caller, which can be used to | ||
2757 | * determine whether there were referrals. | ||
2758 | */ | ||
2759 | static int | ||
2760 | expand_dfs_referral(int xid, struct cifsSesInfo *pSesInfo, | ||
2761 | struct smb_vol *volume_info, struct cifs_sb_info *cifs_sb, | ||
2762 | char **mount_data, int check_prefix) | ||
2763 | { | ||
2764 | int rc; | ||
2765 | unsigned int num_referrals = 0; | ||
2766 | struct dfs_info3_param *referrals = NULL; | ||
2767 | char *full_path = NULL, *ref_path = NULL, *mdata = NULL; | ||
2768 | |||
2769 | full_path = build_unc_path_to_root(volume_info, cifs_sb); | ||
2770 | if (IS_ERR(full_path)) | ||
2771 | return PTR_ERR(full_path); | ||
2772 | |||
2773 | /* For DFS paths, skip the first '\' of the UNC */ | ||
2774 | ref_path = check_prefix ? full_path + 1 : volume_info->UNC + 1; | ||
2775 | |||
2776 | rc = get_dfs_path(xid, pSesInfo , ref_path, cifs_sb->local_nls, | ||
2777 | &num_referrals, &referrals, | ||
2778 | cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
2779 | |||
2780 | if (!rc && num_referrals > 0) { | ||
2781 | char *fake_devname = NULL; | ||
2782 | |||
2783 | mdata = cifs_compose_mount_options(cifs_sb->mountdata, | ||
2784 | full_path + 1, referrals, | ||
2785 | &fake_devname); | ||
2786 | |||
2787 | free_dfs_info_array(referrals, num_referrals); | ||
2788 | kfree(fake_devname); | ||
2789 | |||
2790 | if (IS_ERR(mdata)) { | ||
2791 | rc = PTR_ERR(mdata); | ||
2792 | mdata = NULL; | ||
2793 | } | ||
2794 | *mount_data = mdata; | ||
2795 | } | ||
2796 | kfree(full_path); | ||
2797 | return rc; | ||
2798 | } | ||
2748 | #endif | 2799 | #endif |
2749 | 2800 | ||
2750 | int | 2801 | int |
@@ -2761,10 +2812,19 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, | |||
2761 | char *mount_data = mount_data_global; | 2812 | char *mount_data = mount_data_global; |
2762 | struct tcon_link *tlink; | 2813 | struct tcon_link *tlink; |
2763 | #ifdef CONFIG_CIFS_DFS_UPCALL | 2814 | #ifdef CONFIG_CIFS_DFS_UPCALL |
2764 | struct dfs_info3_param *referrals = NULL; | ||
2765 | unsigned int num_referrals = 0; | ||
2766 | int referral_walks_count = 0; | 2815 | int referral_walks_count = 0; |
2767 | try_mount_again: | 2816 | try_mount_again: |
2817 | |||
2818 | /* cleanup activities if we're chasing a referral */ | ||
2819 | if (referral_walks_count) { | ||
2820 | if (tcon) | ||
2821 | cifs_put_tcon(tcon); | ||
2822 | else if (pSesInfo) | ||
2823 | cifs_put_smb_ses(pSesInfo); | ||
2824 | |||
2825 | cleanup_volume_info(&volume_info); | ||
2826 | FreeXid(xid); | ||
2827 | } | ||
2768 | #endif | 2828 | #endif |
2769 | rc = 0; | 2829 | rc = 0; |
2770 | tcon = NULL; | 2830 | tcon = NULL; |
@@ -2910,46 +2970,19 @@ remote_path_check: | |||
2910 | if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) == 0) | 2970 | if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) == 0) |
2911 | convert_delimiter(cifs_sb->prepath, | 2971 | convert_delimiter(cifs_sb->prepath, |
2912 | CIFS_DIR_SEP(cifs_sb)); | 2972 | CIFS_DIR_SEP(cifs_sb)); |
2913 | full_path = build_unc_path_to_root(volume_info, cifs_sb); | ||
2914 | if (IS_ERR(full_path)) { | ||
2915 | rc = PTR_ERR(full_path); | ||
2916 | goto mount_fail_check; | ||
2917 | } | ||
2918 | 2973 | ||
2919 | cFYI(1, "Getting referral for: %s", full_path); | 2974 | if (mount_data != mount_data_global) |
2920 | rc = get_dfs_path(xid, pSesInfo , full_path + 1, | 2975 | kfree(mount_data); |
2921 | cifs_sb->local_nls, &num_referrals, &referrals, | ||
2922 | cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); | ||
2923 | if (!rc && num_referrals > 0) { | ||
2924 | char *fake_devname = NULL; | ||
2925 | |||
2926 | if (mount_data != mount_data_global) | ||
2927 | kfree(mount_data); | ||
2928 | |||
2929 | mount_data = cifs_compose_mount_options( | ||
2930 | cifs_sb->mountdata, full_path + 1, | ||
2931 | referrals, &fake_devname); | ||
2932 | |||
2933 | free_dfs_info_array(referrals, num_referrals); | ||
2934 | kfree(fake_devname); | ||
2935 | kfree(full_path); | ||
2936 | |||
2937 | if (IS_ERR(mount_data)) { | ||
2938 | rc = PTR_ERR(mount_data); | ||
2939 | mount_data = NULL; | ||
2940 | goto mount_fail_check; | ||
2941 | } | ||
2942 | 2976 | ||
2943 | if (tcon) | 2977 | rc = expand_dfs_referral(xid, pSesInfo, volume_info, cifs_sb, |
2944 | cifs_put_tcon(tcon); | 2978 | &mount_data, true); |
2945 | else if (pSesInfo) | ||
2946 | cifs_put_smb_ses(pSesInfo); | ||
2947 | 2979 | ||
2948 | cleanup_volume_info(&volume_info); | 2980 | if (!rc) { |
2949 | referral_walks_count++; | 2981 | referral_walks_count++; |
2950 | FreeXid(xid); | ||
2951 | goto try_mount_again; | 2982 | goto try_mount_again; |
2952 | } | 2983 | } |
2984 | mount_data = NULL; | ||
2985 | goto mount_fail_check; | ||
2953 | #else /* No DFS support, return error on mount */ | 2986 | #else /* No DFS support, return error on mount */ |
2954 | rc = -EOPNOTSUPP; | 2987 | rc = -EOPNOTSUPP; |
2955 | #endif | 2988 | #endif |