aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorPavel Shilovsky <pshilovsky@samba.org>2012-09-18 19:20:33 -0400
committerSteve French <smfrench@gmail.com>2012-09-24 22:46:30 -0400
commitd324f08d6a87149597817f4496ef0f7ac185e8da (patch)
tree03a02fcacdfe0ce8f2d91eb0cc3b108f02d0ba25 /fs
parent92fc65a74a2be1388d774f7dbf82c9adea1745cf (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')
-rw-r--r--fs/cifs/smb2misc.c8
-rw-r--r--fs/cifs/smb2ops.c57
-rw-r--r--fs/cifs/smb2pdu.c168
-rw-r--r--fs/cifs/smb2pdu.h28
-rw-r--r--fs/cifs/smb2proto.h5
5 files changed, 264 insertions, 2 deletions
diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c
index 9275883c8530..78225f517a60 100644
--- a/fs/cifs/smb2misc.c
+++ b/fs/cifs/smb2misc.c
@@ -248,6 +248,11 @@ smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr)
248 *len = le32_to_cpu(((struct smb2_read_rsp *)hdr)->DataLength); 248 *len = le32_to_cpu(((struct smb2_read_rsp *)hdr)->DataLength);
249 break; 249 break;
250 case SMB2_QUERY_DIRECTORY: 250 case SMB2_QUERY_DIRECTORY:
251 *off = le16_to_cpu(
252 ((struct smb2_query_directory_rsp *)hdr)->OutputBufferOffset);
253 *len = le32_to_cpu(
254 ((struct smb2_query_directory_rsp *)hdr)->OutputBufferLength);
255 break;
251 case SMB2_IOCTL: 256 case SMB2_IOCTL:
252 case SMB2_CHANGE_NOTIFY: 257 case SMB2_CHANGE_NOTIFY:
253 default: 258 default:
@@ -290,8 +295,9 @@ smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr)
290 * portion, the number of word parameters and the data portion of the message. 295 * portion, the number of word parameters and the data portion of the message.
291 */ 296 */
292unsigned int 297unsigned int
293smb2_calc_size(struct smb2_hdr *hdr) 298smb2_calc_size(void *buf)
294{ 299{
300 struct smb2_hdr *hdr = (struct smb2_hdr *)buf;
295 struct smb2_pdu *pdu = (struct smb2_pdu *)hdr; 301 struct smb2_pdu *pdu = (struct smb2_pdu *)hdr;
296 int offset; /* the offset from the beginning of SMB to data area */ 302 int offset; /* the offset from the beginning of SMB to data area */
297 int data_length; /* the length of the variable length data area */ 303 int data_length; /* the length of the variable length data area */
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index 52bc93125726..0fa086c1b5dc 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -424,6 +424,59 @@ smb2_set_file_size(const unsigned int xid, struct cifs_tcon *tcon,
424 cfile->fid.volatile_fid, cfile->pid, &eof); 424 cfile->fid.volatile_fid, cfile->pid, &eof);
425} 425}
426 426
427static int
428smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon,
429 const char *path, struct cifs_sb_info *cifs_sb,
430 struct cifs_fid *fid, __u16 search_flags,
431 struct cifs_search_info *srch_inf)
432{
433 __le16 *utf16_path;
434 int rc;
435 __u64 persistent_fid, volatile_fid;
436
437 utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
438 if (!utf16_path)
439 return -ENOMEM;
440
441 rc = SMB2_open(xid, tcon, utf16_path, &persistent_fid, &volatile_fid,
442 FILE_READ_ATTRIBUTES | FILE_READ_DATA, FILE_OPEN, 0, 0,
443 NULL);
444 kfree(utf16_path);
445 if (rc) {
446 cERROR(1, "open dir failed");
447 return rc;
448 }
449
450 srch_inf->entries_in_buffer = 0;
451 srch_inf->index_of_last_entry = 0;
452 fid->persistent_fid = persistent_fid;
453 fid->volatile_fid = volatile_fid;
454
455 rc = SMB2_query_directory(xid, tcon, persistent_fid, volatile_fid, 0,
456 srch_inf);
457 if (rc) {
458 cERROR(1, "query directory failed");
459 SMB2_close(xid, tcon, persistent_fid, volatile_fid);
460 }
461 return rc;
462}
463
464static int
465smb2_query_dir_next(const unsigned int xid, struct cifs_tcon *tcon,
466 struct cifs_fid *fid, __u16 search_flags,
467 struct cifs_search_info *srch_inf)
468{
469 return SMB2_query_directory(xid, tcon, fid->persistent_fid,
470 fid->volatile_fid, 0, srch_inf);
471}
472
473static int
474smb2_close_dir(const unsigned int xid, struct cifs_tcon *tcon,
475 struct cifs_fid *fid)
476{
477 return SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid);
478}
479
427struct smb_version_operations smb21_operations = { 480struct smb_version_operations smb21_operations = {
428 .setup_request = smb2_setup_request, 481 .setup_request = smb2_setup_request,
429 .setup_async_request = smb2_setup_async_request, 482 .setup_async_request = smb2_setup_async_request,
@@ -473,6 +526,10 @@ struct smb_version_operations smb21_operations = {
473 .async_writev = smb2_async_writev, 526 .async_writev = smb2_async_writev,
474 .sync_read = smb2_sync_read, 527 .sync_read = smb2_sync_read,
475 .sync_write = smb2_sync_write, 528 .sync_write = smb2_sync_write,
529 .query_dir_first = smb2_query_dir_first,
530 .query_dir_next = smb2_query_dir_next,
531 .close_dir = smb2_close_dir,
532 .calc_smb_size = smb2_calc_size,
476}; 533};
477 534
478struct smb_version_values smb21_values = { 535struct smb_version_values smb21_values = {
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
1607static unsigned int
1608num_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 */
1650int
1651SMB2_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
1769qdir_exit:
1770 free_rsp_buf(resp_buftype, rsp);
1771 return rc;
1772}
1773
1606static int 1774static int
1607send_set_info(const unsigned int xid, struct cifs_tcon *tcon, 1775send_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,
diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h
index d775941f552a..e6ddd1f79706 100644
--- a/fs/cifs/smb2pdu.h
+++ b/fs/cifs/smb2pdu.h
@@ -538,6 +538,34 @@ struct smb2_echo_rsp {
538 __u16 Reserved; 538 __u16 Reserved;
539} __packed; 539} __packed;
540 540
541/* search (query_directory) Flags field */
542#define SMB2_RESTART_SCANS 0x01
543#define SMB2_RETURN_SINGLE_ENTRY 0x02
544#define SMB2_INDEX_SPECIFIED 0x04
545#define SMB2_REOPEN 0x10
546
547struct smb2_query_directory_req {
548 struct smb2_hdr hdr;
549 __le16 StructureSize; /* Must be 33 */
550 __u8 FileInformationClass;
551 __u8 Flags;
552 __le32 FileIndex;
553 __u64 PersistentFileId; /* opaque endianness */
554 __u64 VolatileFileId; /* opaque endianness */
555 __le16 FileNameOffset;
556 __le16 FileNameLength;
557 __le32 OutputBufferLength;
558 __u8 Buffer[1];
559} __packed;
560
561struct smb2_query_directory_rsp {
562 struct smb2_hdr hdr;
563 __le16 StructureSize; /* Must be 9 */
564 __le16 OutputBufferOffset;
565 __le32 OutputBufferLength;
566 __u8 Buffer[1];
567} __packed;
568
541/* Possible InfoType values */ 569/* Possible InfoType values */
542#define SMB2_O_INFO_FILE 0x01 570#define SMB2_O_INFO_FILE 0x01
543#define SMB2_O_INFO_FILESYSTEM 0x02 571#define SMB2_O_INFO_FILESYSTEM 0x02
diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h
index 277472433158..0d29db222a5a 100644
--- a/fs/cifs/smb2proto.h
+++ b/fs/cifs/smb2proto.h
@@ -34,7 +34,7 @@ struct statfs;
34 */ 34 */
35extern int map_smb2_to_linux_error(char *buf, bool log_err); 35extern int map_smb2_to_linux_error(char *buf, bool log_err);
36extern int smb2_check_message(char *buf, unsigned int length); 36extern int smb2_check_message(char *buf, unsigned int length);
37extern unsigned int smb2_calc_size(struct smb2_hdr *hdr); 37extern unsigned int smb2_calc_size(void *buf);
38extern char *smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr); 38extern char *smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr);
39extern __le16 *cifs_convert_path_to_utf16(const char *from, 39extern __le16 *cifs_convert_path_to_utf16(const char *from,
40 struct cifs_sb_info *cifs_sb); 40 struct cifs_sb_info *cifs_sb);
@@ -117,6 +117,9 @@ extern int smb2_async_writev(struct cifs_writedata *wdata);
117extern int SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms, 117extern int SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms,
118 unsigned int *nbytes, struct kvec *iov, int n_vec); 118 unsigned int *nbytes, struct kvec *iov, int n_vec);
119extern int SMB2_echo(struct TCP_Server_Info *server); 119extern int SMB2_echo(struct TCP_Server_Info *server);
120extern int SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon,
121 u64 persistent_fid, u64 volatile_fid, int index,
122 struct cifs_search_info *srch_inf);
120extern int SMB2_rename(const unsigned int xid, struct cifs_tcon *tcon, 123extern int SMB2_rename(const unsigned int xid, struct cifs_tcon *tcon,
121 u64 persistent_fid, u64 volatile_fid, 124 u64 persistent_fid, u64 volatile_fid,
122 __le16 *target_file); 125 __le16 *target_file);