2017-02-28 10:40 GMT-08:00 Aurelien Aptel <aaptel@xxxxxxxx>: > 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@xxxxxxxx> > --- > fs/cifs/cifspdu.h | 16 ++++--- > fs/cifs/cifsproto.h | 5 +++ > fs/cifs/cifssmb.c | 119 +++------------------------------------------------- > fs/cifs/misc.c | 105 ++++++++++++++++++++++++++++++++++++++++++++++ > 4 files changed, 125 insertions(+), 120 deletions(-) > > diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h > index f5b8730..1ce733f 100644 > --- a/fs/cifs/cifspdu.h > +++ b/fs/cifs/cifspdu.h > @@ -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 */ > diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h > index 406d2c1..c097830 100644 > --- a/fs/cifs/cifsproto.h > +++ b/fs/cifs/cifsproto.h > @@ -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); > diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c > index f5099fb..5005c79 100644 > --- a/fs/cifs/cifssmb.c > +++ b/fs/cifs/cifssmb.c > @@ -4786,117 +4786,6 @@ CIFSGetSrvInodeNumber(const unsigned int xid, struct cifs_tcon *tcon, > 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 @@ CIFSGetDFSRefer(const unsigned int xid, struct cifs_ses *ses, > 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); > diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c > index c672915..d3fb115 100644 > --- a/fs/cifs/misc.c > +++ b/fs/cifs/misc.c > @@ -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; > +} > -- > 2.10.2 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-cifs" in > the body of a message to majordomo@xxxxxxxxxxxxxxx > More majordomo info at http://vger.kernel.org/majordomo-info.html Acked-by: Pavel Shilovsky <pshilov@xxxxxxxxxxxxx> -- Best regards, Pavel Shilovsky -- To unsubscribe from this list: send the line "unsubscribe linux-cifs" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html