Message ID | 20170228184034.18771-7-aaptel@suse.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
2017-02-28 10:40 GMT-08:00 Aurelien Aptel <aaptel@suse.com>: > When connected to a DFS capable share, the client must set the > SMB2_FLAGS_DFS_OPERATIONS flag in the SMB2 header and use > DFS path names: "<server>\<share>\<path>" *without* leading \\. > > Sources: > > [MS-SMB2] 3.2.5.5 Receiving an SMB2 TREE_CONNECT Response >> TreeConnect.IsDfsShare MUST be set to TRUE, if the SMB2_SHARE_CAP_DFS >> bit is set in the Capabilities field of the response. > > [MS-SMB2] 3.2.4.3 Application Requests Opening a File >> If TreeConnect.IsDfsShare is TRUE, the SMB2_FLAGS_DFS_OPERATIONS flag >> is set in the Flags field. > > [MS-SMB2] 2.2.13 SMB2 CREATE Request, NameOffset: >> If SMB2_FLAGS_DFS_OPERATIONS is set in the Flags field of the SMB2 >> header, the file name includes a prefix that will be processed during >> DFS name normalization as specified in section 3.3.5.9. Otherwise, the >> file name is relative to the share that is identified by the TreeId in >> the SMB2 header. > > Signed-off-by: Aurelien Aptel <aaptel@suse.com> > --- > fs/cifs/smb2pdu.c | 93 +++++++++++++++++++++++++++++++++++++++++++++---------- > 1 file changed, 77 insertions(+), 16 deletions(-) > > diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c > index 2fd93ee..059da05 100644 > --- a/fs/cifs/smb2pdu.c > +++ b/fs/cifs/smb2pdu.c > @@ -1528,6 +1528,48 @@ add_durable_context(struct kvec *iov, unsigned int *num_iovec, > return 0; > } > > +static int > +alloc_path_with_tree_prefix(__le16 **out_path, int *out_size, int *out_len, > + const char *treename, const __le16 *path) > +{ > + int treename_len, path_len; > + const __le16 sep[] = {cpu_to_le16('\\'), cpu_to_le16(0x0000)}; > + > + /* > + * skip leading "\\" > + */ > + treename_len = strlen(treename); > + if (treename_len < 2 || !(treename[0] == '\\' && treename[1] == '\\')) > + return -EINVAL; > + > + treename += 2; > + treename_len -= 2; > + > + path_len = UniStrnlen((wchar_t *)path, PATH_MAX); > + > + /* > + * make room for one path separator between the treename and > + * path > + */ > + *out_len = treename_len + 1 + path_len; > + > + /* > + * final path needs to be null-terminated UTF16 with a > + * size aligned to 8 > + */ > + > + *out_size = roundup((*out_len+1)*2, 8); > + *out_path = kzalloc(*out_size, GFP_KERNEL); > + if (!*out_path) > + return -ENOMEM; > + > + cifs_strtoUTF16(*out_path, treename, treename_len, load_nls_default()); > + UniStrcat(*out_path, sep); > + UniStrcat(*out_path, path); > + > + return 0; > +} > + > int > SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, > __u8 *oplock, struct smb2_file_all_info *buf, > @@ -1576,30 +1618,49 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, > req->ShareAccess = FILE_SHARE_ALL_LE; > req->CreateDisposition = cpu_to_le32(oparms->disposition); > req->CreateOptions = cpu_to_le32(oparms->create_options & CREATE_OPTIONS_MASK); > - uni_path_len = (2 * UniStrnlen((wchar_t *)path, PATH_MAX)) + 2; > - /* do not count rfc1001 len field */ > - req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req) - 4); > > iov[0].iov_base = (char *)req; > /* 4 for rfc1002 length field */ > iov[0].iov_len = get_rfc1002_length(req) + 4; > - > - /* MUST set path len (NameLength) to 0 opening root of share */ > - req->NameLength = cpu_to_le16(uni_path_len - 2); > /* -1 since last byte is buf[0] which is sent below (path) */ > iov[0].iov_len--; > - if (uni_path_len % 8 != 0) { > - copy_size = uni_path_len / 8 * 8; > - if (copy_size < uni_path_len) > - copy_size += 8; > - > - copy_path = kzalloc(copy_size, GFP_KERNEL); > - if (!copy_path) > - return -ENOMEM; > - memcpy((char *)copy_path, (const char *)path, > - uni_path_len); > + > + req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req) - 4); > + > + /* [MS-SMB2] 2.2.13 NameOffset: > + * If SMB2_FLAGS_DFS_OPERATIONS is set in the Flags field of > + * the SMB2 header, the file name includes a prefix that will > + * be processed during DFS name normalization as specified in > + * section 3.3.5.9. Otherwise, the file name is relative to > + * the share that is identified by the TreeId in the SMB2 > + * header. > + */ > + if (tcon->share_flags & SHI1005_FLAGS_DFS) { > + int name_len; > + > + req->hdr.sync_hdr.Flags |= SMB2_FLAGS_DFS_OPERATIONS; > + rc = alloc_path_with_tree_prefix(©_path, ©_size, > + &name_len, > + tcon->treeName, path); > + if (rc) > + return rc; > + req->NameLength = cpu_to_le16(name_len * 2); > uni_path_len = copy_size; > path = copy_path; > + } else { > + uni_path_len = (2 * UniStrnlen((wchar_t *)path, PATH_MAX)) + 2; > + /* MUST set path len (NameLength) to 0 opening root of share */ > + req->NameLength = cpu_to_le16(uni_path_len - 2); > + if (uni_path_len % 8 != 0) { > + copy_size = roundup(uni_path_len, 8); > + copy_path = kzalloc(copy_size, GFP_KERNEL); > + if (!copy_path) > + return -ENOMEM; > + memcpy((char *)copy_path, (const char *)path, > + uni_path_len); > + uni_path_len = copy_size; > + path = copy_path; > + } > } > > iov[1].iov_len = uni_path_len; > -- > 2.10.2 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-cifs" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html Acked-by: Pavel Shilovsky <pshilov@microsoft.com>
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 2fd93ee..059da05 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -1528,6 +1528,48 @@ add_durable_context(struct kvec *iov, unsigned int *num_iovec, return 0; } +static int +alloc_path_with_tree_prefix(__le16 **out_path, int *out_size, int *out_len, + const char *treename, const __le16 *path) +{ + int treename_len, path_len; + const __le16 sep[] = {cpu_to_le16('\\'), cpu_to_le16(0x0000)}; + + /* + * skip leading "\\" + */ + treename_len = strlen(treename); + if (treename_len < 2 || !(treename[0] == '\\' && treename[1] == '\\')) + return -EINVAL; + + treename += 2; + treename_len -= 2; + + path_len = UniStrnlen((wchar_t *)path, PATH_MAX); + + /* + * make room for one path separator between the treename and + * path + */ + *out_len = treename_len + 1 + path_len; + + /* + * final path needs to be null-terminated UTF16 with a + * size aligned to 8 + */ + + *out_size = roundup((*out_len+1)*2, 8); + *out_path = kzalloc(*out_size, GFP_KERNEL); + if (!*out_path) + return -ENOMEM; + + cifs_strtoUTF16(*out_path, treename, treename_len, load_nls_default()); + UniStrcat(*out_path, sep); + UniStrcat(*out_path, path); + + return 0; +} + int SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, __u8 *oplock, struct smb2_file_all_info *buf, @@ -1576,30 +1618,49 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, req->ShareAccess = FILE_SHARE_ALL_LE; req->CreateDisposition = cpu_to_le32(oparms->disposition); req->CreateOptions = cpu_to_le32(oparms->create_options & CREATE_OPTIONS_MASK); - uni_path_len = (2 * UniStrnlen((wchar_t *)path, PATH_MAX)) + 2; - /* do not count rfc1001 len field */ - req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req) - 4); iov[0].iov_base = (char *)req; /* 4 for rfc1002 length field */ iov[0].iov_len = get_rfc1002_length(req) + 4; - - /* MUST set path len (NameLength) to 0 opening root of share */ - req->NameLength = cpu_to_le16(uni_path_len - 2); /* -1 since last byte is buf[0] which is sent below (path) */ iov[0].iov_len--; - if (uni_path_len % 8 != 0) { - copy_size = uni_path_len / 8 * 8; - if (copy_size < uni_path_len) - copy_size += 8; - - copy_path = kzalloc(copy_size, GFP_KERNEL); - if (!copy_path) - return -ENOMEM; - memcpy((char *)copy_path, (const char *)path, - uni_path_len); + + req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req) - 4); + + /* [MS-SMB2] 2.2.13 NameOffset: + * If SMB2_FLAGS_DFS_OPERATIONS is set in the Flags field of + * the SMB2 header, the file name includes a prefix that will + * be processed during DFS name normalization as specified in + * section 3.3.5.9. Otherwise, the file name is relative to + * the share that is identified by the TreeId in the SMB2 + * header. + */ + if (tcon->share_flags & SHI1005_FLAGS_DFS) { + int name_len; + + req->hdr.sync_hdr.Flags |= SMB2_FLAGS_DFS_OPERATIONS; + rc = alloc_path_with_tree_prefix(©_path, ©_size, + &name_len, + tcon->treeName, path); + if (rc) + return rc; + req->NameLength = cpu_to_le16(name_len * 2); uni_path_len = copy_size; path = copy_path; + } else { + uni_path_len = (2 * UniStrnlen((wchar_t *)path, PATH_MAX)) + 2; + /* MUST set path len (NameLength) to 0 opening root of share */ + req->NameLength = cpu_to_le16(uni_path_len - 2); + if (uni_path_len % 8 != 0) { + copy_size = roundup(uni_path_len, 8); + copy_path = kzalloc(copy_size, GFP_KERNEL); + if (!copy_path) + return -ENOMEM; + memcpy((char *)copy_path, (const char *)path, + uni_path_len); + uni_path_len = copy_size; + path = copy_path; + } } iov[1].iov_len = uni_path_len;