[PATCH 25/50] CIFS: Add SMB2 support for cifs_iovec_read

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Signed-off-by: Pavel Shilovsky <piastryyy@xxxxxxxxx>
---
 fs/cifs/cifsglob.h  |    3 +
 fs/cifs/cifsproto.h |    4 ++
 fs/cifs/file.c      |   76 ++++++++++++++++++++++++-------------
 fs/cifs/smb2file.c  |   43 ++++++++++++++++++++-
 fs/cifs/smb2glob.h  |    6 +++
 fs/cifs/smb2misc.c  |    4 ++
 fs/cifs/smb2pdu.c   |  105 +++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/cifs/smb2pdu.h   |   28 +++++++++++++
 fs/cifs/smb2proto.h |    7 +++
 9 files changed, 248 insertions(+), 28 deletions(-)

diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index e2eee60..dedeb0f 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -607,6 +607,9 @@ typedef int (iwrite_callback_t)(int, struct cifsFileInfo *,
 				struct cifs_io_parms *, unsigned int *,
 				struct kvec *, unsigned long, unsigned int,
 				int);
+typedef int (iread_callback_t)(int, struct cifsFileInfo *,
+			       struct cifs_io_parms *, unsigned int *, char **,
+			       char **, int *);
 
 /*
  * Take a reference on the file private data. Must be called with
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index 1c7bedb..76b65dd 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -193,6 +193,10 @@ extern ssize_t cifs_iovec_write_generic(struct file *file,
 					const struct iovec *iov,
 					unsigned long nr_segs, loff_t *poffset,
 					iwrite_callback_t *write_cb);
+extern ssize_t cifs_iovec_read_generic(struct file *file,
+				       const struct iovec *iov,
+				       unsigned long nr_segs, loff_t *poffset,
+				       iread_callback_t *read_cb);
 extern void cifs_mark_open_files_invalid(struct cifs_tcon *tcon);
 void cifs_proc_init(void);
 void cifs_proc_clean(void);
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index ac1ec04..b90417e 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -2361,9 +2361,28 @@ ssize_t cifs_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
 	return written;
 }
 
-static ssize_t
-cifs_iovec_read(struct file *file, const struct iovec *iov,
-		 unsigned long nr_segs, loff_t *poffset)
+static int
+cifs_iread_cb(int xid, struct cifsFileInfo *cfile, struct cifs_io_parms *parms,
+	      unsigned int *bytes_read, char **all_data, char **data_offset,
+	      int *resp_buf_type)
+{
+	struct smb_com_read_rsp *pSMBr;
+	int rc;
+
+	parms->netfid = cfile->netfid;
+	rc = CIFSSMBRead(xid, parms, bytes_read, all_data, resp_buf_type);
+	if (!(*all_data))
+		return rc;
+
+	pSMBr = (struct smb_com_read_rsp *)(*all_data);
+	*data_offset = *all_data + 4 + le16_to_cpu(pSMBr->DataOffset);
+	return rc;
+}
+
+ssize_t
+cifs_iovec_read_generic(struct file *file, const struct iovec *iov,
+			unsigned long nr_segs, loff_t *poffset,
+			iread_callback_t *read_cb)
 {
 	int rc;
 	int xid;
@@ -2372,11 +2391,11 @@ cifs_iovec_read(struct file *file, const struct iovec *iov,
 	size_t len, cur_len;
 	int iov_offset = 0;
 	struct cifs_sb_info *cifs_sb;
-	struct cifs_tcon *pTcon;
+	struct cifs_tcon *tcon;
 	struct cifsFileInfo *open_file;
-	struct smb_com_read_rsp *pSMBr;
 	struct cifs_io_parms io_parms;
 	char *read_data;
+	char *data_offset = NULL;
 	unsigned int rsize;
 	__u32 pid;
 
@@ -2394,7 +2413,7 @@ cifs_iovec_read(struct file *file, const struct iovec *iov,
 	rsize = min_t(unsigned int, cifs_sb->rsize, CIFSMaxBufSize);
 
 	open_file = file->private_data;
-	pTcon = tlink_tcon(open_file->tlink);
+	tcon = tlink_tcon(open_file->tlink);
 
 	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD)
 		pid = open_file->pid;
@@ -2416,27 +2435,24 @@ cifs_iovec_read(struct file *file, const struct iovec *iov,
 				if (rc != 0)
 					break;
 			}
-			io_parms.netfid = open_file->netfid;
 			io_parms.pid = pid;
-			io_parms.tcon = pTcon;
+			io_parms.tcon = tcon;
 			io_parms.offset = *poffset;
 			io_parms.length = cur_len;
-			rc = CIFSSMBRead(xid, &io_parms, &bytes_read,
-					 &read_data, &buf_type);
-			pSMBr = (struct smb_com_read_rsp *)read_data;
-			if (read_data) {
-				char *data_offset = read_data + 4 +
-						le16_to_cpu(pSMBr->DataOffset);
-				if (memcpy_toiovecend(iov, data_offset,
-						      iov_offset, bytes_read))
-					rc = -EFAULT;
-				if (buf_type == CIFS_SMALL_BUFFER)
-					cifs_small_buf_release(read_data);
-				else if (buf_type == CIFS_LARGE_BUFFER)
-					cifs_buf_release(read_data);
-				read_data = NULL;
-				iov_offset += bytes_read;
-			}
+			rc = read_cb(xid, open_file, &io_parms, &bytes_read,
+				     &read_data, &data_offset, &buf_type);
+			if (rc)
+				continue;
+
+			if (memcpy_toiovecend(iov, data_offset,
+					      iov_offset, bytes_read))
+				rc = -EFAULT;
+			if (buf_type == CIFS_SMALL_BUFFER)
+				cifs_small_buf_release(read_data);
+			else if (buf_type == CIFS_LARGE_BUFFER)
+				cifs_buf_release(read_data);
+			read_data = NULL;
+			iov_offset += bytes_read;
 		}
 
 		if (rc || (bytes_read == 0)) {
@@ -2447,7 +2463,7 @@ cifs_iovec_read(struct file *file, const struct iovec *iov,
 				return rc;
 			}
 		} else {
-			cifs_stats_bytes_read(pTcon, bytes_read);
+			cifs_stats_bytes_read(tcon, bytes_read);
 			*poffset += bytes_read;
 		}
 	}
@@ -2456,8 +2472,16 @@ cifs_iovec_read(struct file *file, const struct iovec *iov,
 	return total_read;
 }
 
+static ssize_t
+cifs_iovec_read(struct file *file, const struct iovec *iov,
+		unsigned long nr_segs, loff_t *poffset)
+{
+	return cifs_iovec_read_generic(file, iov, nr_segs, poffset,
+				       cifs_iread_cb);
+}
+
 ssize_t cifs_user_readv(struct kiocb *iocb, const struct iovec *iov,
-			       unsigned long nr_segs, loff_t pos)
+			unsigned long nr_segs, loff_t pos)
 {
 	ssize_t read;
 
diff --git a/fs/cifs/smb2file.c b/fs/cifs/smb2file.c
index a00a453..9829ed7 100644
--- a/fs/cifs/smb2file.c
+++ b/fs/cifs/smb2file.c
@@ -76,7 +76,7 @@ const struct file_operations smb2_file_direct_ops = {
 	/* BB reevaluate whether they can be done with directio, no cache */
 	.read = do_sync_read,
 	.write = do_sync_write,
-	.aio_read = cifs_user_readv,
+	.aio_read = smb2_user_readv,
 	.aio_write = smb2_user_writev,
 	.open = smb2_open,
 	.release = cifs_close,
@@ -132,7 +132,7 @@ const struct file_operations smb2_file_direct_nobrl_ops = {
 	/* BB reevaluate whether they can be done with directio, no cache */
 	.read = do_sync_read,
 	.write = do_sync_write,
-	.aio_read = cifs_user_readv,
+	.aio_read = smb2_user_readv,
 	.aio_write = smb2_user_writev,
 	.open = smb2_open,
 	.release = cifs_close,
@@ -367,3 +367,42 @@ ssize_t smb2_user_writev(struct kiocb *iocb, const struct iovec *iov,
 
 	return written;
 }
+
+static int
+smb2_iread_cb(int xid, struct cifsFileInfo *cfile, struct cifs_io_parms *parms,
+	      unsigned int *bytes_read, char **all_data, char **data_offset,
+	      int *resp_buf_type)
+{
+	struct smb2_read_rsp *pSMB2r;
+	int rc;
+
+	parms->persist_fid = cfile->persist_fid;
+	parms->volatile_fid = cfile->volatile_fid;
+	rc = SMB2_read(xid, parms, bytes_read, all_data, resp_buf_type, 0);
+	if (!(*all_data))
+		return rc;
+
+	pSMB2r = (struct smb2_read_rsp *)(*all_data);
+	*data_offset = (char *)pSMB2r->hdr.ProtocolId + pSMB2r->DataOffset;
+	return rc;
+}
+
+static ssize_t
+smb2_iovec_read(struct file *file, const struct iovec *iov,
+		unsigned long nr_segs, loff_t *poffset)
+{
+	return cifs_iovec_read_generic(file, iov, nr_segs, poffset,
+				       smb2_iread_cb);
+}
+
+ssize_t smb2_user_readv(struct kiocb *iocb, const struct iovec *iov,
+			unsigned long nr_segs, loff_t pos)
+{
+	ssize_t read;
+
+	read = smb2_iovec_read(iocb->ki_filp, iov, nr_segs, &pos);
+	if (read > 0)
+		iocb->ki_pos = pos;
+
+	return read;
+}
diff --git a/fs/cifs/smb2glob.h b/fs/cifs/smb2glob.h
index 1db1196..bde9f99 100644
--- a/fs/cifs/smb2glob.h
+++ b/fs/cifs/smb2glob.h
@@ -40,4 +40,10 @@
 #define SMB2_OP_RENAME 6
 #define SMB2_OP_DELETE 7
 
+/* Used when constructing chained read requests. */
+#define CHAINED_REQUEST 1
+#define START_OF_CHAIN 2
+#define END_OF_CHAIN 4
+#define RELATED_REQUEST 8
+
 #endif	/* _SMB2_GLOB_H */
diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c
index ad84694..d63987c 100644
--- a/fs/cifs/smb2misc.c
+++ b/fs/cifs/smb2misc.c
@@ -245,6 +245,10 @@ char *smb2_get_data_area_len(int *poff, int *plen, struct smb2_hdr *pSMB2)
 		    ((struct smb2_query_info_rsp *)pSMB2)->OutputBufferLength);
 		break;
 	case SMB2_READ:
+		*poff = ((struct smb2_read_rsp *)pSMB2)->DataOffset;
+		*plen = le32_to_cpu(
+				((struct smb2_read_rsp *)pSMB2)->DataLength);
+		break;
 	case SMB2_QUERY_DIRECTORY:
 	case SMB2_IOCTL:
 	case SMB2_CHANGE_NOTIFY:
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index 457718a..c4d4928 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -41,6 +41,7 @@
 #include "cifs_debug.h"
 #include "ntlmssp.h"
 #include "smb2status.h"
+#include "smb2glob.h"
 
 /*
  *  The following table defines the expected "StructureSize" of SMB2 requests
@@ -1127,6 +1128,110 @@ qinf_exit:
 }
 
 /*
+ * To form a chain of read requests, any read requests after the
+ * first should have the end_of_chain boolean set to true.
+ */
+int smb2_new_read_req(struct kvec *iov, struct cifs_io_parms *io_parms,
+		      unsigned int remaining_bytes, int request_type)
+{
+	int rc = -EACCES;
+	struct smb2_read_req *pSMB2 = NULL;
+
+	rc = small_smb2_init(SMB2_READ, io_parms->tcon, (void **) &pSMB2);
+	if (rc)
+		return rc;
+	if (io_parms->tcon->ses->server == NULL)
+		return -ECONNABORTED;
+
+	pSMB2->hdr.ProcessId = cpu_to_le32(io_parms->pid);
+
+	pSMB2->PersistentFileId = io_parms->persist_fid;
+	pSMB2->VolatileFileId = io_parms->volatile_fid;
+	pSMB2->ReadChannelInfoOffset = 0; /* reserved */
+	pSMB2->ReadChannelInfoLength = 0; /* reserved */
+	pSMB2->Channel = 0; /* reserved */
+	pSMB2->MinimumCount = 0;
+	pSMB2->Length = cpu_to_le32(io_parms->length);
+	pSMB2->Offset = cpu_to_le64(io_parms->offset);
+
+	if (request_type & CHAINED_REQUEST) {
+		if (!(request_type & END_OF_CHAIN)) {
+			pSMB2->hdr.NextCommand = cpu_to_le32(
+				be32_to_cpu(pSMB2->hdr.smb2_buf_length) + 4);
+		} else /* END_OF_CHAIN */
+			pSMB2->hdr.NextCommand = 0;
+		if (request_type & RELATED_REQUEST) {
+			pSMB2->hdr.Flags |= SMB2_FLAGS_RELATED_OPERATIONS;
+			/*
+			 * related requests use info from previous
+			 * read request in chain.
+			 */
+			pSMB2->hdr.SessionId = 0xFFFFFFFF;
+			pSMB2->hdr.TreeId = 0xFFFFFFFF;
+			pSMB2->PersistentFileId = 0xFFFFFFFF;
+			pSMB2->VolatileFileId = 0xFFFFFFFF;
+		}
+	}
+	if (remaining_bytes > io_parms->length)
+		pSMB2->RemainingBytes = cpu_to_le32(remaining_bytes);
+	else
+		pSMB2->RemainingBytes = 0;
+
+	iov[0].iov_base = (char *)pSMB2;
+	iov[0].iov_len = be32_to_cpu(pSMB2->hdr.smb2_buf_length) + 4;
+	return rc;
+}
+
+int SMB2_read(const int xid, struct cifs_io_parms *io_parms,
+	      unsigned int *nbytes, char **buf, int *pbuf_type,
+	      unsigned int remaining_bytes)
+{
+	int status, resp_buftype, rc = -EACCES;
+	struct smb2_read_rsp *pSMB2r = NULL;
+	struct kvec iov[1];
+
+	*nbytes = 0;
+	rc = smb2_new_read_req(iov, io_parms, remaining_bytes, 0);
+	if (rc)
+		return rc;
+
+	rc = smb2_sendrcv2(xid, io_parms->tcon->ses, iov, 1,
+			   &resp_buftype, &status,
+			   CIFS_STD_OP | CIFS_LOG_ERROR);
+	if (status == STATUS_END_OF_FILE) {
+		free_rsp_buf(resp_buftype, iov[0].iov_base);
+		return 0;
+	}
+
+	cFYI(1, "read returned buftype %d with rc %d status 0x%x",
+		 resp_buftype, rc, status);
+
+	pSMB2r = (struct smb2_read_rsp *)iov[0].iov_base;
+
+	if (rc) {
+		cifs_stats_fail_inc(io_parms->tcon, SMB2READ);
+		cERROR(1, "Send error in read = %d", rc);
+	} else {
+		*nbytes = le32_to_cpu(pSMB2r->DataLength);
+		if ((*nbytes > CIFS_MAX_MSGSIZE) ||
+		    (*nbytes > io_parms->length)) {
+			cFYI(1, "bad length %d for count %d", *nbytes,
+			     io_parms->length);
+			rc = -EIO;
+			*nbytes = 0;
+		}
+	}
+	if (resp_buftype != CIFS_NO_BUFFER) {
+		*buf = iov[0].iov_base;
+		if (resp_buftype == CIFS_SMALL_BUFFER)
+			*pbuf_type = CIFS_SMALL_BUFFER;
+		else if (resp_buftype == CIFS_LARGE_BUFFER)
+			*pbuf_type = CIFS_LARGE_BUFFER;
+	}
+	return rc;
+}
+
+/*
  * SMB2_write function gets iov pointer to kvec array with n_vec as a length.
  * The length field from io_parms must be at least 1 and indicates a number of
  * elements with data to write that begins with position 1 in iov array. All
diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h
index a66bf18..92d0982 100644
--- a/fs/cifs/smb2pdu.h
+++ b/fs/cifs/smb2pdu.h
@@ -441,6 +441,34 @@ struct smb2_close_rsp {
 	__le32 Attributes;
 } __packed;
 
+struct smb2_read_req {
+	struct smb2_hdr hdr;
+	__le16 StructureSize; /* Must be 49 */
+	__u8   Padding; /* offset from start of SMB2 header to place read */
+	__u8   Reserved;
+	__le32 Length;
+	__le64 Offset;
+	__u64  PersistentFileId; /* opaque endianness */
+	__u64  VolatileFileId; /* opaque endianness */
+	__le32 MinimumCount;
+	__le32 Channel; /* Reserved MBZ */
+	__le32 RemainingBytes;
+	__le16 ReadChannelInfoOffset; /* Reserved MBZ */
+	__le16 ReadChannelInfoLength; /* Reserved MBZ */
+	__u8   Buffer[1];
+} __packed;
+
+struct smb2_read_rsp {
+	struct smb2_hdr hdr;
+	__le16 StructureSize; /* Must be 17 */
+	__u8   DataOffset;
+	__u8   Reserved;
+	__le32 DataLength;
+	__le32 DataRemaining;
+	__u32  Reserved2;
+	__u8   Buffer[1];
+} __packed;
+
 /* For write request Flags field below the following flag is defined: */
 #define SMB2_WRITEFLAG_WRITE_THROUGH 0x00000001
 
diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h
index 56fa08c..b477b90 100644
--- a/fs/cifs/smb2proto.h
+++ b/fs/cifs/smb2proto.h
@@ -82,6 +82,8 @@ extern int smb2_reopen_file_cb(struct cifsFileInfo *cifs_file, int xid,
 			       __u32 *oplock);
 extern ssize_t smb2_user_writev(struct kiocb *iocb, const struct iovec *iov,
 				unsigned long nr_segs, loff_t pos);
+extern ssize_t smb2_user_readv(struct kiocb *iocb, const struct iovec *iov,
+			       unsigned long nr_segs, loff_t pos);
 
 /*
  *  SMB2 Worker functions - most of protocol specific implementation details
@@ -107,5 +109,10 @@ extern int SMB2_query_info(const int xid, struct cifs_tcon *tcon,
 extern int SMB2_write(const int xid, struct cifs_io_parms *io_parms,
 		      unsigned int *nbytes, struct kvec *iov, int n_vec,
 		      const unsigned int remaining_bytes, int wtimeout);
+extern int smb2_new_read_req(struct kvec *iov, struct cifs_io_parms *io_parms,
+			     unsigned int remaining_bytes, int request_type);
+extern int SMB2_read(const int xid, struct cifs_io_parms *io_parms,
+		     unsigned int *nbytes, char **buf, int *pbuf_type,
+		     unsigned int remaining_bytes);
 
 #endif			/* _SMB2PROTO_H */
-- 
1.7.1

--
To unsubscribe from this list: send the line "unsubscribe linux-cifs" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux