]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blobdiff - fs/cifs/transport.c
CIFS: Separate RFC1001 length processing for SMB2 read
[mirror_ubuntu-zesty-kernel.git] / fs / cifs / transport.c
index 5f02edc819af277cfd88a7897f37b34771278b36..729e5b7b80442467e6c5ab8bb2ca4b54064b2542 100644 (file)
@@ -30,7 +30,7 @@
 #include <linux/tcp.h>
 #include <linux/bvec.h>
 #include <linux/highmem.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
 #include <asm/processor.h>
 #include <linux/mempool.h>
 #include "cifspdu.h"
@@ -245,8 +245,12 @@ smb_send_rqst(struct TCP_Server_Info *server, struct smb_rqst *rqst)
                return -EIO;
        }
 
+       if (n_vec < 2)
+               return -EIO;
+
        cifs_dbg(FYI, "Sending smb: smb_len=%u\n", smb_buf_length);
        dump_smb(iov[0].iov_base, iov[0].iov_len);
+       dump_smb(iov[1].iov_base, iov[1].iov_len);
 
        /* cork the socket */
        kernel_setsockopt(ssocket, SOL_TCP, TCP_CORK,
@@ -321,12 +325,14 @@ int
 smb_send(struct TCP_Server_Info *server, struct smb_hdr *smb_buffer,
         unsigned int smb_buf_length)
 {
-       struct kvec iov;
+       struct kvec iov[2];
 
-       iov.iov_base = smb_buffer;
-       iov.iov_len = smb_buf_length + 4;
+       iov[0].iov_base = smb_buffer;
+       iov[0].iov_len = 4;
+       iov[1].iov_base = (char *)smb_buffer + 4;
+       iov[1].iov_len = smb_buf_length;
 
-       return smb_sendv(server, &iov, 1);
+       return smb_sendv(server, iov, 2);
 }
 
 static int
@@ -454,6 +460,10 @@ cifs_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst)
        struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
        struct mid_q_entry *mid;
 
+       if (rqst->rq_iov[0].iov_len != 4 ||
+           rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base)
+               return ERR_PTR(-EIO);
+
        /* enable signing if server requires it */
        if (server->sign)
                hdr->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
@@ -477,8 +487,8 @@ cifs_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst)
  */
 int
 cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst,
-               mid_receive_t *receive, mid_callback_t *callback,
-               void *cbdata, const int flags)
+               mid_receive_t *receive, mid_callback_t *callback, void *cbdata,
+               const int flags)
 {
        int rc, timeout, optype;
        struct mid_q_entry *mid;
@@ -547,12 +557,13 @@ SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses,
 {
        int rc;
        struct kvec iov[1];
+       struct kvec rsp_iov;
        int resp_buf_type;
 
        iov[0].iov_base = in_buf;
        iov[0].iov_len = get_rfc1002_length(in_buf) + 4;
        flags |= CIFS_NO_RESP;
-       rc = SendReceive2(xid, ses, iov, 1, &resp_buf_type, flags);
+       rc = SendReceive2(xid, ses, iov, 1, &resp_buf_type, flags, &rsp_iov);
        cifs_dbg(NOISY, "SendRcvNoRsp flags %d rc %d\n", flags, rc);
 
        return rc;
@@ -595,10 +606,11 @@ cifs_sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server)
 }
 
 static inline int
-send_cancel(struct TCP_Server_Info *server, void *buf, struct mid_q_entry *mid)
+send_cancel(struct TCP_Server_Info *server, struct smb_rqst *rqst,
+           struct mid_q_entry *mid)
 {
        return server->ops->send_cancel ?
-                               server->ops->send_cancel(server, buf, mid) : 0;
+                               server->ops->send_cancel(server, rqst, mid) : 0;
 }
 
 int
@@ -611,13 +623,15 @@ cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,
 
        /* convert the length into a more usable form */
        if (server->sign) {
-               struct kvec iov;
+               struct kvec iov[2];
                int rc = 0;
-               struct smb_rqst rqst = { .rq_iov = &iov,
-                                        .rq_nvec = 1 };
+               struct smb_rqst rqst = { .rq_iov = iov,
+                                        .rq_nvec = 2 };
 
-               iov.iov_base = mid->resp_buf;
-               iov.iov_len = len;
+               iov[0].iov_base = mid->resp_buf;
+               iov[0].iov_len = 4;
+               iov[1].iov_base = (char *)mid->resp_buf + 4;
+               iov[1].iov_len = len - 4;
                /* FIXME: add code to kill session */
                rc = cifs_verify_signature(&rqst, server,
                                           mid->sequence_number);
@@ -637,6 +651,10 @@ cifs_setup_request(struct cifs_ses *ses, struct smb_rqst *rqst)
        struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
        struct mid_q_entry *mid;
 
+       if (rqst->rq_iov[0].iov_len != 4 ||
+           rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base)
+               return ERR_PTR(-EIO);
+
        rc = allocate_mid(ses, hdr, &mid);
        if (rc)
                return ERR_PTR(rc);
@@ -649,17 +667,15 @@ cifs_setup_request(struct cifs_ses *ses, struct smb_rqst *rqst)
 }
 
 int
-SendReceive2(const unsigned int xid, struct cifs_ses *ses,
-            struct kvec *iov, int n_vec, int *resp_buf_type /* ret */,
-            const int flags)
+cifs_send_recv(const unsigned int xid, struct cifs_ses *ses,
+              struct smb_rqst *rqst, int *resp_buf_type, const int flags,
+              struct kvec *resp_iov)
 {
        int rc = 0;
        int timeout, optype;
        struct mid_q_entry *midQ;
-       char *buf = iov[0].iov_base;
        unsigned int credits = 1;
-       struct smb_rqst rqst = { .rq_iov = iov,
-                                .rq_nvec = n_vec };
+       char *buf;
 
        timeout = flags & CIFS_TIMEOUT_MASK;
        optype = flags & CIFS_OP_MASK;
@@ -667,15 +683,12 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses,
        *resp_buf_type = CIFS_NO_BUFFER;  /* no response buf yet */
 
        if ((ses == NULL) || (ses->server == NULL)) {
-               cifs_small_buf_release(buf);
                cifs_dbg(VFS, "Null session\n");
                return -EIO;
        }
 
-       if (ses->server->tcpStatus == CifsExiting) {
-               cifs_small_buf_release(buf);
+       if (ses->server->tcpStatus == CifsExiting)
                return -ENOENT;
-       }
 
        /*
         * Ensure that we do not send more than 50 overlapping requests
@@ -684,10 +697,8 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses,
         */
 
        rc = wait_for_free_request(ses->server, timeout, optype);
-       if (rc) {
-               cifs_small_buf_release(buf);
+       if (rc)
                return rc;
-       }
 
        /*
         * Make sure that we sign in the same order that we send on this socket
@@ -697,10 +708,9 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses,
 
        mutex_lock(&ses->server->srv_mutex);
 
-       midQ = ses->server->ops->setup_request(ses, &rqst);
+       midQ = ses->server->ops->setup_request(ses, rqst);
        if (IS_ERR(midQ)) {
                mutex_unlock(&ses->server->srv_mutex);
-               cifs_small_buf_release(buf);
                /* Update # of requests on wire to server */
                add_credits(ses->server, 1, optype);
                return PTR_ERR(midQ);
@@ -708,7 +718,7 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses,
 
        midQ->mid_state = MID_REQUEST_SUBMITTED;
        cifs_in_send_inc(ses->server);
-       rc = smb_sendv(ses->server, iov, n_vec);
+       rc = smb_send_rqst(ses->server, rqst);
        cifs_in_send_dec(ses->server);
        cifs_save_when_sent(midQ);
 
@@ -716,32 +726,25 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses,
                ses->server->sequence_number -= 2;
        mutex_unlock(&ses->server->srv_mutex);
 
-       if (rc < 0) {
-               cifs_small_buf_release(buf);
+       if (rc < 0)
                goto out;
-       }
 
-       if (timeout == CIFS_ASYNC_OP) {
-               cifs_small_buf_release(buf);
+       if (timeout == CIFS_ASYNC_OP)
                goto out;
-       }
 
        rc = wait_for_response(ses->server, midQ);
        if (rc != 0) {
-               send_cancel(ses->server, buf, midQ);
+               send_cancel(ses->server, rqst, midQ);
                spin_lock(&GlobalMid_Lock);
                if (midQ->mid_state == MID_REQUEST_SUBMITTED) {
                        midQ->callback = DeleteMidQEntry;
                        spin_unlock(&GlobalMid_Lock);
-                       cifs_small_buf_release(buf);
                        add_credits(ses->server, 1, optype);
                        return rc;
                }
                spin_unlock(&GlobalMid_Lock);
        }
 
-       cifs_small_buf_release(buf);
-
        rc = cifs_sync_mid_result(midQ, ses->server);
        if (rc != 0) {
                add_credits(ses->server, 1, optype);
@@ -755,8 +758,8 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses,
        }
 
        buf = (char *)midQ->resp_buf;
-       iov[0].iov_base = buf;
-       iov[0].iov_len = get_rfc1002_length(buf) + 4;
+       resp_iov->iov_base = buf;
+       resp_iov->iov_len = get_rfc1002_length(buf) + 4;
        if (midQ->large_buf)
                *resp_buf_type = CIFS_LARGE_BUFFER;
        else
@@ -777,6 +780,36 @@ out:
        return rc;
 }
 
+int
+SendReceive2(const unsigned int xid, struct cifs_ses *ses,
+            struct kvec *iov, int n_vec, int *resp_buf_type /* ret */,
+            const int flags, struct kvec *resp_iov)
+{
+       struct smb_rqst rqst;
+       struct kvec *new_iov;
+       int rc;
+
+       new_iov = kmalloc(sizeof(struct kvec) * (n_vec + 1), GFP_KERNEL);
+       if (!new_iov)
+               return -ENOMEM;
+
+       /* 1st iov is a RFC1001 length followed by the rest of the packet */
+       memcpy(new_iov + 1, iov, (sizeof(struct kvec) * n_vec));
+
+       new_iov[0].iov_base = new_iov[1].iov_base;
+       new_iov[0].iov_len = 4;
+       new_iov[1].iov_base += 4;
+       new_iov[1].iov_len -= 4;
+
+       memset(&rqst, 0, sizeof(struct smb_rqst));
+       rqst.rq_iov = new_iov;
+       rqst.rq_nvec = n_vec + 1;
+
+       rc = cifs_send_recv(xid, ses, &rqst, resp_buf_type, flags, resp_iov);
+       kfree(new_iov);
+       return rc;
+}
+
 int
 SendReceive(const unsigned int xid, struct cifs_ses *ses,
            struct smb_hdr *in_buf, struct smb_hdr *out_buf,
@@ -784,6 +817,9 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
 {
        int rc = 0;
        struct mid_q_entry *midQ;
+       unsigned int len = be32_to_cpu(in_buf->smb_buf_length);
+       struct kvec iov = { .iov_base = in_buf, .iov_len = len };
+       struct smb_rqst rqst = { .rq_iov = &iov, .rq_nvec = 1 };
 
        if (ses == NULL) {
                cifs_dbg(VFS, "Null smb session\n");
@@ -801,10 +837,9 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
           to the same server. We may make this configurable later or
           use ses->maxReq */
 
-       if (be32_to_cpu(in_buf->smb_buf_length) > CIFSMaxBufSize +
-                       MAX_CIFS_HDR_SIZE - 4) {
+       if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
                cifs_dbg(VFS, "Illegal length, greater than maximum frame, %d\n",
-                        be32_to_cpu(in_buf->smb_buf_length));
+                        len);
                return -EIO;
        }
 
@@ -835,7 +870,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
        midQ->mid_state = MID_REQUEST_SUBMITTED;
 
        cifs_in_send_inc(ses->server);
-       rc = smb_send(ses->server, in_buf, be32_to_cpu(in_buf->smb_buf_length));
+       rc = smb_send(ses->server, in_buf, len);
        cifs_in_send_dec(ses->server);
        cifs_save_when_sent(midQ);
 
@@ -852,7 +887,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
 
        rc = wait_for_response(ses->server, midQ);
        if (rc != 0) {
-               send_cancel(ses->server, in_buf, midQ);
+               send_cancel(ses->server, &rqst, midQ);
                spin_lock(&GlobalMid_Lock);
                if (midQ->mid_state == MID_REQUEST_SUBMITTED) {
                        /* no longer considered to be "in-flight" */
@@ -921,6 +956,9 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
        int rstart = 0;
        struct mid_q_entry *midQ;
        struct cifs_ses *ses;
+       unsigned int len = be32_to_cpu(in_buf->smb_buf_length);
+       struct kvec iov = { .iov_base = in_buf, .iov_len = len };
+       struct smb_rqst rqst = { .rq_iov = &iov, .rq_nvec = 1 };
 
        if (tcon == NULL || tcon->ses == NULL) {
                cifs_dbg(VFS, "Null smb session\n");
@@ -940,10 +978,9 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
           to the same server. We may make this configurable later or
           use ses->maxReq */
 
-       if (be32_to_cpu(in_buf->smb_buf_length) > CIFSMaxBufSize +
-                       MAX_CIFS_HDR_SIZE - 4) {
+       if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
                cifs_dbg(VFS, "Illegal length, greater than maximum frame, %d\n",
-                        be32_to_cpu(in_buf->smb_buf_length));
+                        len);
                return -EIO;
        }
 
@@ -972,7 +1009,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
 
        midQ->mid_state = MID_REQUEST_SUBMITTED;
        cifs_in_send_inc(ses->server);
-       rc = smb_send(ses->server, in_buf, be32_to_cpu(in_buf->smb_buf_length));
+       rc = smb_send(ses->server, in_buf, len);
        cifs_in_send_dec(ses->server);
        cifs_save_when_sent(midQ);
 
@@ -1001,7 +1038,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
                if (in_buf->Command == SMB_COM_TRANSACTION2) {
                        /* POSIX lock. We send a NT_CANCEL SMB to cause the
                           blocking lock to return. */
-                       rc = send_cancel(ses->server, in_buf, midQ);
+                       rc = send_cancel(ses->server, &rqst, midQ);
                        if (rc) {
                                cifs_delete_mid(midQ);
                                return rc;
@@ -1022,7 +1059,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
 
                rc = wait_for_response(ses->server, midQ);
                if (rc) {
-                       send_cancel(ses->server, in_buf, midQ);
+                       send_cancel(ses->server, &rqst, midQ);
                        spin_lock(&GlobalMid_Lock);
                        if (midQ->mid_state == MID_REQUEST_SUBMITTED) {
                                /* no longer considered to be "in-flight" */