diff options
author | Stefan Metzmacher <metze@samba.org> | 2011-11-09 11:47:33 +0100 |
---|---|---|
committer | Stefan Metzmacher <metze@samba.org> | 2011-11-15 17:14:13 +0100 |
commit | 693cb77b2fdb96205ab83bb2c22b67fe91de61b0 (patch) | |
tree | c450abd9297afe0abfa63d05224fee88f97157dd /source3/smbd | |
parent | 10b285ccc29b106f164a6c18116e237634867717 (diff) | |
download | samba-693cb77b2fdb96205ab83bb2c22b67fe91de61b0.tar.gz samba-693cb77b2fdb96205ab83bb2c22b67fe91de61b0.tar.bz2 samba-693cb77b2fdb96205ab83bb2c22b67fe91de61b0.zip |
s3:smb2_server: always send STATUS_PENDING responses, but delayed by 0.5 milliseconds
In future we'll pass the delay from the caller.
metze
Diffstat (limited to 'source3/smbd')
-rw-r--r-- | source3/smbd/globals.h | 2 | ||||
-rw-r--r-- | source3/smbd/smb2_create.c | 34 | ||||
-rw-r--r-- | source3/smbd/smb2_read.c | 8 | ||||
-rw-r--r-- | source3/smbd/smb2_server.c | 225 | ||||
-rw-r--r-- | source3/smbd/smb2_write.c | 8 |
5 files changed, 125 insertions, 152 deletions
diff --git a/source3/smbd/globals.h b/source3/smbd/globals.h index 2e94b55a04..77eed19289 100644 --- a/source3/smbd/globals.h +++ b/source3/smbd/globals.h @@ -347,7 +347,7 @@ struct smbd_smb2_request { int current_idx; bool do_signing; - bool async; + struct tevent_timer *async_te; bool cancelled; bool compound_related; diff --git a/source3/smbd/smb2_create.c b/source3/smbd/smb2_create.c index 29696dcdb4..eee252ba36 100644 --- a/source3/smbd/smb2_create.c +++ b/source3/smbd/smb2_create.c @@ -437,7 +437,7 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx, } - if (!smb2req->async) { + if (smb2req->subreq == NULL) { /* New create call. */ req = tevent_req_create(mem_ctx, &state, struct smbd_smb2_create_state); @@ -445,7 +445,6 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx, return NULL; } state->smb2req = smb2req; - smb2req->subreq = req; /* So we can find this when going async. */ smb1req = smbd_smb2_fake_smb_request(smb2req); if (tevent_req_nomem(smb1req, req)) { @@ -892,7 +891,7 @@ bool get_deferred_open_message_state_smb2(struct smbd_smb2_request *smb2req, if (!smb2req) { return false; } - if (!smb2req->async) { + if (smb2req->subreq == NULL) { return false; } req = smb2req->subreq; @@ -1201,35 +1200,6 @@ bool push_deferred_open_message_smb2(struct smbd_smb2_request *smb2req, return false; } -#if 1 - /* Boo - turns out this isn't what W2K8R2 - does. It actually sends the STATUS_PENDING - message followed by the STATUS_SHARING_VIOLATION - message. Surely this means that all open - calls (even on directories) will potentially - fail in a chain.... ? And I've seen directory - opens as the start of a chain. JRA. - - Update: 19th May 2010. Talking with Microsoft - engineers at the plugfest this is a bug in - Windows. Re-enable this code. - */ - /* - * More subtlety. To match W2K8R2 don't - * send a "gone async" message if it's simply - * a STATUS_SHARING_VIOLATION (short) wait, not - * an oplock break wait. We do this by prematurely - * setting smb2req->async flag. - */ - if (timeout.tv_sec < 2) { - DEBUG(10,("push_deferred_open_message_smb2: " - "short timer wait (usec = %u). " - "Don't send async message.\n", - (unsigned int)timeout.tv_usec )); - smb2req->async = true; - } -#endif - /* Re-schedule us to retry on timer expiry. */ end_time = timeval_sum(&request_time, &timeout); diff --git a/source3/smbd/smb2_read.c b/source3/smbd/smb2_read.c index 405e82d0f7..75494373c6 100644 --- a/source3/smbd/smb2_read.c +++ b/source3/smbd/smb2_read.c @@ -466,14 +466,8 @@ static struct tevent_req *smbd_smb2_read_send(TALLOC_CTX *mem_ctx, if (NT_STATUS_IS_OK(status)) { /* - * Doing an async read. Don't - * send a "gone async" message - * as we expect this to be less - * than the client timeout period. - * JRA. FIXME for offline files.. - * FIXME. Add cancel code.. + * Doing an async read. */ - smb2req->async = true; return req; } diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c index 33e95addbe..01269c927d 100644 --- a/source3/smbd/smb2_server.c +++ b/source3/smbd/smb2_server.c @@ -711,15 +711,6 @@ static struct smbd_smb2_request *dup_smb2_req(const struct smbd_smb2_request *re newreq->session = req->session; newreq->do_signing = req->do_signing; newreq->current_idx = req->current_idx; - newreq->async = false; - newreq->cancelled = false; - /* Note we are leaving: - ->tcon - ->smb1req - ->compat_chain_fsp - uninitialized as NULL here as - they're not used in the interim - response code. JRA. */ outvec = talloc_zero_array(newreq, struct iovec, count); if (!outvec) { @@ -844,19 +835,20 @@ static void smbd_smb2_request_pending_writev_done(struct tevent_req *subreq) TALLOC_FREE(state); } +static void smbd_smb2_request_pending_timer(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval current_time, + void *private_data); + NTSTATUS smbd_smb2_request_pending_queue(struct smbd_smb2_request *req, struct tevent_req *subreq) { NTSTATUS status; - struct smbd_smb2_request_pending_state *state = NULL; int i = req->current_idx; - uint8_t *reqhdr = NULL; - uint8_t *hdr = NULL; - uint8_t *body = NULL; - uint32_t flags = 0; - uint64_t message_id = 0; - uint64_t async_id = 0; - struct iovec *outvec = NULL; + uint32_t defer_time = 500; + struct timeval defer_endtime; + uint8_t *outhdr = NULL; + uint32_t flags; if (!tevent_req_is_in_progress(subreq)) { return NT_STATUS_OK; @@ -865,7 +857,14 @@ NTSTATUS smbd_smb2_request_pending_queue(struct smbd_smb2_request *req, req->subreq = subreq; subreq = NULL; - if (req->async) { + if (req->async_te) { + /* We're already async. */ + return NT_STATUS_OK; + } + + outhdr = (uint8_t *)req->out.vector[i].iov_base; + flags = IVAL(outhdr, SMB2_HDR_FLAGS); + if (flags & SMB2_HDR_FLAG_ASYNC) { /* We're already async. */ return NT_STATUS_OK; } @@ -888,6 +887,8 @@ NTSTATUS smbd_smb2_request_pending_queue(struct smbd_smb2_request *req, } if (req->out.vector_count > 4) { + struct iovec *outvec = NULL; + /* This is a compound reply. We * must do an interim response * followed by the async response @@ -911,18 +912,94 @@ NTSTATUS smbd_smb2_request_pending_queue(struct smbd_smb2_request *req, */ req->compound_related = false; req->sconn->smb2.compound_related_in_progress = false; + + /* Re-arrange the in.vectors. */ + req->in.vector[1] = req->in.vector[i]; + req->in.vector[2] = req->in.vector[i+1]; + req->in.vector[3] = req->in.vector[i+2]; + req->in.vector_count = 4; + + /* Reset the new in size. */ + smb2_setup_nbt_length(req->in.vector, 4); + + /* Now recreate the out.vectors. */ + outvec = talloc_zero_array(req, struct iovec, 4); + if (!outvec) { + return NT_STATUS_NO_MEMORY; + } + + /* 0 is always boilerplate and must + * be of size 4 for the length field. */ + + outvec[0].iov_base = req->out.nbt_hdr; + outvec[0].iov_len = 4; + SIVAL(req->out.nbt_hdr, 0, 0); + + if (!dup_smb2_vec3(outvec, &outvec[1], &req->out.vector[i])) { + return NT_STATUS_NO_MEMORY; + } + + TALLOC_FREE(req->out.vector); + + req->out.vector = outvec; + + req->current_idx = 1; + req->out.vector_count = 4; + + outhdr = (uint8_t *)req->out.vector[1].iov_base; + flags = (IVAL(outhdr, SMB2_HDR_FLAGS) & ~SMB2_HDR_FLAG_CHAINED); + SIVAL(outhdr, SMB2_HDR_FLAGS, flags); } - /* Don't return an intermediate packet on a pipe read/write. */ - if (req->tcon && req->tcon->compat_conn && IS_IPC(req->tcon->compat_conn)) { - goto ipc_out; + defer_endtime = timeval_current_ofs_usec(defer_time); + req->async_te = tevent_add_timer(req->sconn->smb2.event_ctx, + req, defer_endtime, + smbd_smb2_request_pending_timer, + req); + if (req->async_te == NULL) { + return NT_STATUS_NO_MEMORY; } - reqhdr = (uint8_t *)req->out.vector[i].iov_base; - flags = (IVAL(reqhdr, SMB2_HDR_FLAGS) & ~SMB2_HDR_FLAG_CHAINED); - message_id = BVAL(reqhdr, SMB2_HDR_MESSAGE_ID); + return NT_STATUS_OK; +} + +static void smbd_smb2_request_pending_timer(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval current_time, + void *private_data) +{ + struct smbd_smb2_request *req = + talloc_get_type_abort(private_data, + struct smbd_smb2_request); + struct smbd_smb2_request_pending_state *state = NULL; + int i = req->current_idx; + uint8_t *outhdr = NULL; + const uint8_t *inhdr = NULL; + uint8_t *hdr = NULL; + uint8_t *body = NULL; + uint32_t flags = 0; + uint64_t message_id = 0; + uint64_t async_id = 0; + struct tevent_req *subreq = NULL; + + TALLOC_FREE(req->async_te); + + /* Ensure our final reply matches the interim one. */ + inhdr = (const uint8_t *)req->in.vector[1].iov_base; + outhdr = (uint8_t *)req->out.vector[1].iov_base; + flags = IVAL(outhdr, SMB2_HDR_FLAGS); + message_id = BVAL(outhdr, SMB2_HDR_MESSAGE_ID); + async_id = message_id; /* keep it simple for now... */ + SIVAL(outhdr, SMB2_HDR_FLAGS, flags | SMB2_HDR_FLAG_ASYNC); + SBVAL(outhdr, SMB2_HDR_ASYNC_ID, async_id); + + DEBUG(10,("smbd_smb2_request_pending_queue: opcode[%s] mid %llu " + "going async\n", + smb2_opcode_name((uint16_t)IVAL(inhdr, SMB2_HDR_OPCODE)), + (unsigned long long)async_id )); + /* * What we send is identical to a smbd_smb2_request_error * packet with an error status of STATUS_PENDING. Make use @@ -931,7 +1008,9 @@ NTSTATUS smbd_smb2_request_pending_queue(struct smbd_smb2_request *req, state = talloc_zero(req->sconn, struct smbd_smb2_request_pending_state); if (state == NULL) { - return NT_STATUS_NO_MEMORY; + smbd_server_connection_terminate(req->sconn, + nt_errstr(NT_STATUS_NO_MEMORY)); + return; } state->sconn = req->sconn; @@ -953,15 +1032,16 @@ NTSTATUS smbd_smb2_request_pending_queue(struct smbd_smb2_request *req, SSVAL(hdr, SMB2_HDR_LENGTH, SMB2_HDR_BODY); SSVAL(hdr, SMB2_HDR_EPOCH, 0); SIVAL(hdr, SMB2_HDR_STATUS, NT_STATUS_V(STATUS_PENDING)); - SSVAL(hdr, SMB2_HDR_OPCODE, SVAL(reqhdr, SMB2_HDR_OPCODE)); + SSVAL(hdr, SMB2_HDR_OPCODE, SVAL(outhdr, SMB2_HDR_OPCODE)); SIVAL(hdr, SMB2_HDR_FLAGS, flags); SIVAL(hdr, SMB2_HDR_NEXT_COMMAND, 0); SBVAL(hdr, SMB2_HDR_MESSAGE_ID, message_id); SBVAL(hdr, SMB2_HDR_PID, async_id); SBVAL(hdr, SMB2_HDR_SESSION_ID, - BVAL(reqhdr, SMB2_HDR_SESSION_ID)); - memset(hdr+SMB2_HDR_SIGNATURE, 0, 16); + BVAL(outhdr, SMB2_HDR_SESSION_ID)); + memcpy(hdr+SMB2_HDR_SIGNATURE, + outhdr+SMB2_HDR_SIGNATURE, 16); SSVAL(body, 0x00, 0x08 + 1); @@ -981,97 +1061,31 @@ NTSTATUS smbd_smb2_request_pending_queue(struct smbd_smb2_request *req, SIVAL(hdr, SMB2_HDR_FLAGS, flags | SMB2_HDR_FLAG_ASYNC); if (req->do_signing) { + NTSTATUS status; + status = smb2_signing_sign_pdu(req->session->session_key, &state->vector[1], 2); if (!NT_STATUS_IS_OK(status)) { - return status; + smbd_server_connection_terminate(req->sconn, + nt_errstr(status)); + return; } } subreq = tstream_writev_queue_send(state, - req->sconn->smb2.event_ctx, - req->sconn->smb2.stream, - req->sconn->smb2.send_queue, + state->sconn->smb2.event_ctx, + state->sconn->smb2.stream, + state->sconn->smb2.send_queue, state->vector, 3); - if (subreq == NULL) { - return NT_STATUS_NO_MEMORY; + smbd_server_connection_terminate(state->sconn, + nt_errstr(NT_STATUS_NO_MEMORY)); + return; } - tevent_req_set_callback(subreq, smbd_smb2_request_pending_writev_done, state); - - /* Note we're going async with this request. */ - req->async = true; - - ipc_out: - - /* - * Now manipulate req so that the outstanding async request - * is the only one left in the struct smbd_smb2_request. - */ - - if (req->current_idx == 1) { - /* There was only one. */ - goto out; - } - - /* Re-arrange the in.vectors. */ - req->in.vector[1] = req->in.vector[i]; - req->in.vector[2] = req->in.vector[i+1]; - req->in.vector[3] = req->in.vector[i+2]; - req->in.vector_count = 4; - /* Reset the new in size. */ - smb2_setup_nbt_length(req->in.vector, 4); - - /* Now recreate the out.vectors. */ - outvec = talloc_zero_array(req, struct iovec, 4); - if (!outvec) { - return NT_STATUS_NO_MEMORY; - } - - /* 0 is always boilerplate and must - * be of size 4 for the length field. */ - - outvec[0].iov_base = req->out.nbt_hdr; - outvec[0].iov_len = 4; - SIVAL(req->out.nbt_hdr, 0, 0); - - if (!dup_smb2_vec3(outvec, &outvec[1], &req->out.vector[i])) { - return NT_STATUS_NO_MEMORY; - } - - TALLOC_FREE(req->out.vector); - - req->out.vector = outvec; - - req->current_idx = 1; - req->out.vector_count = 4; - - out: - - smb2_setup_nbt_length(req->out.vector, - req->out.vector_count); - - if (req->async) { - /* Ensure our final reply matches the interim one. */ - reqhdr = (uint8_t *)req->out.vector[1].iov_base; - SIVAL(reqhdr, SMB2_HDR_FLAGS, flags | SMB2_HDR_FLAG_ASYNC); - SBVAL(reqhdr, SMB2_HDR_PID, async_id); - - { - const uint8_t *inhdr = - (const uint8_t *)req->in.vector[1].iov_base; - DEBUG(10,("smbd_smb2_request_pending_queue: opcode[%s] mid %llu " - "going async\n", - smb2_opcode_name((uint16_t)IVAL(inhdr, SMB2_HDR_OPCODE)), - (unsigned long long)async_id )); - } - } - - return NT_STATUS_OK; } static NTSTATUS smbd_smb2_request_process_cancel(struct smbd_smb2_request *req) @@ -1807,6 +1821,7 @@ static NTSTATUS smbd_smb2_request_reply(struct smbd_smb2_request *req) int i = req->current_idx; req->subreq = NULL; + TALLOC_FREE(req->async_te); req->current_idx += 3; diff --git a/source3/smbd/smb2_write.c b/source3/smbd/smb2_write.c index edf6176821..0ddb0b1ef0 100644 --- a/source3/smbd/smb2_write.c +++ b/source3/smbd/smb2_write.c @@ -302,14 +302,8 @@ static struct tevent_req *smbd_smb2_write_send(TALLOC_CTX *mem_ctx, if (NT_STATUS_IS_OK(status)) { /* - * Doing an async write. Don't - * send a "gone async" message - * as we expect this to be less - * than the client timeout period. - * JRA. FIXME for offline files.. - * FIXME - add cancel code.. + * Doing an async write. */ - smb2req->async = true; return req; } |