diff options
author | Aurelien Aptel <aaptel@suse.com> | 2017-02-22 08:47:17 -0500 |
---|---|---|
committer | Steve French <smfrench@gmail.com> | 2017-03-02 18:04:58 -0500 |
commit | f0712928be1a66c99c35f871b4df7fa23ec1574a (patch) | |
tree | 3262f33e0131838da803cc1e7c20da2605bbc6f3 | |
parent | b9043cc5b99e9c93596a28647fd9f526f5bfa22c (diff) |
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>
-rw-r--r-- | fs/cifs/smb2pdu.c | 96 |
1 files changed, 80 insertions, 16 deletions
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 2fd93eeed15a..2069431b32e3 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c | |||
@@ -1528,6 +1528,51 @@ add_durable_context(struct kvec *iov, unsigned int *num_iovec, | |||
1528 | return 0; | 1528 | return 0; |
1529 | } | 1529 | } |
1530 | 1530 | ||
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 | |||
1531 | int | 1576 | int |
1532 | SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, | 1577 | SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, |
1533 | __u8 *oplock, struct smb2_file_all_info *buf, | 1578 | __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, | |||
1576 | req->ShareAccess = FILE_SHARE_ALL_LE; | 1621 | req->ShareAccess = FILE_SHARE_ALL_LE; |
1577 | req->CreateDisposition = cpu_to_le32(oparms->disposition); | 1622 | req->CreateDisposition = cpu_to_le32(oparms->disposition); |
1578 | req->CreateOptions = cpu_to_le32(oparms->create_options & CREATE_OPTIONS_MASK); | 1623 | 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); | ||
1582 | 1624 | ||
1583 | iov[0].iov_base = (char *)req; | 1625 | iov[0].iov_base = (char *)req; |
1584 | /* 4 for rfc1002 length field */ | 1626 | /* 4 for rfc1002 length field */ |
1585 | iov[0].iov_len = get_rfc1002_length(req) + 4; | 1627 | 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); | ||
1589 | /* -1 since last byte is buf[0] which is sent below (path) */ | 1628 | /* -1 since last byte is buf[0] which is sent below (path) */ |
1590 | iov[0].iov_len--; | 1629 | iov[0].iov_len--; |
1591 | if (uni_path_len % 8 != 0) { | 1630 | |
1592 | copy_size = uni_path_len / 8 * 8; | 1631 | req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req) - 4); |
1593 | if (copy_size < uni_path_len) | 1632 | |
1594 | copy_size += 8; | 1633 | /* [MS-SMB2] 2.2.13 NameOffset: |
1595 | 1634 | * If SMB2_FLAGS_DFS_OPERATIONS is set in the Flags field of | |
1596 | copy_path = kzalloc(copy_size, GFP_KERNEL); | 1635 | * the SMB2 header, the file name includes a prefix that will |
1597 | if (!copy_path) | 1636 | * be processed during DFS name normalization as specified in |
1598 | return -ENOMEM; | 1637 | * section 3.3.5.9. Otherwise, the file name is relative to |
1599 | memcpy((char *)copy_path, (const char *)path, | 1638 | * the share that is identified by the TreeId in the SMB2 |
1600 | uni_path_len); | 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(©_path, ©_size, | ||
1646 | &name_len, | ||
1647 | tcon->treeName, path); | ||
1648 | if (rc) | ||
1649 | return rc; | ||
1650 | req->NameLength = cpu_to_le16(name_len * 2); | ||
1601 | uni_path_len = copy_size; | 1651 | uni_path_len = copy_size; |
1602 | path = copy_path; | 1652 | 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 | } | ||
1603 | } | 1667 | } |
1604 | 1668 | ||
1605 | iov[1].iov_len = uni_path_len; | 1669 | iov[1].iov_len = uni_path_len; |