Re: [DFS support patchset: ] [2/5]: attempt 2: CIFSGetDFSRefer is rewritten to return more information about DFS refferal.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Steve French wrote:
> Was this current?

Relatively yes.

I've made cleanup patch to CIFSGetDFSRefer on top of what you've recently
commited. It should speedup getting DFS to the working state.
As minimum, we will get rid of OOPs-es.
Please review attached patch.


> FYI - I removed the section in link.c rather than returning ENOSYS
> 
> On Tue, Dec 18, 2007 at 9:15 AM, Q (Igor Mammedov)
> <qwerty0987654321@xxxxxxx> wrote:
>>  1. Now CIFSGetDFSRefer returns array of struct dfs_info3_param that has
>>    necessary parameters for handling connect to refferal storage node.
>>  2. Some formating changes to CIFSGetDFSRefer for the sake of checkpatch.
<cut>


-- 

Best regards,

-------------------------
Igor Mammedov,
niallain "at" gmail.com




>From 8914b72c7c9a0e9c1f82a994437e4fa91e2981fc Mon Sep 17 00:00:00 2001
From: Igor Mammedov <niallain@xxxxxxxxx>
Date: Fri, 16 May 2008 13:06:30 +0400
Subject: [PATCH] CIFSGetDFSRefer cleanup
 + dfs_referral_level_3 fixed to conform REFERRAL_V3 the MS-DFSC spec.

Signed-off-by: Igor Mammedov <niallain@xxxxxxxxx>
---
 fs/cifs/cifspdu.h |   16 ++---
 fs/cifs/cifssmb.c |  205 ++++++++++++++++++++++++++++++++++-------------------
 2 files changed, 139 insertions(+), 82 deletions(-)

diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h
index c43bf4b..93d5ee0 100644
--- a/fs/cifs/cifspdu.h
+++ b/fs/cifs/cifspdu.h
@@ -1906,17 +1906,15 @@ typedef struct smb_com_transaction2_get_dfs_refer_req {
 
 typedef struct dfs_referral_level_3 {
 	__le16 VersionNumber;
-	__le16 ReferralSize;
-	__le16 ServerType;	/* 0x0001 = CIFS server */
-	__le16 ReferralFlags;	/* or proximity - not clear which since it is
-				   always set to zero - SNIA spec says 0x01
-				   means strip off PathConsumed chars before
-				   submitting RequestFileName to remote node */
-	__le16 TimeToLive;
-	__le16 Proximity;
+	__le16 Size;
+	__le16 ServerType; /* 0x0001 = root targets; 0x0000 = link targets */
+	__le16 ReferralEntryFlags; /* 0x0200 bit set only for domain
+				      or DC referral responce */
+	__le32 TimeToLive;
 	__le16 DfsPathOffset;
 	__le16 DfsAlternatePathOffset;
-	__le16 NetworkAddressOffset;
+	__le16 NetworkAddressOffset; /* offset of the link target */
+	__le16 ServiceSiteGuid;
 } __attribute__((packed)) REFERRAL3;
 
 typedef struct smb_com_transaction_get_dfs_refer_rsp {
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index fc29738..6f8ed93 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -81,6 +81,39 @@ static struct {
 #endif /* CONFIG_CIFS_WEAK_PW_HASH */
 #endif /* CIFS_POSIX */
 
+/* Allocates buffer into dst and copies smb string from src to it.
+ * caller is responsible for freeing dst if function returned 0.
+ * returns:
+ * 	on success - 0
+ *	on failure - errno
+ */
+static int
+cifs_strncpy_to_host(char **dst, const char *src, const int maxlen,
+		 const bool is_unicode, const struct nls_table *nls_codepage)
+{
+	int plen;
+
+	if (is_unicode) {
+		plen = UniStrnlen((wchar_t *)src, maxlen);
+		*dst = kmalloc(plen + 2, GFP_KERNEL);
+		if (!*dst)
+			goto cifs_strncpy_to_host_ErrExit;
+		cifs_strfromUCS_le(*dst, (__le16 *)src, plen, nls_codepage);
+	} else {
+		plen = strnlen(src, maxlen);
+		*dst = kmalloc(plen + 2, GFP_KERNEL);
+		if (!*dst)
+			goto cifs_strncpy_to_host_ErrExit;
+		strncpy(*dst, src, plen);
+	}
+	(*dst)[plen] = 0;
+	return 0;
+
+cifs_strncpy_to_host_ErrExit:
+	cERROR(1, ("Failed to allocate buffer for string\n"));
+	return -ENOMEM;
+}
+
 
 /* Mark as invalid, all open files on tree connections since they
    were closed when session to server was lost */
@@ -3867,6 +3900,96 @@ GetInodeNumOut:
 	return rc;
 }
 
+/* parses DFS refferal V3 structure
+ * caller is responsible for freeing target_nodes
+ * returns:
+ * 	on success - 0
+ *	on failure - errno
+ */
+static int
+parse_DFS_REFERRALS(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr,
+		unsigned int *num_of_nodes,
+		struct dfs_info3_param **target_nodes,
+		const struct nls_table *nls_codepage)
+{
+	int i, rc = 0;
+	char *data_end;
+	bool is_unicode;
+	struct dfs_referral_level_3 *ref;
+
+	is_unicode = pSMBr->hdr.Flags2 & SMBFLG2_UNICODE;
+	*num_of_nodes = le16_to_cpu(pSMBr->NumberOfReferrals);
+
+	if (*num_of_nodes < 1) {
+		cERROR(1, ("num_referrals: must be at least > 0,"
+			"but we get num_referrals = %d\n", *num_of_nodes));
+		rc = -EINVAL;
+		goto parse_DFS_REFERRALS_exit;
+	}
+
+	ref = (struct dfs_referral_level_3 *) &(pSMBr->referrals);
+	if (ref->VersionNumber != 3) {
+		cERROR(1, ("Referrals of V%d version are not supported,"
+			"should be V3", ref->VersionNumber));
+		rc = -EINVAL;
+		goto parse_DFS_REFERRALS_exit;
+	}
+
+	/* get the upper boundary of the resp buffer */
+	data_end = (char *)(&(pSMBr->PathConsumed)) +
+				le16_to_cpu(pSMBr->t2.DataCount);
+
+	cFYI(1, ("num_referrals: %d dfs flags: 0x%x ... \n",
+			*num_of_nodes,
+			le16_to_cpu(pSMBr->DFSFlags)));
+
+	*target_nodes = kzalloc(sizeof(struct dfs_info3_param) *
+			*num_of_nodes, GFP_KERNEL);
+	if (*target_nodes == NULL) {
+		cERROR(1, ("Failed to allocate buffer for target_nodes\n"));
+		rc = -ENOMEM;
+		goto parse_DFS_REFERRALS_exit;
+	}
+
+	/* collect neccessary data from referrals */
+	for (i = 0; i < *num_of_nodes; i++) {
+		char *temp;
+		int max_len;
+		struct dfs_info3_param *node = (*target_nodes)+i;
+
+		node->flags = le16_to_cpu(pSMBr->DFSFlags);
+		node->path_consumed = le16_to_cpu(pSMBr->PathConsumed);
+		node->server_type = le16_to_cpu(ref->ServerType);
+		node->ref_flag = le16_to_cpu(ref->ReferralEntryFlags);
+
+		/* copy DfsPath */
+		temp = (char *)ref + le16_to_cpu(ref->DfsPathOffset);
+		max_len = data_end - temp;
+		rc = cifs_strncpy_to_host(&(node->path_name), temp,
+					max_len, is_unicode, nls_codepage);
+		if (rc)
+			goto parse_DFS_REFERRALS_exit;
+
+		/* copy link target UNC */
+		temp = (char *)ref + le16_to_cpu(ref->NetworkAddressOffset);
+		max_len = data_end - temp;
+		rc = cifs_strncpy_to_host(&(node->node_name), temp,
+					max_len, is_unicode, nls_codepage);
+		if (rc)
+			goto parse_DFS_REFERRALS_exit;
+
+		ref += ref->Size;
+	}
+
+parse_DFS_REFERRALS_exit:
+	if (rc) {
+		free_dfs_info_array(*target_nodes, *num_of_nodes);
+		*target_nodes = NULL;
+		*num_of_nodes = 0;
+	}
+	return rc;
+}
+
 int
 CIFSGetDFSRefer(const int xid, struct cifsSesInfo *ses,
 		const unsigned char *searchName,
@@ -3877,12 +4000,9 @@ CIFSGetDFSRefer(const int xid, struct cifsSesInfo *ses,
 /* TRANS2_GET_DFS_REFERRAL */
 	TRANSACTION2_GET_DFS_REFER_REQ *pSMB = NULL;
 	TRANSACTION2_GET_DFS_REFER_RSP *pSMBr = NULL;
-	struct dfs_referral_level_3 *referrals = NULL;
 	int rc = 0;
 	int bytes_returned;
 	int name_len;
-	unsigned int i;
-	char *temp;
 	__u16 params, byte_count;
 	*num_of_nodes = 0;
 	*target_nodes = NULL;
@@ -3960,80 +4080,19 @@ getDFSRetry:
 	rc = validate_t2((struct smb_t2_rsp *)pSMBr);
 
 	/* BB Also check if enough total bytes returned? */
-	if (rc || (pSMBr->ByteCount < 17))
+	if (rc || (pSMBr->ByteCount < 17)) {
 		rc = -EIO;      /* bad smb */
-	else {
-		__u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
-		__u16 data_count = le16_to_cpu(pSMBr->t2.DataCount);
-
-		cFYI(1, ("Decoding GetDFSRefer response BCC: %d  Offset %d",
-			 pSMBr->ByteCount, data_offset));
-		referrals =
-		    (struct dfs_referral_level_3 *)
-				(8 /* sizeof start of data block */ +
-				data_offset +
-				(char *) &pSMBr->hdr.Protocol);
-		cFYI(1, ("num_referrals: %d dfs flags: 0x%x ... \n"
-			"for referral one refer size: 0x%x srv "
-			"type: 0x%x refer flags: 0x%x ttl: 0x%x",
-			le16_to_cpu(pSMBr->NumberOfReferrals),
-			le16_to_cpu(pSMBr->DFSFlags),
-			le16_to_cpu(referrals->ReferralSize),
-			le16_to_cpu(referrals->ServerType),
-			le16_to_cpu(referrals->ReferralFlags),
-			le16_to_cpu(referrals->TimeToLive)));
-		/* BB This field is actually two bytes in from start of
-		   data block so we could do safety check that DataBlock
-		   begins at address of pSMBr->NumberOfReferrals */
-		*num_of_nodes = le16_to_cpu(pSMBr->NumberOfReferrals);
-
-		/* BB Fix below so can return more than one referral */
-		if (*num_of_nodes > 1)
-			*num_of_nodes = 1;
-
-		/* get the length of the strings describing refs */
-		name_len = 0;
-		for (i = 0; i < *num_of_nodes; i++) {
-			/* make sure that DfsPathOffset not past end */
-			__u16 offset = le16_to_cpu(referrals->DfsPathOffset);
-			if (offset > data_count) {
-				/* if invalid referral, stop here and do
-				not try to copy any more */
-				*num_of_nodes = i;
-				break;
-			}
-			temp = ((char *)referrals) + offset;
+		goto GetDFSRefExit;
+	}
 
-			if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) {
-				name_len += UniStrnlen((wchar_t *)temp,
-							data_count);
-			} else {
-				name_len += strnlen(temp, data_count);
-			}
-			referrals++;
-			/* BB add check that referral pointer does
-			   not fall off end PDU */
-		}
-		/* BB add check for name_len bigger than bcc */
-		*target_nodes =
-			kmalloc(name_len+1+(*num_of_nodes),
-				GFP_KERNEL);
-		if (*target_nodes == NULL) {
-			rc = -ENOMEM;
-			goto GetDFSRefExit;
-		}
+	cFYI(1, ("Decoding GetDFSRefer response BCC: %d  Offset %d",
+				pSMBr->ByteCount,
+				le16_to_cpu(pSMBr->t2.DataOffset)));
 
-		referrals = (struct dfs_referral_level_3 *)
-				(8 /* sizeof data hdr */ + data_offset +
-				(char *) &pSMBr->hdr.Protocol);
+	/* parse returned result into more usable form */
+	rc = parse_DFS_REFERRALS(pSMBr, num_of_nodes,
+				 target_nodes, nls_codepage);
 
-		for (i = 0; i < *num_of_nodes; i++) {
-			temp = ((char *)referrals) +
-				  le16_to_cpu(referrals->DfsPathOffset);
-			/*  BB update target_uncs pointers */
-			referrals++;
-		}
-	}
 GetDFSRefExit:
 	if (pSMB)
 		cifs_buf_release(pSMB);
-- 
1.5.3.7


[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [Samba]     [Device Mapper]     [CEPH Development]
  Powered by Linux