aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/cifs/connect.c105
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 */
2759static int
2760expand_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
2750int 2801int
@@ -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;
2767try_mount_again: 2816try_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