Skip to content

Commit f071292

Browse files
aaptelsmfrench
authored andcommitted
CIFS: use DFS pathnames in SMB2+ Create requests
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> Acked-by: Pavel Shilovsky <pshilov@microsoft.com> Signed-off-by: Steve French <smfrench@gmail.com>
1 parent b9043cc commit f071292

File tree

1 file changed

+80
-16
lines changed

1 file changed

+80
-16
lines changed

fs/cifs/smb2pdu.c

Lines changed: 80 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1528,6 +1528,51 @@ add_durable_context(struct kvec *iov, unsigned int *num_iovec,
15281528
return 0;
15291529
}
15301530

1531+
static int
1532+
alloc_path_with_tree_prefix(__le16 **out_path, int *out_size, int *out_len,
1533+
const char *treename, const __le16 *path)
1534+
{
1535+
int treename_len, path_len;
1536+
struct nls_table *cp;
1537+
const __le16 sep[] = {cpu_to_le16('\\'), cpu_to_le16(0x0000)};
1538+
1539+
/*
1540+
* skip leading "\\"
1541+
*/
1542+
treename_len = strlen(treename);
1543+
if (treename_len < 2 || !(treename[0] == '\\' && treename[1] == '\\'))
1544+
return -EINVAL;
1545+
1546+
treename += 2;
1547+
treename_len -= 2;
1548+
1549+
path_len = UniStrnlen((wchar_t *)path, PATH_MAX);
1550+
1551+
/*
1552+
* make room for one path separator between the treename and
1553+
* path
1554+
*/
1555+
*out_len = treename_len + 1 + path_len;
1556+
1557+
/*
1558+
* final path needs to be null-terminated UTF16 with a
1559+
* size aligned to 8
1560+
*/
1561+
1562+
*out_size = roundup((*out_len+1)*2, 8);
1563+
*out_path = kzalloc(*out_size, GFP_KERNEL);
1564+
if (!*out_path)
1565+
return -ENOMEM;
1566+
1567+
cp = load_nls_default();
1568+
cifs_strtoUTF16(*out_path, treename, treename_len, cp);
1569+
UniStrcat(*out_path, sep);
1570+
UniStrcat(*out_path, path);
1571+
unload_nls(cp);
1572+
1573+
return 0;
1574+
}
1575+
15311576
int
15321577
SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
15331578
__u8 *oplock, struct smb2_file_all_info *buf,
@@ -1576,30 +1621,49 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
15761621
req->ShareAccess = FILE_SHARE_ALL_LE;
15771622
req->CreateDisposition = cpu_to_le32(oparms->disposition);
15781623
req->CreateOptions = cpu_to_le32(oparms->create_options & CREATE_OPTIONS_MASK);
1579-
uni_path_len = (2 * UniStrnlen((wchar_t *)path, PATH_MAX)) + 2;
1580-
/* do not count rfc1001 len field */
1581-
req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req) - 4);
15821624

15831625
iov[0].iov_base = (char *)req;
15841626
/* 4 for rfc1002 length field */
15851627
iov[0].iov_len = get_rfc1002_length(req) + 4;
1586-
1587-
/* MUST set path len (NameLength) to 0 opening root of share */
1588-
req->NameLength = cpu_to_le16(uni_path_len - 2);
15891628
/* -1 since last byte is buf[0] which is sent below (path) */
15901629
iov[0].iov_len--;
1591-
if (uni_path_len % 8 != 0) {
1592-
copy_size = uni_path_len / 8 * 8;
1593-
if (copy_size < uni_path_len)
1594-
copy_size += 8;
1595-
1596-
copy_path = kzalloc(copy_size, GFP_KERNEL);
1597-
if (!copy_path)
1598-
return -ENOMEM;
1599-
memcpy((char *)copy_path, (const char *)path,
1600-
uni_path_len);
1630+
1631+
req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req) - 4);
1632+
1633+
/* [MS-SMB2] 2.2.13 NameOffset:
1634+
* If SMB2_FLAGS_DFS_OPERATIONS is set in the Flags field of
1635+
* the SMB2 header, the file name includes a prefix that will
1636+
* be processed during DFS name normalization as specified in
1637+
* section 3.3.5.9. Otherwise, the file name is relative to
1638+
* the share that is identified by the TreeId in the SMB2
1639+
* header.
1640+
*/
1641+
if (tcon->share_flags & SHI1005_FLAGS_DFS) {
1642+
int name_len;
1643+
1644+
req->hdr.sync_hdr.Flags |= SMB2_FLAGS_DFS_OPERATIONS;
1645+
rc = alloc_path_with_tree_prefix(&copy_path, &copy_size,
1646+
&name_len,
1647+
tcon->treeName, path);
1648+
if (rc)
1649+
return rc;
1650+
req->NameLength = cpu_to_le16(name_len * 2);
16011651
uni_path_len = copy_size;
16021652
path = copy_path;
1653+
} else {
1654+
uni_path_len = (2 * UniStrnlen((wchar_t *)path, PATH_MAX)) + 2;
1655+
/* MUST set path len (NameLength) to 0 opening root of share */
1656+
req->NameLength = cpu_to_le16(uni_path_len - 2);
1657+
if (uni_path_len % 8 != 0) {
1658+
copy_size = roundup(uni_path_len, 8);
1659+
copy_path = kzalloc(copy_size, GFP_KERNEL);
1660+
if (!copy_path)
1661+
return -ENOMEM;
1662+
memcpy((char *)copy_path, (const char *)path,
1663+
uni_path_len);
1664+
uni_path_len = copy_size;
1665+
path = copy_path;
1666+
}
16031667
}
16041668

16051669
iov[1].iov_len = uni_path_len;

0 commit comments

Comments
 (0)