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 | |
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')
-rw-r--r-- | fs/cifs/smb2misc.c | 8 | ||||
-rw-r--r-- | fs/cifs/smb2ops.c | 57 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.c | 168 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.h | 28 | ||||
-rw-r--r-- | fs/cifs/smb2proto.h | 5 |
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 | */ |
292 | unsigned int | 297 | unsigned int |
293 | smb2_calc_size(struct smb2_hdr *hdr) | 298 | smb2_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 | ||
427 | static int | ||
428 | smb2_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 | |||
464 | static int | ||
465 | smb2_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 | |||
473 | static int | ||
474 | smb2_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 | |||
427 | struct smb_version_operations smb21_operations = { | 480 | struct 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 | ||
478 | struct smb_version_values smb21_values = { | 535 | struct 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 | ||
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, |
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 | |||
547 | struct 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 | |||
561 | struct 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 | */ |
35 | extern int map_smb2_to_linux_error(char *buf, bool log_err); | 35 | extern int map_smb2_to_linux_error(char *buf, bool log_err); |
36 | extern int smb2_check_message(char *buf, unsigned int length); | 36 | extern int smb2_check_message(char *buf, unsigned int length); |
37 | extern unsigned int smb2_calc_size(struct smb2_hdr *hdr); | 37 | extern unsigned int smb2_calc_size(void *buf); |
38 | extern char *smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr); | 38 | extern char *smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr); |
39 | extern __le16 *cifs_convert_path_to_utf16(const char *from, | 39 | extern __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); | |||
117 | extern int SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms, | 117 | extern 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); |
119 | extern int SMB2_echo(struct TCP_Server_Info *server); | 119 | extern int SMB2_echo(struct TCP_Server_Info *server); |
120 | extern 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); | ||
120 | extern int SMB2_rename(const unsigned int xid, struct cifs_tcon *tcon, | 123 | extern 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); |