CIFS: move DFS response parsing out of SMB1 code
authorAurelien Aptel <aaptel@suse.com>
Mon, 13 Feb 2017 15:03:47 +0000 (16:03 +0100)
committerSteve French <smfrench@gmail.com>
Thu, 2 Mar 2017 04:26:10 +0000 (22:26 -0600)
since the DFS payload is not tied to the SMB version we can:
* isolate the DFS payload in its own struct, and include that struct in
  packet structs
* move the function that parses the response to misc.c and make it work
  on the new DFS payload struct (add payload size and utf16 flag as a
  result).

Signed-off-by: Aurelien Aptel <aaptel@suse.com>
Acked-by: Pavel Shilovsky <pshilov@microsoft.com>
Signed-off-by: Steve French <smfrench@gmail.com>
fs/cifs/cifspdu.h
fs/cifs/cifsproto.h
fs/cifs/cifssmb.c
fs/cifs/misc.c

index f5b87303ce46d50abab41e6017c33cecb06194bb..1ce733f3582f66702c18db771fac1416d702a706 100644 (file)
@@ -2086,17 +2086,21 @@ typedef struct dfs_referral_level_3 { /* version 4 is same, + one flag bit */
        __u8   ServiceSiteGuid[16];  /* MBZ, ignored */
 } __attribute__((packed)) REFERRAL3;
 
-typedef struct smb_com_transaction_get_dfs_refer_rsp {
-       struct smb_hdr hdr;     /* wct = 10 */
-       struct trans2_resp t2;
-       __u16 ByteCount;
-       __u8 Pad;
+struct get_dfs_referral_rsp {
        __le16 PathConsumed;
        __le16 NumberOfReferrals;
        __le32 DFSFlags;
        REFERRAL3 referrals[1]; /* array of level 3 dfs_referral structures */
        /* followed by the strings pointed to by the referral structures */
-} __attribute__((packed)) TRANSACTION2_GET_DFS_REFER_RSP;
+} __packed;
+
+typedef struct smb_com_transaction_get_dfs_refer_rsp {
+       struct smb_hdr hdr;     /* wct = 10 */
+       struct trans2_resp t2;
+       __u16 ByteCount;
+       __u8 Pad;
+       struct get_dfs_referral_rsp dfs_data;
+} __packed TRANSACTION2_GET_DFS_REFER_RSP;
 
 /* DFS Flags */
 #define DFSREF_REFERRAL_SERVER  0x00000001 /* all targets are DFS roots */
index 406d2c10ba78d0e53d5754fcb17d08586e534cdf..c0978304528841138a2e5fda886bd666b03bacff 100644 (file)
@@ -284,6 +284,11 @@ extern int get_dfs_path(const unsigned int xid, struct cifs_ses *ses,
                        const struct nls_table *nls_codepage,
                        unsigned int *num_referrals,
                        struct dfs_info3_param **referrals, int remap);
+extern int parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size,
+                              unsigned int *num_of_nodes,
+                              struct dfs_info3_param **target_nodes,
+                              const struct nls_table *nls_codepage, int remap,
+                              const char *searchName, bool is_unicode);
 extern void reset_cifs_unix_caps(unsigned int xid, struct cifs_tcon *tcon,
                                 struct cifs_sb_info *cifs_sb,
                                 struct smb_vol *vol);
index f5099fb8a22f14e4c1ca2b18ff135d6b29de8e01..5005c7995b6a1a29c59a6cd6628fd16b2864462e 100644 (file)
@@ -4786,117 +4786,6 @@ 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 remap,
-               const char *searchName)
-{
-       int i, rc = 0;
-       char *data_end;
-       bool is_unicode;
-       struct dfs_referral_level_3 *ref;
-
-       if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
-               is_unicode = true;
-       else
-               is_unicode = false;
-       *num_of_nodes = le16_to_cpu(pSMBr->NumberOfReferrals);
-
-       if (*num_of_nodes < 1) {
-               cifs_dbg(VFS, "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 != cpu_to_le16(3)) {
-               cifs_dbg(VFS, "Referrals of V%d version are not supported, should be V3\n",
-                        le16_to_cpu(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);
-
-       cifs_dbg(FYI, "num_referrals: %d dfs flags: 0x%x ...\n",
-                *num_of_nodes, le32_to_cpu(pSMBr->DFSFlags));
-
-       *target_nodes = kcalloc(*num_of_nodes, sizeof(struct dfs_info3_param),
-                               GFP_KERNEL);
-       if (*target_nodes == NULL) {
-               rc = -ENOMEM;
-               goto parse_DFS_referrals_exit;
-       }
-
-       /* collect necessary 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 = le32_to_cpu(pSMBr->DFSFlags);
-               if (is_unicode) {
-                       __le16 *tmp = kmalloc(strlen(searchName)*2 + 2,
-                                               GFP_KERNEL);
-                       if (tmp == NULL) {
-                               rc = -ENOMEM;
-                               goto parse_DFS_referrals_exit;
-                       }
-                       cifsConvertToUTF16((__le16 *) tmp, searchName,
-                                          PATH_MAX, nls_codepage, remap);
-                       node->path_consumed = cifs_utf16_bytes(tmp,
-                                       le16_to_cpu(pSMBr->PathConsumed),
-                                       nls_codepage);
-                       kfree(tmp);
-               } else
-                       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;
-               node->path_name = cifs_strndup_from_utf16(temp, max_len,
-                                               is_unicode, nls_codepage);
-               if (!node->path_name) {
-                       rc = -ENOMEM;
-                       goto parse_DFS_referrals_exit;
-               }
-
-               /* copy link target UNC */
-               temp = (char *)ref + le16_to_cpu(ref->NetworkAddressOffset);
-               max_len = data_end - temp;
-               node->node_name = cifs_strndup_from_utf16(temp, max_len,
-                                               is_unicode, nls_codepage);
-               if (!node->node_name) {
-                       rc = -ENOMEM;
-                       goto parse_DFS_referrals_exit;
-               }
-
-               ref++;
-       }
-
-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 unsigned int xid, struct cifs_ses *ses,
                const char *search_name, struct dfs_info3_param **target_nodes,
@@ -4993,9 +4882,11 @@ getDFSRetry:
                 get_bcc(&pSMBr->hdr), le16_to_cpu(pSMBr->t2.DataOffset));
 
        /* parse returned result into more usable form */
-       rc = parse_DFS_referrals(pSMBr, num_of_nodes,
-                                target_nodes, nls_codepage, remap,
-                                search_name);
+       rc = parse_dfs_referrals(&pSMBr->dfs_data,
+                                le16_to_cpu(pSMBr->t2.DataCount),
+                                num_of_nodes, target_nodes, nls_codepage,
+                                remap, search_name,
+                                pSMBr->hdr.Flags2 & SMBFLG2_UNICODE);
 
 GetDFSRefExit:
        cifs_buf_release(pSMB);
index c6729156f9a00cf69938f8ec66c92fde561849ab..d3fb11529ed96054ac753a9e26898942bb708d56 100644 (file)
@@ -640,3 +640,108 @@ cifs_add_pending_open(struct cifs_fid *fid, struct tcon_link *tlink,
        cifs_add_pending_open_locked(fid, tlink, open);
        spin_unlock(&tlink_tcon(open->tlink)->open_file_lock);
 }
+
+/* parses DFS refferal V3 structure
+ * caller is responsible for freeing target_nodes
+ * returns:
+ * - on success - 0
+ * - on failure - errno
+ */
+int
+parse_dfs_referrals(struct get_dfs_referral_rsp *rsp, u32 rsp_size,
+                   unsigned int *num_of_nodes,
+                   struct dfs_info3_param **target_nodes,
+                   const struct nls_table *nls_codepage, int remap,
+                   const char *searchName, bool is_unicode)
+{
+       int i, rc = 0;
+       char *data_end;
+       struct dfs_referral_level_3 *ref;
+
+       *num_of_nodes = le16_to_cpu(rsp->NumberOfReferrals);
+
+       if (*num_of_nodes < 1) {
+               cifs_dbg(VFS, "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 *) &(rsp->referrals);
+       if (ref->VersionNumber != cpu_to_le16(3)) {
+               cifs_dbg(VFS, "Referrals of V%d version are not supported, should be V3\n",
+                        le16_to_cpu(ref->VersionNumber));
+               rc = -EINVAL;
+               goto parse_DFS_referrals_exit;
+       }
+
+       /* get the upper boundary of the resp buffer */
+       data_end = (char *)rsp + rsp_size;
+
+       cifs_dbg(FYI, "num_referrals: %d dfs flags: 0x%x ...\n",
+                *num_of_nodes, le32_to_cpu(rsp->DFSFlags));
+
+       *target_nodes = kcalloc(*num_of_nodes, sizeof(struct dfs_info3_param),
+                               GFP_KERNEL);
+       if (*target_nodes == NULL) {
+               rc = -ENOMEM;
+               goto parse_DFS_referrals_exit;
+       }
+
+       /* collect necessary 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 = le32_to_cpu(rsp->DFSFlags);
+               if (is_unicode) {
+                       __le16 *tmp = kmalloc(strlen(searchName)*2 + 2,
+                                               GFP_KERNEL);
+                       if (tmp == NULL) {
+                               rc = -ENOMEM;
+                               goto parse_DFS_referrals_exit;
+                       }
+                       cifsConvertToUTF16((__le16 *) tmp, searchName,
+                                          PATH_MAX, nls_codepage, remap);
+                       node->path_consumed = cifs_utf16_bytes(tmp,
+                                       le16_to_cpu(rsp->PathConsumed),
+                                       nls_codepage);
+                       kfree(tmp);
+               } else
+                       node->path_consumed = le16_to_cpu(rsp->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;
+               node->path_name = cifs_strndup_from_utf16(temp, max_len,
+                                               is_unicode, nls_codepage);
+               if (!node->path_name) {
+                       rc = -ENOMEM;
+                       goto parse_DFS_referrals_exit;
+               }
+
+               /* copy link target UNC */
+               temp = (char *)ref + le16_to_cpu(ref->NetworkAddressOffset);
+               max_len = data_end - temp;
+               node->node_name = cifs_strndup_from_utf16(temp, max_len,
+                                               is_unicode, nls_codepage);
+               if (!node->node_name) {
+                       rc = -ENOMEM;
+                       goto parse_DFS_referrals_exit;
+               }
+
+               ref++;
+       }
+
+parse_DFS_referrals_exit:
+       if (rc) {
+               free_dfs_info_array(*target_nodes, *num_of_nodes);
+               *target_nodes = NULL;
+               *num_of_nodes = 0;
+       }
+       return rc;
+}