|
@@ -45,6 +45,7 @@
|
|
|
#include "ntlmssp.h"
|
|
|
#include "smb2status.h"
|
|
|
#include "smb2glob.h"
|
|
|
+#include "cifspdu.h"
|
|
|
|
|
|
/*
|
|
|
* 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,
|
|
|
return rc;
|
|
|
}
|
|
|
|
|
|
+static unsigned int
|
|
|
+num_entries(char *bufstart, char *end_of_buf, char **lastentry, size_t size)
|
|
|
+{
|
|
|
+ int len;
|
|
|
+ unsigned int entrycount = 0;
|
|
|
+ unsigned int next_offset = 0;
|
|
|
+ FILE_DIRECTORY_INFO *entryptr;
|
|
|
+
|
|
|
+ if (bufstart == NULL)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ entryptr = (FILE_DIRECTORY_INFO *)bufstart;
|
|
|
+
|
|
|
+ while (1) {
|
|
|
+ entryptr = (FILE_DIRECTORY_INFO *)
|
|
|
+ ((char *)entryptr + next_offset);
|
|
|
+
|
|
|
+ if ((char *)entryptr + size > end_of_buf) {
|
|
|
+ cERROR(1, "malformed search entry would overflow");
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ len = le32_to_cpu(entryptr->FileNameLength);
|
|
|
+ if ((char *)entryptr + len + size > end_of_buf) {
|
|
|
+ cERROR(1, "directory entry name would overflow frame "
|
|
|
+ "end of buf %p", end_of_buf);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ *lastentry = (char *)entryptr;
|
|
|
+ entrycount++;
|
|
|
+
|
|
|
+ next_offset = le32_to_cpu(entryptr->NextEntryOffset);
|
|
|
+ if (!next_offset)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ return entrycount;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Readdir/FindFirst
|
|
|
+ */
|
|
|
+int
|
|
|
+SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon,
|
|
|
+ u64 persistent_fid, u64 volatile_fid, int index,
|
|
|
+ struct cifs_search_info *srch_inf)
|
|
|
+{
|
|
|
+ struct smb2_query_directory_req *req;
|
|
|
+ struct smb2_query_directory_rsp *rsp = NULL;
|
|
|
+ struct kvec iov[2];
|
|
|
+ int rc = 0;
|
|
|
+ int len;
|
|
|
+ int resp_buftype;
|
|
|
+ unsigned char *bufptr;
|
|
|
+ struct TCP_Server_Info *server;
|
|
|
+ struct cifs_ses *ses = tcon->ses;
|
|
|
+ __le16 asteriks = cpu_to_le16('*');
|
|
|
+ char *end_of_smb;
|
|
|
+ unsigned int output_size = CIFSMaxBufSize;
|
|
|
+ size_t info_buf_size;
|
|
|
+
|
|
|
+ if (ses && (ses->server))
|
|
|
+ server = ses->server;
|
|
|
+ else
|
|
|
+ return -EIO;
|
|
|
+
|
|
|
+ rc = small_smb2_init(SMB2_QUERY_DIRECTORY, tcon, (void **) &req);
|
|
|
+ if (rc)
|
|
|
+ return rc;
|
|
|
+
|
|
|
+ switch (srch_inf->info_level) {
|
|
|
+ case SMB_FIND_FILE_DIRECTORY_INFO:
|
|
|
+ req->FileInformationClass = FILE_DIRECTORY_INFORMATION;
|
|
|
+ info_buf_size = sizeof(FILE_DIRECTORY_INFO) - 1;
|
|
|
+ break;
|
|
|
+ case SMB_FIND_FILE_ID_FULL_DIR_INFO:
|
|
|
+ req->FileInformationClass = FILEID_FULL_DIRECTORY_INFORMATION;
|
|
|
+ info_buf_size = sizeof(SEARCH_ID_FULL_DIR_INFO) - 1;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ cERROR(1, "info level %u isn't supported",
|
|
|
+ srch_inf->info_level);
|
|
|
+ rc = -EINVAL;
|
|
|
+ goto qdir_exit;
|
|
|
+ }
|
|
|
+
|
|
|
+ req->FileIndex = cpu_to_le32(index);
|
|
|
+ req->PersistentFileId = persistent_fid;
|
|
|
+ req->VolatileFileId = volatile_fid;
|
|
|
+
|
|
|
+ len = 0x2;
|
|
|
+ bufptr = req->Buffer;
|
|
|
+ memcpy(bufptr, &asteriks, len);
|
|
|
+
|
|
|
+ req->FileNameOffset =
|
|
|
+ cpu_to_le16(sizeof(struct smb2_query_directory_req) - 1 - 4);
|
|
|
+ req->FileNameLength = cpu_to_le16(len);
|
|
|
+ /*
|
|
|
+ * BB could be 30 bytes or so longer if we used SMB2 specific
|
|
|
+ * buffer lengths, but this is safe and close enough.
|
|
|
+ */
|
|
|
+ output_size = min_t(unsigned int, output_size, server->maxBuf);
|
|
|
+ output_size = min_t(unsigned int, output_size, 2 << 15);
|
|
|
+ req->OutputBufferLength = cpu_to_le32(output_size);
|
|
|
+
|
|
|
+ iov[0].iov_base = (char *)req;
|
|
|
+ /* 4 for RFC1001 length and 1 for Buffer */
|
|
|
+ iov[0].iov_len = get_rfc1002_length(req) + 4 - 1;
|
|
|
+
|
|
|
+ iov[1].iov_base = (char *)(req->Buffer);
|
|
|
+ iov[1].iov_len = len;
|
|
|
+
|
|
|
+ inc_rfc1001_len(req, len - 1 /* Buffer */);
|
|
|
+
|
|
|
+ rc = SendReceive2(xid, ses, iov, 2, &resp_buftype, 0);
|
|
|
+ if (rc) {
|
|
|
+ cifs_stats_fail_inc(tcon, SMB2_QUERY_DIRECTORY_HE);
|
|
|
+ goto qdir_exit;
|
|
|
+ }
|
|
|
+ rsp = (struct smb2_query_directory_rsp *)iov[0].iov_base;
|
|
|
+
|
|
|
+ rc = validate_buf(le16_to_cpu(rsp->OutputBufferOffset),
|
|
|
+ le32_to_cpu(rsp->OutputBufferLength), &rsp->hdr,
|
|
|
+ info_buf_size);
|
|
|
+ if (rc)
|
|
|
+ goto qdir_exit;
|
|
|
+
|
|
|
+ srch_inf->unicode = true;
|
|
|
+
|
|
|
+ if (srch_inf->ntwrk_buf_start) {
|
|
|
+ if (srch_inf->smallBuf)
|
|
|
+ cifs_small_buf_release(srch_inf->ntwrk_buf_start);
|
|
|
+ else
|
|
|
+ cifs_buf_release(srch_inf->ntwrk_buf_start);
|
|
|
+ }
|
|
|
+ srch_inf->ntwrk_buf_start = (char *)rsp;
|
|
|
+ srch_inf->srch_entries_start = srch_inf->last_entry = 4 /* rfclen */ +
|
|
|
+ (char *)&rsp->hdr + le16_to_cpu(rsp->OutputBufferOffset);
|
|
|
+ /* 4 for rfc1002 length field */
|
|
|
+ end_of_smb = get_rfc1002_length(rsp) + 4 + (char *)&rsp->hdr;
|
|
|
+ srch_inf->entries_in_buffer =
|
|
|
+ num_entries(srch_inf->srch_entries_start, end_of_smb,
|
|
|
+ &srch_inf->last_entry, info_buf_size);
|
|
|
+ srch_inf->index_of_last_entry += srch_inf->entries_in_buffer;
|
|
|
+ cFYI(1, "num entries %d last_index %lld srch start %p srch end %p",
|
|
|
+ srch_inf->entries_in_buffer, srch_inf->index_of_last_entry,
|
|
|
+ srch_inf->srch_entries_start, srch_inf->last_entry);
|
|
|
+ if (resp_buftype == CIFS_LARGE_BUFFER)
|
|
|
+ srch_inf->smallBuf = false;
|
|
|
+ else if (resp_buftype == CIFS_SMALL_BUFFER)
|
|
|
+ srch_inf->smallBuf = true;
|
|
|
+ else
|
|
|
+ cERROR(1, "illegal search buffer type");
|
|
|
+
|
|
|
+ if (rsp->hdr.Status == STATUS_NO_MORE_FILES)
|
|
|
+ srch_inf->endOfSearch = 1;
|
|
|
+ else
|
|
|
+ srch_inf->endOfSearch = 0;
|
|
|
+
|
|
|
+ return rc;
|
|
|
+
|
|
|
+qdir_exit:
|
|
|
+ free_rsp_buf(resp_buftype, rsp);
|
|
|
+ return rc;
|
|
|
+}
|
|
|
+
|
|
|
static int
|
|
|
send_set_info(const unsigned int xid, struct cifs_tcon *tcon,
|
|
|
u64 persistent_fid, u64 volatile_fid, u32 pid, int info_class,
|