diff options
author | Pavel Shilovsky <pshilovsky@samba.org> | 2012-09-18 19:20:33 -0400 |
---|---|---|
committer | Steve French <smfrench@gmail.com> | 2012-09-24 22:46:30 -0400 |
commit | d324f08d6a87149597817f4496ef0f7ac185e8da (patch) | |
tree | 03a02fcacdfe0ce8f2d91eb0cc3b108f02d0ba25 /fs/cifs/smb2pdu.c | |
parent | 92fc65a74a2be1388d774f7dbf82c9adea1745cf (diff) |
CIFS: Add readdir support for SMB2
Signed-off-by: Pavel Shilovsky <pshilovsky@samba.org>
Signed-off-by: Steve French <smfrench@gmail.com>
Diffstat (limited to 'fs/cifs/smb2pdu.c')
-rw-r--r-- | fs/cifs/smb2pdu.c | 168 |
1 files changed, 168 insertions, 0 deletions
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index a1314f99b4cc..21b3a652e192 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c | |||
@@ -45,6 +45,7 @@ | |||
45 | #include "ntlmssp.h" | 45 | #include "ntlmssp.h" |
46 | #include "smb2status.h" | 46 | #include "smb2status.h" |
47 | #include "smb2glob.h" | 47 | #include "smb2glob.h" |
48 | #include "cifspdu.h" | ||
48 | 49 | ||
49 | /* | 50 | /* |
50 | * The following table defines the expected "StructureSize" of SMB2 requests | 51 | * The following table defines the expected "StructureSize" of SMB2 requests |
@@ -1603,6 +1604,173 @@ SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms, | |||
1603 | return rc; | 1604 | return rc; |
1604 | } | 1605 | } |
1605 | 1606 | ||
1607 | static unsigned int | ||
1608 | num_entries(char *bufstart, char *end_of_buf, char **lastentry, size_t size) | ||
1609 | { | ||
1610 | int len; | ||
1611 | unsigned int entrycount = 0; | ||
1612 | unsigned int next_offset = 0; | ||
1613 | FILE_DIRECTORY_INFO *entryptr; | ||
1614 | |||
1615 | if (bufstart == NULL) | ||
1616 | return 0; | ||
1617 | |||
1618 | entryptr = (FILE_DIRECTORY_INFO *)bufstart; | ||
1619 | |||
1620 | while (1) { | ||
1621 | entryptr = (FILE_DIRECTORY_INFO *) | ||
1622 | ((char *)entryptr + next_offset); | ||
1623 | |||
1624 | if ((char *)entryptr + size > end_of_buf) { | ||
1625 | cERROR(1, "malformed search entry would overflow"); | ||
1626 | break; | ||
1627 | } | ||
1628 | |||
1629 | len = le32_to_cpu(entryptr->FileNameLength); | ||
1630 | if ((char *)entryptr + len + size > end_of_buf) { | ||
1631 | cERROR(1, "directory entry name would overflow frame " | ||
1632 | "end of buf %p", end_of_buf); | ||
1633 | break; | ||
1634 | } | ||
1635 | |||
1636 | *lastentry = (char *)entryptr; | ||
1637 | entrycount++; | ||
1638 | |||
1639 | next_offset = le32_to_cpu(entryptr->NextEntryOffset); | ||
1640 | if (!next_offset) | ||
1641 | break; | ||
1642 | } | ||
1643 | |||
1644 | return entrycount; | ||
1645 | } | ||
1646 | |||
1647 | /* | ||
1648 | * Readdir/FindFirst | ||
1649 | */ | ||
1650 | int | ||
1651 | SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon, | ||
1652 | u64 persistent_fid, u64 volatile_fid, int index, | ||
1653 | struct cifs_search_info *srch_inf) | ||
1654 | { | ||
1655 | struct smb2_query_directory_req *req; | ||
1656 | struct smb2_query_directory_rsp *rsp = NULL; | ||
1657 | struct kvec iov[2]; | ||
1658 | int rc = 0; | ||
1659 | int len; | ||
1660 | int resp_buftype; | ||
1661 | unsigned char *bufptr; | ||
1662 | struct TCP_Server_Info *server; | ||
1663 | struct cifs_ses *ses = tcon->ses; | ||
1664 | __le16 asteriks = cpu_to_le16('*'); | ||
1665 | char *end_of_smb; | ||
1666 | unsigned int output_size = CIFSMaxBufSize; | ||
1667 | size_t info_buf_size; | ||
1668 | |||
1669 | if (ses && (ses->server)) | ||
1670 | server = ses->server; | ||
1671 | else | ||
1672 | return -EIO; | ||
1673 | |||
1674 | rc = small_smb2_init(SMB2_QUERY_DIRECTORY, tcon, (void **) &req); | ||
1675 | if (rc) | ||
1676 | return rc; | ||
1677 | |||
1678 | switch (srch_inf->info_level) { | ||
1679 | case SMB_FIND_FILE_DIRECTORY_INFO: | ||
1680 | req->FileInformationClass = FILE_DIRECTORY_INFORMATION; | ||
1681 | info_buf_size = sizeof(FILE_DIRECTORY_INFO) - 1; | ||
1682 | break; | ||
1683 | case SMB_FIND_FILE_ID_FULL_DIR_INFO: | ||
1684 | req->FileInformationClass = FILEID_FULL_DIRECTORY_INFORMATION; | ||
1685 | info_buf_size = sizeof(SEARCH_ID_FULL_DIR_INFO) - 1; | ||
1686 | break; | ||
1687 | default: | ||
1688 | cERROR(1, "info level %u isn't supported", | ||
1689 | srch_inf->info_level); | ||
1690 | rc = -EINVAL; | ||
1691 | goto qdir_exit; | ||
1692 | } | ||
1693 | |||
1694 | req->FileIndex = cpu_to_le32(index); | ||
1695 | req->PersistentFileId = persistent_fid; | ||
1696 | req->VolatileFileId = volatile_fid; | ||
1697 | |||
1698 | len = 0x2; | ||
1699 | bufptr = req->Buffer; | ||
1700 | memcpy(bufptr, &asteriks, len); | ||
1701 | |||
1702 | req->FileNameOffset = | ||
1703 | cpu_to_le16(sizeof(struct smb2_query_directory_req) - 1 - 4); | ||
1704 | req->FileNameLength = cpu_to_le16(len); | ||
1705 | /* | ||
1706 | * BB could be 30 bytes or so longer if we used SMB2 specific | ||
1707 | * buffer lengths, but this is safe and close enough. | ||
1708 | */ | ||
1709 | output_size = min_t(unsigned int, output_size, server->maxBuf); | ||
1710 | output_size = min_t(unsigned int, output_size, 2 << 15); | ||
1711 | req->OutputBufferLength = cpu_to_le32(output_size); | ||
1712 | |||
1713 | iov[0].iov_base = (char *)req; | ||
1714 | /* 4 for RFC1001 length and 1 for Buffer */ | ||
1715 | iov[0].iov_len = get_rfc1002_length(req) + 4 - 1; | ||
1716 | |||
1717 | iov[1].iov_base = (char *)(req->Buffer); | ||
1718 | iov[1].iov_len = len; | ||
1719 | |||
1720 | inc_rfc1001_len(req, len - 1 /* Buffer */); | ||
1721 | |||
1722 | rc = SendReceive2(xid, ses, iov, 2, &resp_buftype, 0); | ||
1723 | if (rc) { | ||
1724 | cifs_stats_fail_inc(tcon, SMB2_QUERY_DIRECTORY_HE); | ||
1725 | goto qdir_exit; | ||
1726 | } | ||
1727 | rsp = (struct smb2_query_directory_rsp *)iov[0].iov_base; | ||
1728 | |||
1729 | rc = validate_buf(le16_to_cpu(rsp->OutputBufferOffset), | ||
1730 | le32_to_cpu(rsp->OutputBufferLength), &rsp->hdr, | ||
1731 | info_buf_size); | ||
1732 | if (rc) | ||
1733 | goto qdir_exit; | ||
1734 | |||
1735 | srch_inf->unicode = true; | ||
1736 | |||
1737 | if (srch_inf->ntwrk_buf_start) { | ||
1738 | if (srch_inf->smallBuf) | ||
1739 | cifs_small_buf_release(srch_inf->ntwrk_buf_start); | ||
1740 | else | ||
1741 | cifs_buf_release(srch_inf->ntwrk_buf_start); | ||
1742 | } | ||
1743 | srch_inf->ntwrk_buf_start = (char *)rsp; | ||
1744 | srch_inf->srch_entries_start = srch_inf->last_entry = 4 /* rfclen */ + | ||
1745 | (char *)&rsp->hdr + le16_to_cpu(rsp->OutputBufferOffset); | ||
1746 | /* 4 for rfc1002 length field */ | ||
1747 | end_of_smb = get_rfc1002_length(rsp) + 4 + (char *)&rsp->hdr; | ||
1748 | srch_inf->entries_in_buffer = | ||
1749 | num_entries(srch_inf->srch_entries_start, end_of_smb, | ||
1750 | &srch_inf->last_entry, info_buf_size); | ||
1751 | srch_inf->index_of_last_entry += srch_inf->entries_in_buffer; | ||
1752 | cFYI(1, "num entries %d last_index %lld srch start %p srch end %p", | ||
1753 | srch_inf->entries_in_buffer, srch_inf->index_of_last_entry, | ||
1754 | srch_inf->srch_entries_start, srch_inf->last_entry); | ||
1755 | if (resp_buftype == CIFS_LARGE_BUFFER) | ||
1756 | srch_inf->smallBuf = false; | ||
1757 | else if (resp_buftype == CIFS_SMALL_BUFFER) | ||
1758 | srch_inf->smallBuf = true; | ||
1759 | else | ||
1760 | cERROR(1, "illegal search buffer type"); | ||
1761 | |||
1762 | if (rsp->hdr.Status == STATUS_NO_MORE_FILES) | ||
1763 | srch_inf->endOfSearch = 1; | ||
1764 | else | ||
1765 | srch_inf->endOfSearch = 0; | ||
1766 | |||
1767 | return rc; | ||
1768 | |||
1769 | qdir_exit: | ||
1770 | free_rsp_buf(resp_buftype, rsp); | ||
1771 | return rc; | ||
1772 | } | ||
1773 | |||
1606 | static int | 1774 | static int |
1607 | send_set_info(const unsigned int xid, struct cifs_tcon *tcon, | 1775 | send_set_info(const unsigned int xid, struct cifs_tcon *tcon, |
1608 | u64 persistent_fid, u64 volatile_fid, u32 pid, int info_class, | 1776 | u64 persistent_fid, u64 volatile_fid, u32 pid, int info_class, |