From patchwork Fri Mar 25 09:20:43 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sean Finney X-Patchwork-Id: 674162 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id p2UDhtkZ006719 for ; Wed, 30 Mar 2011 13:43:58 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755150Ab1C3Nn5 (ORCPT ); Wed, 30 Mar 2011 09:43:57 -0400 Received: from cobija.connexer.com ([66.93.22.232]:56103 "EHLO cobija.connexer.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1755936Ab1C3Nn4 (ORCPT ); Wed, 30 Mar 2011 09:43:56 -0400 Received: by cobija.connexer.com (Postfix, from userid 1000) id A7F6217CC1D; Wed, 30 Mar 2011 09:43:53 -0400 (EDT) Message-Id: <9eff2210c957a5d97e75fbabe5eae39cdbcc1860.1301492580.git.seanius@seanius.net> In-Reply-To: <258c7d7b9e8dbd73829c701e6494f91bfa707cb3.1301492580.git.seanius@seanius.net> References: <258c7d7b9e8dbd73829c701e6494f91bfa707cb3.1301492580.git.seanius@seanius.net> From: Sean Finney Date: Fri, 25 Mar 2011 10:20:43 +0100 Subject: [PATCH 2/4] Consolidate and extract similar DFS referral expansion code in cifs_mount To: linux-cifs@vger.kernel.org Sender: linux-cifs-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-cifs@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.6 (demeter1.kernel.org [140.211.167.41]); Wed, 30 Mar 2011 13:43:58 +0000 (UTC) diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 7e4ca38..6bf7fd6 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -2742,6 +2742,57 @@ build_unc_path_to_root(const struct smb_vol *volume_info, full_path[unc_len + cifs_sb->prepathlen] = 0; /* add trailing null */ return full_path; } + +/* + * Perform a dfs referral query for a share and (optionally) prefix + * + * If a referral is found, mount_data will be set to point at a newly + * allocated string containing updated options for the submount. + * Otherwise it will be left untouched. + * + * Returns the rc from get_dfs_path to the caller, which can be used to + * determine whether there were referrals. + */ +static int +expand_dfs_referral(int xid, struct cifs_ses *pSesInfo, + struct smb_vol *volume_info, struct cifs_sb_info *cifs_sb, + char **mount_data, int check_prefix) +{ + int rc; + unsigned int num_referrals = 0; + struct dfs_info3_param *referrals = NULL; + char *full_path = NULL, *ref_path = NULL, *mdata = NULL; + + full_path = build_unc_path_to_root(volume_info, cifs_sb); + if (IS_ERR(full_path)) + return PTR_ERR(full_path); + + /* For DFS paths, skip the first '\' of the UNC */ + ref_path = check_prefix ? full_path + 1 : volume_info->UNC + 1; + + rc = get_dfs_path(xid, pSesInfo , ref_path, cifs_sb->local_nls, + &num_referrals, &referrals, + cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); + + if (!rc && num_referrals > 0) { + char *fake_devname = NULL; + + mdata = cifs_compose_mount_options(cifs_sb->mountdata, + full_path + 1, referrals, + &fake_devname); + + free_dfs_info_array(referrals, num_referrals); + kfree(fake_devname); + + if (IS_ERR(mdata)) { + rc = PTR_ERR(mdata); + mdata = NULL; + } + *mount_data = mdata; + } + kfree(full_path); + return rc; +} #endif int @@ -2758,10 +2809,19 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, char *mount_data = mount_data_global; struct tcon_link *tlink; #ifdef CONFIG_CIFS_DFS_UPCALL - struct dfs_info3_param *referrals = NULL; - unsigned int num_referrals = 0; int referral_walks_count = 0; try_mount_again: + + /* cleanup activities if we're chasing a referral */ + if (referral_walks_count) { + if (tcon) + cifs_put_tcon(tcon); + else if (pSesInfo) + cifs_put_smb_ses(pSesInfo); + + cleanup_volume_info(&volume_info); + FreeXid(xid); + } #endif rc = 0; tcon = NULL; @@ -2876,49 +2936,17 @@ try_mount_again: remote_path_check: #ifdef CONFIG_CIFS_DFS_UPCALL /* - * Perform an unconditional check for whether there are - * DFS referrals for this path (without prefix), to provide - * some limited support for domain DFS referrals on w2k8 + * Perform an unconditional check for whether there are DFS + * referrals for this path without prefix, to provide support + * for DFS referrals from w2k8 servers which don't seem to respond + * with PATH_NOT_COVERED to requests that include the prefix. + * Chase the referral if found, otherwise continue normally. */ if (referral_walks_count == 0) { - cFYI(1, "Getting referral for: %s", volume_info->UNC); - rc = get_dfs_path(xid, pSesInfo , volume_info->UNC + 1, - cifs_sb->local_nls, &num_referrals, &referrals, - cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); - if (!rc && num_referrals > 0) { - char *fake_devname = NULL; - - full_path = build_unc_path_to_root(volume_info, - cifs_sb); - if (IS_ERR(full_path)) { - rc = PTR_ERR(full_path); - goto mount_fail_check; - } - - if (mount_data != mount_data_global) - kfree(mount_data); - - mount_data = cifs_compose_mount_options( - cifs_sb->mountdata, full_path + 1, - referrals, &fake_devname); - - free_dfs_info_array(referrals, num_referrals); - kfree(fake_devname); - - if (IS_ERR(mount_data)) { - rc = PTR_ERR(mount_data); - mount_data = NULL; - goto mount_fail_check; - } - - if (tcon) - cifs_put_tcon(tcon); - else if (pSesInfo) - cifs_put_smb_ses(pSesInfo); - - cleanup_volume_info(&volume_info); + int refrc = expand_dfs_referral(xid, pSesInfo, volume_info, + cifs_sb, &mount_data, false); + if (!refrc) { referral_walks_count++; - FreeXid(xid); goto try_mount_again; } } @@ -2957,46 +2985,19 @@ remote_path_check: if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) == 0) convert_delimiter(cifs_sb->prepath, CIFS_DIR_SEP(cifs_sb)); - full_path = build_unc_path_to_root(volume_info, cifs_sb); - if (IS_ERR(full_path)) { - rc = PTR_ERR(full_path); - goto mount_fail_check; - } - - cFYI(1, "Getting referral for: %s", full_path); - rc = get_dfs_path(xid, pSesInfo , full_path + 1, - cifs_sb->local_nls, &num_referrals, &referrals, - cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); - if (!rc && num_referrals > 0) { - char *fake_devname = NULL; - - if (mount_data != mount_data_global) - kfree(mount_data); - - mount_data = cifs_compose_mount_options( - cifs_sb->mountdata, full_path + 1, - referrals, &fake_devname); - free_dfs_info_array(referrals, num_referrals); - kfree(fake_devname); - kfree(full_path); - - if (IS_ERR(mount_data)) { - rc = PTR_ERR(mount_data); - mount_data = NULL; - goto mount_fail_check; - } + if (mount_data != mount_data_global) + kfree(mount_data); - if (tcon) - cifs_put_tcon(tcon); - else if (pSesInfo) - cifs_put_smb_ses(pSesInfo); + rc = expand_dfs_referral(xid, pSesInfo, volume_info, cifs_sb, + &mount_data, true); - cleanup_volume_info(&volume_info); + if (!rc) { referral_walks_count++; - FreeXid(xid); goto try_mount_again; } + mount_data = NULL; + goto mount_fail_check; #else /* No DFS support, return error on mount */ rc = -EOPNOTSUPP; #endif @@ -3029,7 +3030,7 @@ remote_path_check: mount_fail_check: /* on error free sesinfo and tcon struct if needed */ if (rc) { - if (mount_data != mount_data_global) + if (mount_data != NULL && mount_data != mount_data_global) kfree(mount_data); /* If find_unc succeeded then rc == 0 so we can not end */ /* up accidently freeing someone elses tcon struct */