CIFSGetDFSRefer cleanup + dfs_referral_level_3 fixed to conform REFERRAL_V3 the MS...
Igor Mammedov [Fri, 16 May 2008 09:06:30 +0000 (13:06 +0400)]
Signed-off-by: Igor Mammedov <niallain@gmail.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>

fs/cifs/cifspdu.h
fs/cifs/cifssmb.c

index c43bf4b..93d5ee0 100644 (file)
@@ -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 {
index fc29738..6f8ed93 100644 (file)
@@ -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);