static int cifs_setup_async_request(struct TCP_Server_Info *server, struct kvec *iov, unsigned int nvec, struct mid_q_entry **ret_mid) { int rc; struct smb_hdr *hdr = (struct smb_hdr *)iov[0].iov_base; struct mid_q_entry *mid; /* enable signing if server requires it */ if (server->sec_mode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) hdr->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; mid = AllocMidQEntry(hdr, server); if (mid == NULL) return -ENOMEM; /* put it on the pending_mid_q */ spin_lock(&GlobalMid_Lock); list_add_tail(&mid->qhead, &server->pending_mid_q); spin_unlock(&GlobalMid_Lock); rc = cifs_sign_smb2(iov, nvec, server, &mid->sequence_number); if (rc) delete_mid(mid); *ret_mid = mid; return rc; }
/* * Send a SMB request and set the callback function in the mid to handle * the result. Caller is responsible for dealing with timeouts. */ int cifs_call_async(struct TCP_Server_Info *server, struct kvec *iov, unsigned int nvec, mid_callback_t *callback, void *cbdata, bool ignore_pend) { int rc; struct mid_q_entry *mid; struct smb_hdr *hdr = (struct smb_hdr *)iov[0].iov_base; rc = wait_for_free_request(server, ignore_pend ? CIFS_ASYNC_OP : 0); if (rc) return rc; /* enable signing if server requires it */ if (server->sec_mode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) hdr->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; mutex_lock(&server->srv_mutex); mid = AllocMidQEntry(hdr, server); if (mid == NULL) { mutex_unlock(&server->srv_mutex); atomic_dec(&server->inFlight); wake_up(&server->request_q); return -ENOMEM; } /* put it on the pending_mid_q */ spin_lock(&GlobalMid_Lock); list_add_tail(&mid->qhead, &server->pending_mid_q); spin_unlock(&GlobalMid_Lock); rc = cifs_sign_smb2(iov, nvec, server, &mid->sequence_number); if (rc) { mutex_unlock(&server->srv_mutex); goto out_err; } mid->callback = callback; mid->callback_data = cbdata; mid->midState = MID_REQUEST_SUBMITTED; #ifdef CONFIG_CIFS_STATS2 atomic_inc(&server->inSend); #endif rc = smb_sendv(server, iov, nvec); #ifdef CONFIG_CIFS_STATS2 atomic_dec(&server->inSend); mid->when_sent = jiffies; #endif mutex_unlock(&server->srv_mutex); if (rc) goto out_err; return rc; out_err: delete_mid(mid); atomic_dec(&server->inFlight); wake_up(&server->request_q); return rc; }
/* * Send a SMB request and set the callback function in the mid to handle * the result. Caller is responsible for dealing with timeouts. */ int cifs_call_async(struct TCP_Server_Info *server, struct smb_hdr *in_buf, mid_callback_t *callback, void *cbdata) { int rc; struct mid_q_entry *mid; rc = wait_for_free_request(server, CIFS_ASYNC_OP); if (rc) return rc; mutex_lock(&server->srv_mutex); mid = AllocMidQEntry(in_buf, server); if (mid == NULL) { mutex_unlock(&server->srv_mutex); return -ENOMEM; } /* put it on the pending_mid_q */ spin_lock(&GlobalMid_Lock); list_add_tail(&mid->qhead, &server->pending_mid_q); spin_unlock(&GlobalMid_Lock); rc = cifs_sign_smb(in_buf, server, &mid->sequence_number); if (rc) { mutex_unlock(&server->srv_mutex); goto out_err; } mid->callback = callback; mid->callback_data = cbdata; mid->midState = MID_REQUEST_SUBMITTED; #ifdef CONFIG_CIFS_STATS2 atomic_inc(&server->inSend); #endif rc = smb_send(server, in_buf, in_buf->smb_buf_length); #ifdef CONFIG_CIFS_STATS2 atomic_dec(&server->inSend); mid->when_sent = jiffies; #endif mutex_unlock(&server->srv_mutex); if (rc) goto out_err; return rc; out_err: delete_mid(mid); atomic_dec(&server->inFlight); wake_up(&server->request_q); return rc; }
static int cifs_setup_request(struct cifs_ses *ses, struct kvec *iov, unsigned int nvec, struct mid_q_entry **ret_mid) { int rc; struct smb_hdr *hdr = (struct smb_hdr *)iov[0].iov_base; struct mid_q_entry *mid; rc = allocate_mid(ses, hdr, &mid); if (rc) return rc; rc = cifs_sign_smb2(iov, nvec, ses->server, &mid->sequence_number); if (rc) delete_mid(mid); *ret_mid = mid; return rc; }
/* * Send a SMB request and set the callback function in the mid to handle * the result. Caller is responsible for dealing with timeouts. */ int cifs_call_async(struct TCP_Server_Info *server, struct kvec *iov, unsigned int nvec, mid_receive_t *receive, mid_callback_t *callback, void *cbdata, bool ignore_pend) { int rc; struct mid_q_entry *mid; rc = wait_for_free_request(server, ignore_pend ? CIFS_ASYNC_OP : 0); if (rc) return rc; mutex_lock(&server->srv_mutex); rc = cifs_setup_async_request(server, iov, nvec, &mid); if (rc) { mutex_unlock(&server->srv_mutex); cifs_add_credits(server, 1); wake_up(&server->request_q); return rc; } mid->receive = receive; mid->callback = callback; mid->callback_data = cbdata; mid->mid_state = MID_REQUEST_SUBMITTED; cifs_in_send_inc(server); rc = smb_sendv(server, iov, nvec); cifs_in_send_dec(server); cifs_save_when_sent(mid); mutex_unlock(&server->srv_mutex); if (rc) goto out_err; return rc; out_err: delete_mid(mid); cifs_add_credits(server, 1); wake_up(&server->request_q); return rc; }
int SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon, struct smb_hdr *in_buf, struct smb_hdr *out_buf, int *pbytes_returned) { int rc = 0; int rstart = 0; struct mid_q_entry *midQ; struct cifs_ses *ses; if (tcon == NULL || tcon->ses == NULL) { cERROR(1, "Null smb session"); return -EIO; } ses = tcon->ses; if (ses->server == NULL) { cERROR(1, "Null tcp session"); return -EIO; } if (ses->server->tcpStatus == CifsExiting) return -ENOENT; /* Ensure that we do not send more than 50 overlapping requests 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) { cERROR(1, "Illegal length, greater than maximum frame, %d", be32_to_cpu(in_buf->smb_buf_length)); return -EIO; } rc = wait_for_free_request(ses->server, CIFS_BLOCKING_OP); if (rc) return rc; /* make sure that we sign in the same order that we send on this socket and avoid races inside tcp sendmsg code that could cause corruption of smb data */ mutex_lock(&ses->server->srv_mutex); rc = allocate_mid(ses, in_buf, &midQ); if (rc) { mutex_unlock(&ses->server->srv_mutex); return rc; } rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number); if (rc) { delete_mid(midQ); mutex_unlock(&ses->server->srv_mutex); return rc; } 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)); cifs_in_send_dec(ses->server); cifs_save_when_sent(midQ); mutex_unlock(&ses->server->srv_mutex); if (rc < 0) { delete_mid(midQ); return rc; } /* Wait for a reply - allow signals to interrupt. */ rc = wait_event_interruptible(ses->server->response_q, (!(midQ->mid_state == MID_REQUEST_SUBMITTED)) || ((ses->server->tcpStatus != CifsGood) && (ses->server->tcpStatus != CifsNew))); /* Were we interrupted by a signal ? */ if ((rc == -ERESTARTSYS) && (midQ->mid_state == MID_REQUEST_SUBMITTED) && ((ses->server->tcpStatus == CifsGood) || (ses->server->tcpStatus == CifsNew))) { if (in_buf->Command == SMB_COM_TRANSACTION2) { /* POSIX lock. We send a NT_CANCEL SMB to cause the blocking lock to return. */ rc = send_nt_cancel(ses->server, in_buf, midQ); if (rc) { delete_mid(midQ); return rc; } } else { /* Windows lock. We send a LOCKINGX_CANCEL_LOCK to cause the blocking lock to return. */ rc = send_lock_cancel(xid, tcon, in_buf, out_buf); /* If we get -ENOLCK back the lock may have already been removed. Don't exit in this case. */ if (rc && rc != -ENOLCK) { delete_mid(midQ); return rc; } } rc = wait_for_response(ses->server, midQ); if (rc) { send_nt_cancel(ses->server, in_buf, midQ); spin_lock(&GlobalMid_Lock); if (midQ->mid_state == MID_REQUEST_SUBMITTED) { /* no longer considered to be "in-flight" */ midQ->callback = DeleteMidQEntry; spin_unlock(&GlobalMid_Lock); return rc; } spin_unlock(&GlobalMid_Lock); } /* We got the response - restart system call. */ rstart = 1; } rc = cifs_sync_mid_result(midQ, ses->server); if (rc != 0) return rc; /* rcvd frame is ok */ if (out_buf == NULL || midQ->mid_state != MID_RESPONSE_RECEIVED) { rc = -EIO; cERROR(1, "Bad MID state?"); goto out; } *pbytes_returned = get_rfc1002_length(midQ->resp_buf); memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4); rc = cifs_check_receive(midQ, ses->server, 0); out: delete_mid(midQ); if (rstart && rc == -EACCES) return -ERESTARTSYS; 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 long_op) { int rc = 0; struct mid_q_entry *midQ; if (ses == NULL) { cERROR(1, "Null smb session"); return -EIO; } if (ses->server == NULL) { cERROR(1, "Null tcp session"); return -EIO; } if (ses->server->tcpStatus == CifsExiting) return -ENOENT; /* Ensure that we do not send more than 50 overlapping requests 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) { cERROR(1, "Illegal length, greater than maximum frame, %d", be32_to_cpu(in_buf->smb_buf_length)); return -EIO; } rc = wait_for_free_request(ses->server, long_op); if (rc) return rc; /* make sure that we sign in the same order that we send on this socket and avoid races inside tcp sendmsg code that could cause corruption of smb data */ mutex_lock(&ses->server->srv_mutex); rc = allocate_mid(ses, in_buf, &midQ); if (rc) { mutex_unlock(&ses->server->srv_mutex); /* Update # of requests on wire to server */ cifs_add_credits(ses->server, 1); return rc; } rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number); if (rc) { mutex_unlock(&ses->server->srv_mutex); goto out; } 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)); cifs_in_send_dec(ses->server); cifs_save_when_sent(midQ); mutex_unlock(&ses->server->srv_mutex); if (rc < 0) goto out; if (long_op == CIFS_ASYNC_OP) goto out; rc = wait_for_response(ses->server, midQ); if (rc != 0) { send_nt_cancel(ses->server, in_buf, midQ); spin_lock(&GlobalMid_Lock); if (midQ->mid_state == MID_REQUEST_SUBMITTED) { /* no longer considered to be "in-flight" */ midQ->callback = DeleteMidQEntry; spin_unlock(&GlobalMid_Lock); cifs_add_credits(ses->server, 1); return rc; } spin_unlock(&GlobalMid_Lock); } rc = cifs_sync_mid_result(midQ, ses->server); if (rc != 0) { cifs_add_credits(ses->server, 1); return rc; } if (!midQ->resp_buf || !out_buf || midQ->mid_state != MID_RESPONSE_RECEIVED) { rc = -EIO; cERROR(1, "Bad MID state?"); goto out; } *pbytes_returned = get_rfc1002_length(midQ->resp_buf); memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4); rc = cifs_check_receive(midQ, ses->server, 0); out: delete_mid(midQ); cifs_add_credits(ses->server, 1); return rc; }
int SendReceive2(const unsigned int xid, struct cifs_ses *ses, struct kvec *iov, int n_vec, int *pRespBufType /* ret */, const int flags) { int rc = 0; int long_op; struct mid_q_entry *midQ = NULL; char *buf = iov[0].iov_base; long_op = flags & CIFS_TIMEOUT_MASK; *pRespBufType = CIFS_NO_BUFFER; /* no response buf yet */ if ((ses == NULL) || (ses->server == NULL)) { cifs_small_buf_release(buf); cERROR(1, "Null session"); return -EIO; } if (ses->server->tcpStatus == CifsExiting) { cifs_small_buf_release(buf); return -ENOENT; } /* * Ensure that we do not send more than 50 overlapping requests * to the same server. We may make this configurable later or * use ses->maxReq. */ rc = wait_for_free_request(ses->server, long_op); if (rc) { cifs_small_buf_release(buf); return rc; } /* * Make sure that we sign in the same order that we send on this socket * and avoid races inside tcp sendmsg code that could cause corruption * of smb data. */ mutex_lock(&ses->server->srv_mutex); rc = cifs_setup_request(ses, iov, n_vec, &midQ); if (rc) { mutex_unlock(&ses->server->srv_mutex); cifs_small_buf_release(buf); /* Update # of requests on wire to server */ cifs_add_credits(ses->server, 1); return rc; } midQ->mid_state = MID_REQUEST_SUBMITTED; cifs_in_send_inc(ses->server); rc = smb_sendv(ses->server, iov, n_vec); cifs_in_send_dec(ses->server); cifs_save_when_sent(midQ); mutex_unlock(&ses->server->srv_mutex); if (rc < 0) { cifs_small_buf_release(buf); goto out; } if (long_op == CIFS_ASYNC_OP) { cifs_small_buf_release(buf); goto out; } rc = wait_for_response(ses->server, midQ); if (rc != 0) { send_nt_cancel(ses->server, (struct smb_hdr *)buf, midQ); spin_lock(&GlobalMid_Lock); if (midQ->mid_state == MID_REQUEST_SUBMITTED) { midQ->callback = DeleteMidQEntry; spin_unlock(&GlobalMid_Lock); cifs_small_buf_release(buf); cifs_add_credits(ses->server, 1); return rc; } spin_unlock(&GlobalMid_Lock); } cifs_small_buf_release(buf); rc = cifs_sync_mid_result(midQ, ses->server); if (rc != 0) { cifs_add_credits(ses->server, 1); return rc; } if (!midQ->resp_buf || midQ->mid_state != MID_RESPONSE_RECEIVED) { rc = -EIO; cFYI(1, "Bad MID state?"); goto out; } buf = (char *)midQ->resp_buf; iov[0].iov_base = buf; iov[0].iov_len = get_rfc1002_length(buf) + 4; if (midQ->large_buf) *pRespBufType = CIFS_LARGE_BUFFER; else *pRespBufType = CIFS_SMALL_BUFFER; rc = cifs_check_receive(midQ, ses->server, flags & CIFS_LOG_ERROR); /* mark it so buf will not be freed by delete_mid */ if ((flags & CIFS_NO_RESP) == 0) midQ->resp_buf = NULL; out: delete_mid(midQ); cifs_add_credits(ses->server, 1); return rc; }
int SendReceive2(const unsigned int xid, struct cifs_ses *ses, struct kvec *iov, int n_vec, int *pRespBufType /* ret */, const int flags) { int rc = 0; int long_op; struct mid_q_entry *midQ; struct smb_hdr *in_buf = iov[0].iov_base; long_op = flags & CIFS_TIMEOUT_MASK; *pRespBufType = CIFS_NO_BUFFER; /* no response buf yet */ if ((ses == NULL) || (ses->server == NULL)) { cifs_small_buf_release(in_buf); cERROR(1, "Null session"); return -EIO; } if (ses->server->tcpStatus == CifsExiting) { cifs_small_buf_release(in_buf); return -ENOENT; } /* Ensure that we do not send more than 50 overlapping requests to the same server. We may make this configurable later or use ses->maxReq */ rc = wait_for_free_request(ses->server, long_op); if (rc) { cifs_small_buf_release(in_buf); return rc; } /* make sure that we sign in the same order that we send on this socket and avoid races inside tcp sendmsg code that could cause corruption of smb data */ mutex_lock(&ses->server->srv_mutex); rc = allocate_mid(ses, in_buf, &midQ); if (rc) { mutex_unlock(&ses->server->srv_mutex); cifs_small_buf_release(in_buf); /* Update # of requests on wire to server */ atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); return rc; } rc = cifs_sign_smb2(iov, n_vec, ses->server, &midQ->sequence_number); if (rc) { mutex_unlock(&ses->server->srv_mutex); cifs_small_buf_release(in_buf); goto out; } midQ->midState = MID_REQUEST_SUBMITTED; #ifdef CONFIG_CIFS_STATS2 atomic_inc(&ses->server->inSend); #endif rc = smb_sendv(ses->server, iov, n_vec); #ifdef CONFIG_CIFS_STATS2 atomic_dec(&ses->server->inSend); midQ->when_sent = jiffies; #endif mutex_unlock(&ses->server->srv_mutex); if (rc < 0) { cifs_small_buf_release(in_buf); goto out; } if (long_op == CIFS_ASYNC_OP) { cifs_small_buf_release(in_buf); goto out; } rc = wait_for_response(ses->server, midQ); if (rc != 0) { send_nt_cancel(ses->server, in_buf, midQ); spin_lock(&GlobalMid_Lock); if (midQ->midState == MID_REQUEST_SUBMITTED) { midQ->callback = DeleteMidQEntry; spin_unlock(&GlobalMid_Lock); cifs_small_buf_release(in_buf); atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); return rc; } spin_unlock(&GlobalMid_Lock); } cifs_small_buf_release(in_buf); rc = cifs_sync_mid_result(midQ, ses->server); if (rc != 0) { atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); return rc; } if (!midQ->resp_buf || midQ->midState != MID_RESPONSE_RECEIVED) { rc = -EIO; cFYI(1, "Bad MID state?"); goto out; } iov[0].iov_base = (char *)midQ->resp_buf; iov[0].iov_len = be32_to_cpu(midQ->resp_buf->smb_buf_length) + 4; if (midQ->largeBuf) *pRespBufType = CIFS_LARGE_BUFFER; else *pRespBufType = CIFS_SMALL_BUFFER; rc = cifs_check_receive(midQ, ses->server, flags & CIFS_LOG_ERROR); /* mark it so buf will not be freed by delete_mid */ if ((flags & CIFS_NO_RESP) == 0) midQ->resp_buf = NULL; out: delete_mid(midQ); atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); return rc; }
int SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon, struct smb_hdr *in_buf, struct smb_hdr *out_buf, int *pbytes_returned) { int rc = 0; int rstart = 0; unsigned int receive_len; struct mid_q_entry *midQ; struct cifsSesInfo *ses; if (tcon == NULL || tcon->ses == NULL) { cERROR(1, "Null smb session"); return -EIO; } ses = tcon->ses; if (ses->server == NULL) { cERROR(1, "Null tcp session"); return -EIO; } if (ses->server->tcpStatus == CifsExiting) return -ENOENT; /* Ensure that we do not send more than 50 overlapping requests to the same server. We may make this configurable later or use ses->maxReq */ if (in_buf->smb_buf_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { cERROR(1, "Illegal length, greater than maximum frame, %d", in_buf->smb_buf_length); return -EIO; } rc = wait_for_free_request(ses->server, CIFS_BLOCKING_OP); if (rc) return rc; /* make sure that we sign in the same order that we send on this socket and avoid races inside tcp sendmsg code that could cause corruption of smb data */ mutex_lock(&ses->server->srv_mutex); rc = allocate_mid(ses, in_buf, &midQ); if (rc) { mutex_unlock(&ses->server->srv_mutex); return rc; } rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number); if (rc) { delete_mid(midQ); mutex_unlock(&ses->server->srv_mutex); return rc; } midQ->midState = MID_REQUEST_SUBMITTED; #ifdef CONFIG_CIFS_STATS2 atomic_inc(&ses->server->inSend); #endif rc = smb_send(ses->server, in_buf, in_buf->smb_buf_length); #ifdef CONFIG_CIFS_STATS2 atomic_dec(&ses->server->inSend); midQ->when_sent = jiffies; #endif mutex_unlock(&ses->server->srv_mutex); if (rc < 0) { delete_mid(midQ); return rc; } /* Wait for a reply - allow signals to interrupt. */ rc = wait_event_interruptible(ses->server->response_q, (!(midQ->midState == MID_REQUEST_SUBMITTED)) || ((ses->server->tcpStatus != CifsGood) && (ses->server->tcpStatus != CifsNew))); /* Were we interrupted by a signal ? */ if ((rc == -ERESTARTSYS) && (midQ->midState == MID_REQUEST_SUBMITTED) && ((ses->server->tcpStatus == CifsGood) || (ses->server->tcpStatus == CifsNew))) { if (in_buf->Command == SMB_COM_TRANSACTION2) { /* POSIX lock. We send a NT_CANCEL SMB to cause the blocking lock to return. */ rc = send_nt_cancel(ses->server, in_buf, midQ); if (rc) { delete_mid(midQ); return rc; } } else { /* Windows lock. We send a LOCKINGX_CANCEL_LOCK to cause the blocking lock to return. */ rc = send_lock_cancel(xid, tcon, in_buf, out_buf); /* If we get -ENOLCK back the lock may have already been removed. Don't exit in this case. */ if (rc && rc != -ENOLCK) { delete_mid(midQ); return rc; } } if (wait_for_response(ses->server, midQ) == 0) { /* We got the response - restart system call. */ rstart = 1; } } rc = sync_mid_result(midQ, ses->server); if (rc != 0) return rc; receive_len = midQ->resp_buf->smb_buf_length; if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) { cERROR(1, "Frame too large received. Length: %d Xid: %d", receive_len, xid); rc = -EIO; goto out; } /* rcvd frame is ok */ if ((out_buf == NULL) || (midQ->midState != MID_RESPONSE_RECEIVED)) { rc = -EIO; cERROR(1, "Bad MID state?"); goto out; } out_buf->smb_buf_length = receive_len; memcpy((char *)out_buf + 4, (char *)midQ->resp_buf + 4, receive_len); dump_smb(out_buf, 92); /* convert the length into a more usable form */ if ((receive_len > 24) && (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))) { rc = cifs_verify_signature(out_buf, ses->server, midQ->sequence_number+1); if (rc) { cERROR(1, "Unexpected SMB signature"); /* BB FIXME add code to kill session */ } } *pbytes_returned = out_buf->smb_buf_length; /* BB special case reconnect tid and uid here? */ rc = map_smb_to_linux_error(out_buf, 0 /* no log */ ); /* convert ByteCount if necessary */ if (receive_len >= sizeof(struct smb_hdr) - 4 /* do not count RFC1001 header */ + (2 * out_buf->WordCount) + 2 /* bcc */ ) put_bcc(get_bcc_le(out_buf), out_buf); out: delete_mid(midQ); if (rstart && rc == -EACCES) return -ERESTARTSYS; return rc; }
int SendReceive(const unsigned int xid, struct cifsSesInfo *ses, struct smb_hdr *in_buf, struct smb_hdr *out_buf, int *pbytes_returned, const int long_op) { int rc = 0; unsigned int receive_len; struct mid_q_entry *midQ; if (ses == NULL) { cERROR(1, "Null smb session"); return -EIO; } if (ses->server == NULL) { cERROR(1, "Null tcp session"); return -EIO; } if (ses->server->tcpStatus == CifsExiting) return -ENOENT; /* Ensure that we do not send more than 50 overlapping requests to the same server. We may make this configurable later or use ses->maxReq */ if (in_buf->smb_buf_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { cERROR(1, "Illegal length, greater than maximum frame, %d", in_buf->smb_buf_length); return -EIO; } rc = wait_for_free_request(ses->server, long_op); if (rc) return rc; /* make sure that we sign in the same order that we send on this socket and avoid races inside tcp sendmsg code that could cause corruption of smb data */ mutex_lock(&ses->server->srv_mutex); rc = allocate_mid(ses, in_buf, &midQ); if (rc) { mutex_unlock(&ses->server->srv_mutex); /* Update # of requests on wire to server */ atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); return rc; } rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number); if (rc) { mutex_unlock(&ses->server->srv_mutex); goto out; } midQ->midState = MID_REQUEST_SUBMITTED; #ifdef CONFIG_CIFS_STATS2 atomic_inc(&ses->server->inSend); #endif rc = smb_send(ses->server, in_buf, in_buf->smb_buf_length); #ifdef CONFIG_CIFS_STATS2 atomic_dec(&ses->server->inSend); midQ->when_sent = jiffies; #endif mutex_unlock(&ses->server->srv_mutex); if (rc < 0) goto out; if (long_op == CIFS_ASYNC_OP) goto out; rc = wait_for_response(ses->server, midQ); if (rc != 0) goto out; rc = sync_mid_result(midQ, ses->server); if (rc != 0) { atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); return rc; } receive_len = midQ->resp_buf->smb_buf_length; if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) { cERROR(1, "Frame too large received. Length: %d Xid: %d", receive_len, xid); rc = -EIO; goto out; } /* rcvd frame is ok */ if (midQ->resp_buf && out_buf && (midQ->midState == MID_RESPONSE_RECEIVED)) { out_buf->smb_buf_length = receive_len; memcpy((char *)out_buf + 4, (char *)midQ->resp_buf + 4, receive_len); dump_smb(out_buf, 92); /* convert the length into a more usable form */ if ((receive_len > 24) && (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))) { rc = cifs_verify_signature(out_buf, ses->server, midQ->sequence_number+1); if (rc) { cERROR(1, "Unexpected SMB signature"); /* BB FIXME add code to kill session */ } } *pbytes_returned = out_buf->smb_buf_length; /* BB special case reconnect tid and uid here? */ rc = map_smb_to_linux_error(out_buf, 0 /* no log */ ); /* convert ByteCount if necessary */ if (receive_len >= sizeof(struct smb_hdr) - 4 /* do not count RFC1001 header */ + (2 * out_buf->WordCount) + 2 /* bcc */ ) put_bcc(get_bcc_le(midQ->resp_buf), midQ->resp_buf); } else { rc = -EIO; cERROR(1, "Bad MID state?"); } out: delete_mid(midQ); atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); return rc; }
int SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, struct kvec *iov, int n_vec, int *pRespBufType /* ret */, const int flags) { int rc = 0; int long_op; unsigned int receive_len; struct mid_q_entry *midQ; struct smb_hdr *in_buf = iov[0].iov_base; long_op = flags & CIFS_TIMEOUT_MASK; *pRespBufType = CIFS_NO_BUFFER; /* no response buf yet */ if ((ses == NULL) || (ses->server == NULL)) { cifs_small_buf_release(in_buf); cERROR(1, "Null session"); return -EIO; } if (ses->server->tcpStatus == CifsExiting) { cifs_small_buf_release(in_buf); return -ENOENT; } /* Ensure that we do not send more than 50 overlapping requests to the same server. We may make this configurable later or use ses->maxReq */ rc = wait_for_free_request(ses->server, long_op); if (rc) { cifs_small_buf_release(in_buf); return rc; } /* make sure that we sign in the same order that we send on this socket and avoid races inside tcp sendmsg code that could cause corruption of smb data */ mutex_lock(&ses->server->srv_mutex); rc = allocate_mid(ses, in_buf, &midQ); if (rc) { mutex_unlock(&ses->server->srv_mutex); cifs_small_buf_release(in_buf); /* Update # of requests on wire to server */ atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); return rc; } rc = cifs_sign_smb2(iov, n_vec, ses->server, &midQ->sequence_number); if (rc) { mutex_unlock(&ses->server->srv_mutex); cifs_small_buf_release(in_buf); goto out; } midQ->midState = MID_REQUEST_SUBMITTED; #ifdef CONFIG_CIFS_STATS2 atomic_inc(&ses->server->inSend); #endif rc = smb_sendv(ses->server, iov, n_vec); #ifdef CONFIG_CIFS_STATS2 atomic_dec(&ses->server->inSend); midQ->when_sent = jiffies; #endif mutex_unlock(&ses->server->srv_mutex); cifs_small_buf_release(in_buf); if (rc < 0) goto out; if (long_op == CIFS_ASYNC_OP) goto out; rc = wait_for_response(ses->server, midQ); if (rc != 0) goto out; rc = sync_mid_result(midQ, ses->server); if (rc != 0) { atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); return rc; } receive_len = midQ->resp_buf->smb_buf_length; if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) { cERROR(1, "Frame too large received. Length: %d Xid: %d", receive_len, xid); rc = -EIO; goto out; } /* rcvd frame is ok */ if (midQ->resp_buf && (midQ->midState == MID_RESPONSE_RECEIVED)) { iov[0].iov_base = (char *)midQ->resp_buf; if (midQ->largeBuf) *pRespBufType = CIFS_LARGE_BUFFER; else *pRespBufType = CIFS_SMALL_BUFFER; iov[0].iov_len = receive_len + 4; dump_smb(midQ->resp_buf, 80); /* convert the length into a more usable form */ if ((receive_len > 24) && (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))) { rc = cifs_verify_signature(midQ->resp_buf, ses->server, midQ->sequence_number+1); if (rc) { cERROR(1, "Unexpected SMB signature"); /* BB FIXME add code to kill session */ } } /* BB special case reconnect tid and uid here? */ rc = map_smb_to_linux_error(midQ->resp_buf, flags & CIFS_LOG_ERROR); /* convert ByteCount if necessary */ if (receive_len >= sizeof(struct smb_hdr) - 4 /* do not count RFC1001 header */ + (2 * midQ->resp_buf->WordCount) + 2 /* bcc */ ) put_bcc(get_bcc_le(midQ->resp_buf), midQ->resp_buf); if ((flags & CIFS_NO_RESP) == 0) midQ->resp_buf = NULL; /* mark it so buf will not be freed by delete_mid */ } else { rc = -EIO; cFYI(1, "Bad MID state?"); } out: delete_mid(midQ); atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); return rc; }