2016-11-08 8:33 GMT-08:00 Aurelien Aptel <aaptel@xxxxxxxx>: > Few issues with the current situation > > * the get_dfs_refer op is meant to be used from cifs thus the prototype > of the operation is not adapted for smb2: > > - it passes a session as opposed to a tcon like other smb2 operations, > which means we have to find the corresponding tcon ourselves. see > new function find_session_tcon() which currently assumes the first one > is the right one. I think it is ok since it matches the spec which suggests to find any ses and tcon. > > - it doesn't pass a cifs_sb so we cannot use the usual utf16 > conversion function used in the rest of smb2 code I suggest to pass cifs_sb to get_dfs_path() (instead of cifs_remap(cifs_sb)) and then modify a get_dfs_refer() callback to take cifs_sb as well. This let us use cifs_convert_path_to_utf16(). > > * the DFS request has to be made on the IPC tcon and i'm not really sure > how to get to it since it doesnt seem to be part of the session tcon > list. Should we probably add an extra param to SMB2_ioctl called ipc_tid? In case when tcon passed to this function is NULL ipc_tid will be used in a similar manner how it is done now for CIFS. Otherwise (if tcon is not NULL) tcon is used as it is now. > > * the function that extracts the data from the responce is almost > entirely copied and adapted from the smb1 code. ideally they should be > merged. > > Signed-off-by: Aurelien Aptel <aaptel@xxxxxxxx> > --- > fs/cifs/smb2ops.c | 206 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 206 insertions(+) > > diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c > index 5d456eb..677579b 100644 > --- a/fs/cifs/smb2ops.c > +++ b/fs/cifs/smb2ops.c > @@ -1093,6 +1093,212 @@ smb2_new_lease_key(struct cifs_fid *fid) > generate_random_uuid(fid->lease_key); > } > > +/* > + * XXX: Return the tcon currently used in the session > + */ > +static struct cifs_tcon * > +find_session_tcon (struct cifs_ses *ses) > +{ > + struct cifs_tcon *tcon = NULL, *it; > + struct list_head *tmp; > + int i = 0; > + > + spin_lock(&cifs_tcp_ses_lock); > + list_for_each(tmp, &ses->tcon_list) { > + it = list_entry(tmp, struct cifs_tcon, tcon_list); > + cifs_dbg(FYI, "XXX: [%d] tcon %p\n", i, it); > + > + /* XXX: Assumes the first one is always the right one. */ > + if (i == 0) { > + tcon = it; > + } > + i++; > + } > + spin_unlock(&cifs_tcp_ses_lock); > + > + if (!tcon) > + cifs_dbg(FYI, "XXX: cannot find our tcon\n"); > + return tcon; > +} > + > +/* > + * XXX: Copied and adapted from parse_DFS_referrals in cifssmb.c > + * TODO: extract logic in a SMB agnostic way > + */ > +static int > +smb2_parse_dfs_referrers_rsp(struct fsctl_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) > +{ > + int i, rc = 0; > + char *data_end; > + bool is_unicode = true; > + 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->buffer[0]; > + 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->PathConsumed)) + rsp_size; > + > + cifs_dbg(FYI, "num_referrals: %d dfs flags: 0x%x ...\n", > + *num_of_nodes, le32_to_cpu(rsp->ReferralHeaderFlags)); > + > + *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; > + __le16 *tmp; > + struct dfs_info3_param *node = (*target_nodes)+i; > + > + node->flags = le32_to_cpu(rsp->ReferralHeaderFlags); > + 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); > + > + 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, > + true /* 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; > +} > + > + > +static int > +smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses, > + const char *search_name, struct dfs_info3_param **target_nodes, > + unsigned int *num_of_nodes, > + const struct nls_table *nls_codepage, int remap) > +{ > + int rc = -ENOSYS; > + __le16 *utf16_path = NULL; > + int utf16_path_len = 0; > + struct cifs_tcon *tcon; > + struct fsctl_get_dfs_referral_req *dfs_req = NULL; > + struct fsctl_get_dfs_referral_rsp *dfs_rsp = NULL; > + u32 dfs_req_size = 0, dfs_rsp_size = 0; > + > + cifs_dbg(FYI, "XXX: In smb2_get_dfs_refer the path <%s> %d\n", search_name, (int)strlen(search_name)); > + > + /* > + * XXX: Find this session tcon. We shouldnt have to do this > + * proper solution would be to update get_dfs_refer op > + * prototype to pass it somehow. > + */ > + tcon = find_session_tcon(ses); > + if (!tcon) > + goto out; > + > + /* > + * XXX: Should we use use cifs_convert_path_to_utf16(full_path, cifs_sb) instead? > + * it needs cifs_sb superblock pointer which we don't have... > + */ > + > + utf16_path_len = strlen(search_name) * 2; > + utf16_path = kzalloc(utf16_path_len + 2, GFP_KERNEL); > + if (!utf16_path) { > + rc = -ENOMEM; > + goto out; > + } > + > + cifsConvertToUTF16(utf16_path, > + search_name, PATH_MAX, nls_codepage, > + remap); > + > + dfs_req_size = sizeof(*dfs_req) + utf16_path_len + 2; > + dfs_req = kzalloc(dfs_req_size, GFP_KERNEL); > + if (!dfs_req) { > + rc = -ENOMEM; > + goto out; > + } > + > + /* Highest DFS referral version understood (actually the only supported one) */ > + dfs_req->MaxReferralLevel = cpu_to_le16(DFS_VERSION); > + /* Path to resolve in an UTF-16 null-terminated string */ > + memcpy(dfs_req->RequestFileName, utf16_path, utf16_path_len); > + > + do { > + rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID, > + FSCTL_DFS_GET_REFERRALS, true /* is_fsctl */, > + (char *)dfs_req, dfs_req_size, > + (char **)&dfs_rsp, &dfs_rsp_size); > + if (rc) { > + cifs_dbg(FYI, "SMB2_ioctl error in smb2_get_dfs_refer rc=%d\n", rc); > + goto out; > + } > + > + rc = smb2_parse_dfs_referrers_rsp(dfs_rsp, dfs_rsp_size, > + num_of_nodes, target_nodes, > + nls_codepage, remap, search_name); > + kfree(dfs_rsp); > + } while (rc == -EAGAIN); > + > + out: > + kfree(utf16_path); > + kfree(dfs_req); > + return rc; > +} > + > #define SMB2_SYMLINK_STRUCT_SIZE \ > (sizeof(struct smb2_err_rsp) - 1 + sizeof(struct smb2_symlink_err_rsp)) > > -- > 2.1.4 > > -- > 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 -- 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