/* * An NT cancel request header looks just like the original request except: * * The Command is SMB_COM_NT_CANCEL * The WordCount is zeroed out * The ByteCount is zeroed out * * This function mangles an existing request buffer into a * SMB_COM_NT_CANCEL request and then sends it. */ static int send_nt_cancel(struct TCP_Server_Info *server, struct smb_hdr *in_buf, struct mid_q_entry *mid) { int rc = 0; /* -4 for RFC1001 length and +2 for BCC field */ in_buf->smb_buf_length = cpu_to_be32(sizeof(struct smb_hdr) - 4 + 2); in_buf->Command = SMB_COM_NT_CANCEL; in_buf->WordCount = 0; put_bcc(0, in_buf); mutex_lock(&server->srv_mutex); rc = cifs_sign_smb(in_buf, server, &mid->sequence_number); if (rc) { mutex_unlock(&server->srv_mutex); return rc; } rc = smb_send(server, in_buf, be32_to_cpu(in_buf->smb_buf_length)); mutex_unlock(&server->srv_mutex); cFYI(1, "issued NT_CANCEL for mid %u, rc = %d", in_buf->Mid, rc); return rc; }
/* * An NT cancel request header looks just like the original request except: * * The Command is SMB_COM_NT_CANCEL * The WordCount is zeroed out * The ByteCount is zeroed out * * This function mangles an existing request buffer into a * SMB_COM_NT_CANCEL request and then sends it. */ static int send_nt_cancel(struct TCP_Server_Info *server, struct smb_hdr *in_buf, struct mid_q_entry *mid) { int rc = 0; /* -4 for RFC1001 length and +2 for BCC field */ in_buf->smb_buf_length = cpu_to_be32(sizeof(struct smb_hdr) - 4 + 2); in_buf->Command = SMB_COM_NT_CANCEL; in_buf->WordCount = 0; put_bcc(0, in_buf); mutex_lock(&server->srv_mutex); rc = cifs_sign_smb(in_buf, server, &mid->sequence_number); if (rc) { mutex_unlock(&server->srv_mutex); return rc; } /* * The response to this call was already factored into the sequence * number when the call went out, so we must adjust it back downward * after signing here. */ --server->sequence_number; rc = smb_send(server, in_buf, be32_to_cpu(in_buf->smb_buf_length)); mutex_unlock(&server->srv_mutex); cFYI(1, "issued NT_CANCEL for mid %u, rc = %d", in_buf->Mid, rc); 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 send_nt_cancel(struct cifsTconInfo *tcon, struct smb_hdr *in_buf, struct mid_q_entry *midQ) { int rc = 0; struct cifsSesInfo *ses = tcon->ses; __u16 mid = in_buf->Mid; header_assemble(in_buf, SMB_COM_NT_CANCEL, tcon, 0); in_buf->Mid = mid; mutex_lock(&ses->server->srv_mutex); rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number); if (rc) { mutex_unlock(&ses->server->srv_mutex); return rc; } rc = smb_send(ses->server, in_buf, in_buf->smb_buf_length); mutex_unlock(&ses->server->srv_mutex); return rc; }
static int send_nt_cancel(struct cifsTconInfo *tcon, struct smb_hdr *in_buf, struct mid_q_entry *midQ) { int rc = 0; struct cifsSesInfo *ses = tcon->ses; __u16 mid = in_buf->Mid; header_assemble(in_buf, SMB_COM_NT_CANCEL, tcon, 0); in_buf->Mid = mid; down(&ses->server->tcpSem); rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number); if (rc) { up(&ses->server->tcpSem); return rc; } rc = smb_send(ses->server->ssocket, in_buf, in_buf->smb_buf_length, (struct sockaddr *) &(ses->server->addr.sockAddr)); up(&ses->server->tcpSem); 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) { cifs_dbg(VFS, "Null smb session\n"); return -EIO; } ses = tcon->ses; if (ses->server == NULL) { cifs_dbg(VFS, "Null tcp session\n"); 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) { cifs_dbg(VFS, "Illegal length, greater than maximum frame, %d\n", be32_to_cpu(in_buf->smb_buf_length)); return -EIO; } rc = wait_for_free_request(ses->server, CIFS_BLOCKING_OP, 0); 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) { cifs_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); if (rc < 0) ses->server->sequence_number -= 2; mutex_unlock(&ses->server->srv_mutex); if (rc < 0) { cifs_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_cancel(ses->server, in_buf, midQ); if (rc) { cifs_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) { cifs_delete_mid(midQ); return rc; } } rc = wait_for_response(ses->server, midQ); if (rc) { send_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; cifs_dbg(VFS, "Bad MID state?\n"); 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: cifs_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 timeout) { int rc = 0; struct mid_q_entry *midQ; if (ses == NULL) { cifs_dbg(VFS, "Null smb session\n"); return -EIO; } if (ses->server == NULL) { cifs_dbg(VFS, "Null tcp session\n"); 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) { cifs_dbg(VFS, "Illegal length, greater than maximum frame, %d\n", be32_to_cpu(in_buf->smb_buf_length)); return -EIO; } rc = wait_for_free_request(ses->server, timeout, 0); 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 */ add_credits(ses->server, 1, 0); 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); if (rc < 0) ses->server->sequence_number -= 2; mutex_unlock(&ses->server->srv_mutex); if (rc < 0) goto out; if (timeout == CIFS_ASYNC_OP) goto out; rc = wait_for_response(ses->server, midQ); if (rc != 0) { send_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); add_credits(ses->server, 1, 0); return rc; } spin_unlock(&GlobalMid_Lock); } rc = cifs_sync_mid_result(midQ, ses->server); if (rc != 0) { add_credits(ses->server, 1, 0); return rc; } if (!midQ->resp_buf || !out_buf || midQ->mid_state != MID_RESPONSE_RECEIVED) { rc = -EIO; cifs_dbg(VFS, "Bad MID state?\n"); 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: cifs_delete_mid(midQ); add_credits(ses->server, 1, 0); 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; unsigned long timeout; 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; } /* 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(long_op == -1) { /* oplock breaks must not be held up */ atomic_inc(&ses->server->inFlight); } else { spin_lock(&GlobalMid_Lock); while(1) { if(atomic_read(&ses->server->inFlight) >= CIFS_MAX_REQ){ spin_unlock(&GlobalMid_Lock); wait_event(ses->server->request_q, atomic_read(&ses->server->inFlight) < CIFS_MAX_REQ); spin_lock(&GlobalMid_Lock); } else { if(ses->server->tcpStatus == CifsExiting) { spin_unlock(&GlobalMid_Lock); return -ENOENT; } /* can not count locking commands against total since they are allowed to block on server */ if(long_op < 3) { /* update # of requests on the wire to server */ atomic_inc(&ses->server->inFlight); } spin_unlock(&GlobalMid_Lock); break; } } } /* 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 */ down(&ses->server->tcpSem); if (ses->server->tcpStatus == CifsExiting) { rc = -ENOENT; goto out_unlock; } else if (ses->server->tcpStatus == CifsNeedReconnect) { cFYI(1,("tcp session dead - return to caller to retry")); rc = -EAGAIN; goto out_unlock; } else if (ses->status != CifsGood) { /* check if SMB session is bad because we are setting it up */ if((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) && (in_buf->Command != SMB_COM_NEGOTIATE)) { rc = -EAGAIN; goto out_unlock; } /* else ok - we are setting up session */ } midQ = AllocMidQEntry(in_buf, ses); if (midQ == NULL) { up(&ses->server->tcpSem); /* If not lock req, update # of requests on wire to server */ if(long_op < 3) { atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); } return -ENOMEM; } if (in_buf->smb_buf_length > CIFS_MAX_MSGSIZE + MAX_CIFS_HDR_SIZE - 4) { up(&ses->server->tcpSem); cERROR(1, ("Illegal length, greater than maximum frame, %d ", in_buf->smb_buf_length)); DeleteMidQEntry(midQ); /* If not lock req, update # of requests on wire to server */ if(long_op < 3) { atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); } return -EIO; } rc = cifs_sign_smb(in_buf, ses, &midQ->sequence_number); midQ->midState = MID_REQUEST_SUBMITTED; rc = smb_send(ses->server->ssocket, in_buf, in_buf->smb_buf_length, (struct sockaddr *) &(ses->server->addr.sockAddr)); if(rc < 0) { DeleteMidQEntry(midQ); up(&ses->server->tcpSem); /* If not lock req, update # of requests on wire to server */ if(long_op < 3) { atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); } return rc; } else up(&ses->server->tcpSem); if (long_op == -1) goto cifs_no_response_exit; else if (long_op == 2) /* writes past end of file can take looooong time */ timeout = 300 * HZ; else if (long_op == 1) timeout = 45 * HZ; /* should be greater than servers oplock break timeout (about 43 seconds) */ else if (long_op > 2) { timeout = MAX_SCHEDULE_TIMEOUT; } else timeout = 15 * HZ; /* wait for 15 seconds or until woken up due to response arriving or due to last connection to this server being unmounted */ if (signal_pending(current)) { /* if signal pending do not hold up user for full smb timeout but we still give response a change to complete */ timeout = 2 * HZ; } /* No user interrupts in wait - wreaks havoc with performance */ if(timeout != MAX_SCHEDULE_TIMEOUT) { timeout += jiffies; wait_event(ses->server->response_q, (midQ->midState & MID_RESPONSE_RECEIVED) || time_after(jiffies, timeout) || ((ses->server->tcpStatus != CifsGood) && (ses->server->tcpStatus != CifsNew))); } else { wait_event(ses->server->response_q, (midQ->midState & MID_RESPONSE_RECEIVED) || ((ses->server->tcpStatus != CifsGood) && (ses->server->tcpStatus != CifsNew))); } spin_lock(&GlobalMid_Lock); if (midQ->resp_buf) { spin_unlock(&GlobalMid_Lock); receive_len = be32_to_cpu(*(__be32 *)midQ->resp_buf); } else { cERROR(1,("No response buffer")); if(midQ->midState == MID_REQUEST_SUBMITTED) { if(ses->server->tcpStatus == CifsExiting) rc = -EHOSTDOWN; else { ses->server->tcpStatus = CifsNeedReconnect; midQ->midState = MID_RETRY_NEEDED; } } if (rc != -EHOSTDOWN) { if(midQ->midState == MID_RETRY_NEEDED) { rc = -EAGAIN; cFYI(1,("marking request for retry")); } else { rc = -EIO; } } spin_unlock(&GlobalMid_Lock); DeleteMidQEntry(midQ); /* If not lock req, update # of requests on wire to server */ if(long_op < 3) { atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); } return rc; } if (receive_len > CIFS_MAX_MSGSIZE + MAX_CIFS_HDR_SIZE) { cERROR(1, ("Frame too large received. Length: %d Xid: %d", receive_len, xid)); rc = -EIO; } else { /* 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->mac_signing_key,midQ->sequence_number); /* BB fix BB */ if(rc) cFYI(1,("Unexpected signature received from server")); } *pbytes_returned = out_buf->smb_buf_length; /* BB special case reconnect tid and reconnect uid here? */ rc = map_smb_to_linux_error(out_buf); /* convert ByteCount if necessary */ if (receive_len >= sizeof (struct smb_hdr) - 4 /* do not count RFC1001 header */ + (2 * out_buf->WordCount) + 2 /* bcc */ ) BCC(out_buf) = le16_to_cpu(BCC(out_buf)); } else { rc = -EIO; cFYI(1,("Bad MID state? ")); } } cifs_no_response_exit: DeleteMidQEntry(midQ); if(long_op < 3) { atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); } return rc; out_unlock: up(&ses->server->tcpSem); /* If not lock req, update # of requests on wire to server */ if(long_op < 3) { 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, 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) { DeleteMidQEntry(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) { DeleteMidQEntry(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(tcon, in_buf, midQ); if (rc) { DeleteMidQEntry(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) { DeleteMidQEntry(midQ); return rc; } } /* Wait 5 seconds for the response. */ if (wait_for_response(ses, midQ, 5 * HZ, 5 * HZ) == 0) { /* We got the response - restart system call. */ rstart = 1; } } spin_lock(&GlobalMid_Lock); if (midQ->resp_buf) { spin_unlock(&GlobalMid_Lock); receive_len = midQ->resp_buf->smb_buf_length; } else { cERROR(1, "No response for cmd %d mid %d", midQ->command, midQ->mid); if (midQ->midState == MID_REQUEST_SUBMITTED) { if (ses->server->tcpStatus == CifsExiting) rc = -EHOSTDOWN; else { ses->server->tcpStatus = CifsNeedReconnect; midQ->midState = MID_RETRY_NEEDED; } } if (rc != -EHOSTDOWN) { if (midQ->midState == MID_RETRY_NEEDED) { rc = -EAGAIN; cFYI(1, "marking request for retry"); } else { rc = -EIO; } } spin_unlock(&GlobalMid_Lock); DeleteMidQEntry(midQ); return rc; } 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 */ ) BCC(out_buf) = le16_to_cpu(BCC_LE(out_buf)); out: DeleteMidQEntry(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; unsigned long timeout; 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, 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_STD_OP) timeout = 15 * HZ; /* wait for 15 seconds or until woken up due to response arriving or due to last connection to this server being unmounted */ else if (long_op == CIFS_ASYNC_OP) goto out; else if (long_op == CIFS_VLONG_OP) /* writes past EOF can be slow */ timeout = 180 * HZ; else if (long_op == CIFS_LONG_OP) timeout = 45 * HZ; /* should be greater than servers oplock break timeout (about 43 seconds) */ else if (long_op == CIFS_BLOCKING_OP) timeout = 0x7FFFFFFF; /* large but no so large as to wrap */ else { cERROR(1, "unknown timeout flag %d", long_op); rc = -EIO; goto out; } if (signal_pending(current)) { /* if signal pending do not hold up user for full smb timeout but we still give response a chance to complete */ timeout = 2 * HZ; } /* No user interrupts in wait - wreaks havoc with performance */ wait_for_response(ses, midQ, timeout, 10 * HZ); spin_lock(&GlobalMid_Lock); if (midQ->resp_buf == NULL) { cERROR(1, "No response for cmd %d mid %d", midQ->command, midQ->mid); if (midQ->midState == MID_REQUEST_SUBMITTED) { if (ses->server->tcpStatus == CifsExiting) rc = -EHOSTDOWN; else { ses->server->tcpStatus = CifsNeedReconnect; midQ->midState = MID_RETRY_NEEDED; } } if (rc != -EHOSTDOWN) { if (midQ->midState == MID_RETRY_NEEDED) { rc = -EAGAIN; cFYI(1, "marking request for retry"); } else { rc = -EIO; } } spin_unlock(&GlobalMid_Lock); DeleteMidQEntry(midQ); /* Update # of requests on wire to server */ atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); return rc; } spin_unlock(&GlobalMid_Lock); 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 */ ) BCC(out_buf) = le16_to_cpu(BCC_LE(out_buf)); } else { rc = -EIO; cERROR(1, "Bad MID state?"); } out: DeleteMidQEntry(midQ); atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); return rc; }
int CIFSSendRcv(const unsigned int xid, struct cifsSesInfo *ses, struct smb_hdr *in_buf, struct kvec * write_vector /* page list */, int *pbytes_returned, const int long_op) { int rc = 0; unsigned long timeout = 15 * HZ; struct mid_q_entry *midQ = NULL; if (ses == NULL) { cERROR(1,("Null smb session")); return -EIO; } if(ses->server == NULL) { cERROR(1,("Null tcp session")); return -EIO; } if(pbytes_returned == NULL) return -EIO; else *pbytes_returned = 0; /* 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(long_op == -1) { /* oplock breaks must not be held up */ atomic_inc(&ses->server->inFlight); } else { spin_lock(&GlobalMid_Lock); while(1) { if(atomic_read(&ses->server->inFlight) >= cifs_max_pending){ spin_unlock(&GlobalMid_Lock); wait_event(ses->server->request_q, atomic_read(&ses->server->inFlight) < cifs_max_pending); spin_lock(&GlobalMid_Lock); } else { if(ses->server->tcpStatus == CifsExiting) { spin_unlock(&GlobalMid_Lock); return -ENOENT; } /* can not count locking commands against total since they are allowed to block on server */ if(long_op < 3) { /* update # of requests on the wire to server */ atomic_inc(&ses->server->inFlight); } spin_unlock(&GlobalMid_Lock); break; } } } /* 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 */ down(&ses->server->tcpSem); if (ses->server->tcpStatus == CifsExiting) { rc = -ENOENT; goto cifs_out_label; } else if (ses->server->tcpStatus == CifsNeedReconnect) { cFYI(1,("tcp session dead - return to caller to retry")); rc = -EAGAIN; goto cifs_out_label; } else if (ses->status != CifsGood) { /* check if SMB session is bad because we are setting it up */ if((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) && (in_buf->Command != SMB_COM_NEGOTIATE)) { rc = -EAGAIN; goto cifs_out_label; } /* else ok - we are setting up session */ } midQ = AllocMidQEntry(in_buf, ses); if (midQ == NULL) { up(&ses->server->tcpSem); /* If not lock req, update # of requests on wire to server */ if(long_op < 3) { atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); } return -ENOMEM; } if (in_buf->smb_buf_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { up(&ses->server->tcpSem); cERROR(1, ("Illegal length, greater than maximum frame, %d ", in_buf->smb_buf_length)); DeleteMidQEntry(midQ); /* If not lock req, update # of requests on wire to server */ if(long_op < 3) { atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); } return -EIO; } /* BB can we sign efficiently in this path? */ rc = cifs_sign_smb(in_buf, ses, &midQ->sequence_number); midQ->midState = MID_REQUEST_SUBMITTED; /* rc = smb_sendv(ses->server->ssocket, in_buf, in_buf->smb_buf_length, piovec, (struct sockaddr *) &(ses->server->addr.sockAddr));*/ if(rc < 0) { DeleteMidQEntry(midQ); up(&ses->server->tcpSem); /* If not lock req, update # of requests on wire to server */ if(long_op < 3) { atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); } return rc; } else up(&ses->server->tcpSem); cifs_out_label: if(midQ) DeleteMidQEntry(midQ); if(long_op < 3) { atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); } return rc; }
int compound_send_recv(const unsigned int xid, struct cifs_ses *ses, const int flags, const int num_rqst, struct smb_rqst *rqst, int *resp_buf_type, struct kvec *resp_iov) { int i, j, rc = 0; int timeout, optype; struct mid_q_entry *midQ[MAX_COMPOUND]; bool cancelled_mid[MAX_COMPOUND] = {false}; unsigned int credits[MAX_COMPOUND] = {0}; char *buf; timeout = flags & CIFS_TIMEOUT_MASK; optype = flags & CIFS_OP_MASK; for (i = 0; i < num_rqst; i++) resp_buf_type[i] = CIFS_NO_BUFFER; /* no response buf yet */ if ((ses == NULL) || (ses->server == NULL)) { cifs_dbg(VFS, "Null session\n"); return -EIO; } if (ses->server->tcpStatus == CifsExiting) return -ENOENT; /* * Ensure we obtain 1 credit per request in the compound chain. * It can be optimized further by waiting for all the credits * at once but this can wait long enough if we don't have enough * credits due to some heavy operations in progress or the server * not granting us much, so a fallback to the current approach is * needed anyway. */ for (i = 0; i < num_rqst; i++) { rc = wait_for_free_request(ses->server, timeout, optype); if (rc) { /* * We haven't sent an SMB packet to the server yet but * we already obtained credits for i requests in the * compound chain - need to return those credits back * for future use. Note that we need to call add_credits * multiple times to match the way we obtained credits * in the first place and to account for in flight * requests correctly. */ for (j = 0; j < i; j++) add_credits(ses->server, 1, optype); return rc; } credits[i] = 1; } /* * 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); for (i = 0; i < num_rqst; i++) { midQ[i] = ses->server->ops->setup_request(ses, &rqst[i]); if (IS_ERR(midQ[i])) { for (j = 0; j < i; j++) cifs_delete_mid(midQ[j]); mutex_unlock(&ses->server->srv_mutex); /* Update # of requests on wire to server */ for (j = 0; j < num_rqst; j++) add_credits(ses->server, credits[j], optype); return PTR_ERR(midQ[i]); } midQ[i]->mid_state = MID_REQUEST_SUBMITTED; midQ[i]->optype = optype; /* * Invoke callback for every part of the compound chain * to calculate credits properly. Wake up this thread only when * the last element is received. */ if (i < num_rqst - 1) midQ[i]->callback = cifs_compound_callback; else midQ[i]->callback = cifs_compound_last_callback; } cifs_in_send_inc(ses->server); rc = smb_send_rqst(ses->server, num_rqst, rqst, flags); cifs_in_send_dec(ses->server); for (i = 0; i < num_rqst; i++) cifs_save_when_sent(midQ[i]); if (rc < 0) ses->server->sequence_number -= 2; mutex_unlock(&ses->server->srv_mutex); if (rc < 0) { /* Sending failed for some reason - return credits back */ for (i = 0; i < num_rqst; i++) add_credits(ses->server, credits[i], optype); goto out; } /* * At this point the request is passed to the network stack - we assume * that any credits taken from the server structure on the client have * been spent and we can't return them back. Once we receive responses * we will collect credits granted by the server in the mid callbacks * and add those credits to the server structure. */ /* * Compounding is never used during session establish. */ if ((ses->status == CifsNew) || (optype & CIFS_NEG_OP)) smb311_update_preauth_hash(ses, rqst[0].rq_iov, rqst[0].rq_nvec); if (timeout == CIFS_ASYNC_OP) goto out; for (i = 0; i < num_rqst; i++) { rc = wait_for_response(ses->server, midQ[i]); if (rc != 0) break; } if (rc != 0) { for (; i < num_rqst; i++) { cifs_dbg(VFS, "Cancelling wait for mid %llu cmd: %d\n", midQ[i]->mid, le16_to_cpu(midQ[i]->command)); send_cancel(ses->server, &rqst[i], midQ[i]); spin_lock(&GlobalMid_Lock); if (midQ[i]->mid_state == MID_REQUEST_SUBMITTED) { midQ[i]->mid_flags |= MID_WAIT_CANCELLED; midQ[i]->callback = cifs_cancelled_callback; cancelled_mid[i] = true; credits[i] = 0; } spin_unlock(&GlobalMid_Lock); } } for (i = 0; i < num_rqst; i++) { if (rc < 0) goto out; rc = cifs_sync_mid_result(midQ[i], ses->server); if (rc != 0) { /* mark this mid as cancelled to not free it below */ cancelled_mid[i] = true; goto out; } if (!midQ[i]->resp_buf || midQ[i]->mid_state != MID_RESPONSE_RECEIVED) { rc = -EIO; cifs_dbg(FYI, "Bad MID state?\n"); goto out; } buf = (char *)midQ[i]->resp_buf; resp_iov[i].iov_base = buf; resp_iov[i].iov_len = midQ[i]->resp_buf_size + ses->server->vals->header_preamble_size; if (midQ[i]->large_buf) resp_buf_type[i] = CIFS_LARGE_BUFFER; else resp_buf_type[i] = CIFS_SMALL_BUFFER; rc = ses->server->ops->check_receive(midQ[i], ses->server, flags & CIFS_LOG_ERROR); /* mark it so buf will not be freed by cifs_delete_mid */ if ((flags & CIFS_NO_RESP) == 0) midQ[i]->resp_buf = NULL; } /* * Compounding is never used during session establish. */ if ((ses->status == CifsNew) || (optype & CIFS_NEG_OP)) { struct kvec iov = { .iov_base = resp_iov[0].iov_base, .iov_len = resp_iov[0].iov_len }; smb311_update_preauth_hash(ses, &iov, 1); } out: /* * This will dequeue all mids. After this it is important that the * demultiplex_thread will not process any of these mids any futher. * This is prevented above by using a noop callback that will not * wake this thread except for the very last PDU. */ for (i = 0; i < num_rqst; i++) { if (!cancelled_mid[i]) cifs_delete_mid(midQ[i]); } return rc; } int 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) { return compound_send_recv(xid, ses, flags, 1, rqst, resp_buf_type, resp_iov); } 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 s_iov[CIFS_MAX_IOV_SIZE], *new_iov; int rc; if (n_vec + 1 > CIFS_MAX_IOV_SIZE) { new_iov = kmalloc_array(n_vec + 1, sizeof(struct kvec), GFP_KERNEL); if (!new_iov) { /* otherwise cifs_send_recv below sets resp_buf_type */ *resp_buf_type = CIFS_NO_BUFFER; return -ENOMEM; } } else new_iov = s_iov; /* 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); if (n_vec + 1 > CIFS_MAX_IOV_SIZE) 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"); return -EIO; } if (ses->server == NULL) { cifs_dbg(VFS, "Null tcp session\n"); 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 (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { cifs_dbg(VFS, "Illegal length, greater than maximum frame, %d\n", len); return -EIO; } rc = wait_for_free_request(ses->server, timeout, 0); 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 */ add_credits(ses->server, 1, 0); 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, len); cifs_in_send_dec(ses->server); cifs_save_when_sent(midQ); if (rc < 0) ses->server->sequence_number -= 2; mutex_unlock(&ses->server->srv_mutex); if (rc < 0) goto out; if (timeout == CIFS_ASYNC_OP) goto out; rc = wait_for_response(ses->server, midQ); if (rc != 0) { send_cancel(ses->server, &rqst, 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); add_credits(ses->server, 1, 0); return rc; } spin_unlock(&GlobalMid_Lock); } rc = cifs_sync_mid_result(midQ, ses->server); if (rc != 0) { add_credits(ses->server, 1, 0); return rc; } if (!midQ->resp_buf || !out_buf || midQ->mid_state != MID_RESPONSE_RECEIVED) { rc = -EIO; cifs_dbg(VFS, "Bad MID state?\n"); 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: cifs_delete_mid(midQ); add_credits(ses->server, 1, 0); return rc; } /* We send a LOCKINGX_CANCEL_LOCK to cause the Windows blocking lock to return. */ static int send_lock_cancel(const unsigned int xid, struct cifs_tcon *tcon, struct smb_hdr *in_buf, struct smb_hdr *out_buf) { int bytes_returned; struct cifs_ses *ses = tcon->ses; LOCK_REQ *pSMB = (LOCK_REQ *)in_buf; /* We just modify the current in_buf to change the type of lock from LOCKING_ANDX_SHARED_LOCK or LOCKING_ANDX_EXCLUSIVE_LOCK to LOCKING_ANDX_CANCEL_LOCK. */ pSMB->LockType = LOCKING_ANDX_CANCEL_LOCK|LOCKING_ANDX_LARGE_FILES; pSMB->Timeout = 0; pSMB->hdr.Mid = get_next_mid(ses->server); return SendReceive(xid, ses, in_buf, out_buf, &bytes_returned, 0); }
int compound_send_recv(const unsigned int xid, struct cifs_ses *ses, const int flags, const int num_rqst, struct smb_rqst *rqst, int *resp_buf_type, struct kvec *resp_iov) { int i, j, rc = 0; int timeout, optype; struct mid_q_entry *midQ[MAX_COMPOUND]; unsigned int credits = 1; char *buf; timeout = flags & CIFS_TIMEOUT_MASK; optype = flags & CIFS_OP_MASK; for (i = 0; i < num_rqst; i++) resp_buf_type[i] = CIFS_NO_BUFFER; /* no response buf yet */ if ((ses == NULL) || (ses->server == NULL)) { cifs_dbg(VFS, "Null session\n"); 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. */ rc = wait_for_free_request(ses->server, timeout, optype); 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); for (i = 0; i < num_rqst; i++) { midQ[i] = ses->server->ops->setup_request(ses, &rqst[i]); if (IS_ERR(midQ[i])) { for (j = 0; j < i; j++) cifs_delete_mid(midQ[j]); mutex_unlock(&ses->server->srv_mutex); /* Update # of requests on wire to server */ add_credits(ses->server, 1, optype); return PTR_ERR(midQ[i]); } midQ[i]->mid_state = MID_REQUEST_SUBMITTED; /* * We don't invoke the callback compounds unless it is the last * request. */ if (i < num_rqst - 1) midQ[i]->callback = cifs_noop_callback; } cifs_in_send_inc(ses->server); rc = smb_send_rqst(ses->server, num_rqst, rqst, flags); cifs_in_send_dec(ses->server); for (i = 0; i < num_rqst; i++) cifs_save_when_sent(midQ[i]); if (rc < 0) ses->server->sequence_number -= 2; mutex_unlock(&ses->server->srv_mutex); for (i = 0; i < num_rqst; i++) { if (rc < 0) goto out; if ((ses->status == CifsNew) || (optype & CIFS_NEG_OP)) smb311_update_preauth_hash(ses, rqst[i].rq_iov, rqst[i].rq_nvec); if (timeout == CIFS_ASYNC_OP) goto out; rc = wait_for_response(ses->server, midQ[i]); if (rc != 0) { cifs_dbg(FYI, "Cancelling wait for mid %llu\n", midQ[i]->mid); send_cancel(ses->server, &rqst[i], midQ[i]); spin_lock(&GlobalMid_Lock); if (midQ[i]->mid_state == MID_REQUEST_SUBMITTED) { midQ[i]->mid_flags |= MID_WAIT_CANCELLED; midQ[i]->callback = DeleteMidQEntry; spin_unlock(&GlobalMid_Lock); add_credits(ses->server, 1, optype); return rc; } spin_unlock(&GlobalMid_Lock); } rc = cifs_sync_mid_result(midQ[i], ses->server); if (rc != 0) { add_credits(ses->server, 1, optype); return rc; } if (!midQ[i]->resp_buf || midQ[i]->mid_state != MID_RESPONSE_RECEIVED) { rc = -EIO; cifs_dbg(FYI, "Bad MID state?\n"); goto out; } buf = (char *)midQ[i]->resp_buf; resp_iov[i].iov_base = buf; resp_iov[i].iov_len = midQ[i]->resp_buf_size + ses->server->vals->header_preamble_size; if (midQ[i]->large_buf) resp_buf_type[i] = CIFS_LARGE_BUFFER; else resp_buf_type[i] = CIFS_SMALL_BUFFER; if ((ses->status == CifsNew) || (optype & CIFS_NEG_OP)) { struct kvec iov = { .iov_base = resp_iov[i].iov_base, .iov_len = resp_iov[i].iov_len }; smb311_update_preauth_hash(ses, &iov, 1); } credits = ses->server->ops->get_credits(midQ[i]); rc = ses->server->ops->check_receive(midQ[i], ses->server, flags & CIFS_LOG_ERROR); /* mark it so buf will not be freed by cifs_delete_mid */ if ((flags & CIFS_NO_RESP) == 0) midQ[i]->resp_buf = NULL; } out: /* * This will dequeue all mids. After this it is important that the * demultiplex_thread will not process any of these mids any futher. * This is prevented above by using a noop callback that will not * wake this thread except for the very last PDU. */ for (i = 0; i < num_rqst; i++) cifs_delete_mid(midQ[i]); add_credits(ses->server, credits, optype); return rc; } int 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) { return compound_send_recv(xid, ses, flags, 1, rqst, resp_buf_type, resp_iov); } 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 s_iov[CIFS_MAX_IOV_SIZE], *new_iov; int rc; if (n_vec + 1 > CIFS_MAX_IOV_SIZE) { new_iov = kmalloc_array(n_vec + 1, sizeof(struct kvec), GFP_KERNEL); if (!new_iov) { /* otherwise cifs_send_recv below sets resp_buf_type */ *resp_buf_type = CIFS_NO_BUFFER; return -ENOMEM; } } else new_iov = s_iov; /* 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); if (n_vec + 1 > CIFS_MAX_IOV_SIZE) 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"); return -EIO; } if (ses->server == NULL) { cifs_dbg(VFS, "Null tcp session\n"); 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 (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { cifs_dbg(VFS, "Illegal length, greater than maximum frame, %d\n", len); return -EIO; } rc = wait_for_free_request(ses->server, timeout, 0); 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 */ add_credits(ses->server, 1, 0); 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, len); cifs_in_send_dec(ses->server); cifs_save_when_sent(midQ); if (rc < 0) ses->server->sequence_number -= 2; mutex_unlock(&ses->server->srv_mutex); if (rc < 0) goto out; if (timeout == CIFS_ASYNC_OP) goto out; rc = wait_for_response(ses->server, midQ); if (rc != 0) { send_cancel(ses->server, &rqst, 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); add_credits(ses->server, 1, 0); return rc; } spin_unlock(&GlobalMid_Lock); } rc = cifs_sync_mid_result(midQ, ses->server); if (rc != 0) { add_credits(ses->server, 1, 0); return rc; } if (!midQ->resp_buf || !out_buf || midQ->mid_state != MID_RESPONSE_RECEIVED) { rc = -EIO; cifs_dbg(VFS, "Bad MID state?\n"); 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: cifs_delete_mid(midQ); add_credits(ses->server, 1, 0); return rc; } /* We send a LOCKINGX_CANCEL_LOCK to cause the Windows blocking lock to return. */ static int send_lock_cancel(const unsigned int xid, struct cifs_tcon *tcon, struct smb_hdr *in_buf, struct smb_hdr *out_buf) { int bytes_returned; struct cifs_ses *ses = tcon->ses; LOCK_REQ *pSMB = (LOCK_REQ *)in_buf; /* We just modify the current in_buf to change the type of lock from LOCKING_ANDX_SHARED_LOCK or LOCKING_ANDX_EXCLUSIVE_LOCK to LOCKING_ANDX_CANCEL_LOCK. */ pSMB->LockType = LOCKING_ANDX_CANCEL_LOCK|LOCKING_ANDX_LARGE_FILES; pSMB->Timeout = 0; pSMB->hdr.Mid = get_next_mid(ses->server); return SendReceive(xid, ses, in_buf, out_buf, &bytes_returned, 0); }
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; 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 */ 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, be32_to_cpu(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) { send_nt_cancel(ses->server, in_buf, midQ); spin_lock(&GlobalMid_Lock); if (midQ->midState == MID_REQUEST_SUBMITTED) { /* no longer considered to be "in-flight" */ midQ->callback = DeleteMidQEntry; spin_unlock(&GlobalMid_Lock); atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); return rc; } spin_unlock(&GlobalMid_Lock); } 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 || !out_buf || midQ->midState != MID_RESPONSE_RECEIVED) { rc = -EIO; cERROR(1, "Bad MID state?"); goto out; } *pbytes_returned = be32_to_cpu(midQ->resp_buf->smb_buf_length); memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4); rc = cifs_check_receive(midQ, ses->server, 0); out: delete_mid(midQ); atomic_dec(&ses->server->inFlight); wake_up(&ses->server->request_q); 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; }