diff options
Diffstat (limited to 'fs/cifs/transport.c')
-rw-r--r-- | fs/cifs/transport.c | 171 |
1 files changed, 113 insertions, 58 deletions
diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index fbb84c08e3cd..526f0533cb4e 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -221,7 +221,7 @@ rqst_len(struct smb_rqst *rqst) } static int -smb_send_rqst(struct TCP_Server_Info *server, struct smb_rqst *rqst) +__smb_send_rqst(struct TCP_Server_Info *server, struct smb_rqst *rqst) { int rc; struct kvec *iov = rqst->rq_iov; @@ -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, @@ -309,24 +313,43 @@ uncork: } static int -smb_sendv(struct TCP_Server_Info *server, struct kvec *iov, int n_vec) +smb_send_rqst(struct TCP_Server_Info *server, struct smb_rqst *rqst, int flags) { - struct smb_rqst rqst = { .rq_iov = iov, - .rq_nvec = n_vec }; + struct smb_rqst cur_rqst; + int rc; + + if (!(flags & CIFS_TRANSFORM_REQ)) + return __smb_send_rqst(server, rqst); + + if (!server->ops->init_transform_rq || + !server->ops->free_transform_rq) { + cifs_dbg(VFS, "Encryption requested but transform callbacks are missed\n"); + return -EIO; + } + + rc = server->ops->init_transform_rq(server, &cur_rqst, rqst); + if (rc) + return rc; - return smb_send_rqst(server, &rqst); + rc = __smb_send_rqst(server, &cur_rqst); + server->ops->free_transform_rq(&cur_rqst); + return rc; } 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]; + struct smb_rqst rqst = { .rq_iov = iov, + .rq_nvec = 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_send_rqst(server, &rqst); } static int @@ -454,6 +477,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; @@ -478,7 +505,7 @@ 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_handle_t *handle, void *cbdata, const int flags) { int rc, timeout, optype; struct mid_q_entry *mid; @@ -505,6 +532,7 @@ cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst, mid->receive = receive; mid->callback = callback; mid->callback_data = cbdata; + mid->handle = handle; mid->mid_state = MID_REQUEST_SUBMITTED; /* put it on the pending_mid_q */ @@ -514,7 +542,7 @@ cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst, cifs_in_send_inc(server); - rc = smb_send_rqst(server, rqst); + rc = smb_send_rqst(server, rqst, flags); cifs_in_send_dec(server); cifs_save_when_sent(mid); @@ -547,12 +575,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 +624,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 +641,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 +669,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 +685,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 +701,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 +715,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 +726,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 +736,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, flags); cifs_in_send_dec(ses->server); cifs_save_when_sent(midQ); @@ -716,32 +744,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 +776,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 @@ -778,12 +799,45 @@ out: } 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, int *pbytes_returned, const int timeout) { 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 +855,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 +888,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 +905,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 +974,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 +996,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 +1027,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 +1056,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 +1077,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" */ |