From 13886dacabd2622f4723e1981412c6fa61bbd2ee Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 8 May 2013 11:50:32 -0700 Subject: [PATCH 1/4] Only do the 1 second delay for sharing violations for SMB1, not SMB2. Match Windows behavior. Signed-off-by: Jeremy Allison --- source3/smbd/open.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source3/smbd/open.c b/source3/smbd/open.c index d10b697..447de80 100644 --- a/source3/smbd/open.c +++ b/source3/smbd/open.c @@ -2002,10 +2002,11 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, /* * If we're returning a share violation, ensure we - * cope with the braindead 1 second delay. + * cope with the braindead 1 second delay (SMB1 only). */ if (!(oplock_request & INTERNAL_OPEN_ONLY) && + !conn->sconn->using_smb2 && lp_defer_sharing_violations()) { struct timeval timeout; struct deferred_open_record state; -- 1.8.2.1 From 6bdf6ec5ce116d15995014f5162a17ac930772dc Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 8 May 2013 11:51:38 -0700 Subject: [PATCH 2/4] Ensure we don't try and cancel anything that is in a compound-related request. Too hard to deal with splitting off the replies. Signed-off-by: Jeremy Allison --- source3/smbd/smb2_server.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c index cc884c3..396d476 100644 --- a/source3/smbd/smb2_server.c +++ b/source3/smbd/smb2_server.c @@ -1178,6 +1178,14 @@ static NTSTATUS smbd_smb2_request_process_cancel(struct smbd_smb2_request *req) uint64_t message_id; uint64_t async_id; + if (cur->compound_related) { + /* + * Never cancel anything in a compound request. + * Way too hard to deal with the result. + */ + continue; + } + i = cur->current_idx; outhdr = (const uint8_t *)cur->out.vector[i].iov_base; -- 1.8.2.1 From be669df1dbb62d605fedd71788afa0b02f345d75 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 8 May 2013 15:08:50 -0700 Subject: [PATCH 3/4] The core of the fix to allow opens to go async inside a compound request. This is only allowed for opens that cause an oplock break, otherwise it is not allowed. See [MS-SMB2].pdf note <194> on Section 3.3.5.2.7. Signed-off-by: Jeremy Allison --- source3/smbd/smb2_server.c | 128 ++++++++++++++++++++------------------------- 1 file changed, 57 insertions(+), 71 deletions(-) diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c index 396d476..f1f9966 100644 --- a/source3/smbd/smb2_server.c +++ b/source3/smbd/smb2_server.c @@ -927,7 +927,6 @@ NTSTATUS smbd_smb2_request_pending_queue(struct smbd_smb2_request *req, uint32_t flags = 0; uint64_t message_id = 0; uint64_t async_id = 0; - struct iovec *outvec = NULL; if (!tevent_req_is_in_progress(subreq)) { return NT_STATUS_OK; @@ -944,16 +943,27 @@ NTSTATUS smbd_smb2_request_pending_queue(struct smbd_smb2_request *req, if (req->in.vector_count > i + 3) { /* * We're trying to go async in a compound - * request chain. This is not allowed. - * Cancel the outstanding request. + * request chain. + * This is only allowed for opens that + * cause an oplock break, otherwise it + * is not allowed. See [MS-SMB2].pdf + * note <194> on Section 3.3.5.2.7. */ - bool ok = tevent_req_cancel(req->subreq); - if (ok) { - return NT_STATUS_OK; - } - TALLOC_FREE(req->subreq); - return smbd_smb2_request_error(req, - NT_STATUS_INTERNAL_ERROR); + const uint8_t *inhdr = + (const uint8_t *)req->in.vector[i].iov_base; + + if (SVAL(inhdr, SMB2_HDR_OPCODE) != SMB2_OP_CREATE) { + /* + * Cancel the outstanding request. + */ + bool ok = tevent_req_cancel(req->subreq); + if (ok) { + return NT_STATUS_OK; + } + TALLOC_FREE(req->subreq); + return smbd_smb2_request_error(req, + NT_STATUS_INTERNAL_ERROR); + } } if (DEBUGLEVEL >= 10) { @@ -962,35 +972,55 @@ NTSTATUS smbd_smb2_request_pending_queue(struct smbd_smb2_request *req, print_req_vectors(req); } - if (req->out.vector_count > 4) { - /* This is a compound reply. We - * must do an interim response - * followed by the async response - * to match W2K8R2. + if (i > 1) { + /* + * We're going async in a compound + * chain after the first request has + * already been processed. Send an + * interim response containing the + * set of replies already generated. */ status = smb2_send_async_interim_response(req); if (!NT_STATUS_IS_OK(status)) { return status; } + req->current_idx = 1; + /* - * We're splitting off the last SMB2 - * request in a compound set, and the - * smb2_send_async_interim_response() - * call above just sent all the replies - * for the previous SMB2 requests in - * this compound set. So we're no longer - * in the "compound_related_in_progress" - * state, and this is no longer a compound - * request. + * Re-arrange the in.vectors to remove what + * we just sent. */ - req->compound_related = false; - req->sconn->smb2.compound_related_in_progress = false; + memmove(&req->in.vector[1], + &req->in.vector[i], + sizeof(req->in.vector[0])*(req->in.vector_count - i)); + req->in.vector_count = 1 + (req->in.vector_count - i); + + smb2_setup_nbt_length(req->in.vector, req->in.vector_count); + + /* Re-arrange the out.vectors to match. */ + memmove(&req->out.vector[1], + &req->out.vector[i], + sizeof(req->out.vector[0])*(req->out.vector_count - i)); + req->out.vector_count = 1 + (req->out.vector_count - i); + + if (req->in.vector_count == 4) { + uint8_t *outhdr = (uint8_t *)req->out.vector[i].iov_base; + /* + * We only have one remaining request as + * we've processed everything else. + * This is no longer a compound request. + */ + req->compound_related = false; + req->sconn->smb2.compound_related_in_progress = false; + 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; + goto out; } reqhdr = (uint8_t *)req->out.vector[i].iov_base; @@ -1081,50 +1111,6 @@ NTSTATUS smbd_smb2_request_pending_queue(struct smbd_smb2_request *req, /* 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, -- 1.8.2.1 From e8a381d214471a45ba707b1e0bf388cb722e0824 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 8 May 2013 15:10:32 -0700 Subject: [PATCH 4/4] Remove the compound_related_in_progress state from the smb2 global state. And also remove the restriction that we can't read a new request whilst we're in this state. Signed-off-by: Jeremy Allison --- source3/smbd/globals.h | 1 - source3/smbd/smb2_server.c | 11 ----------- 2 files changed, 12 deletions(-) diff --git a/source3/smbd/globals.h b/source3/smbd/globals.h index 730bb7e..ce5b18d 100644 --- a/source3/smbd/globals.h +++ b/source3/smbd/globals.h @@ -647,7 +647,6 @@ struct smbd_server_connection { uint32_t max_trans; uint32_t max_read; uint32_t max_write; - bool compound_related_in_progress; } smb2; }; diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c index f1f9966..9615907 100644 --- a/source3/smbd/smb2_server.c +++ b/source3/smbd/smb2_server.c @@ -1012,7 +1012,6 @@ NTSTATUS smbd_smb2_request_pending_queue(struct smbd_smb2_request *req, * This is no longer a compound request. */ req->compound_related = false; - req->sconn->smb2.compound_related_in_progress = false; flags = (IVAL(outhdr, SMB2_HDR_FLAGS) & ~SMB2_HDR_FLAG_CHAINED); SIVAL(outhdr, SMB2_HDR_FLAGS, flags); } @@ -1354,7 +1353,6 @@ NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req) if (flags & SMB2_HDR_FLAG_CHAINED) { req->compound_related = true; - req->sconn->smb2.compound_related_in_progress = true; } switch (opcode) { @@ -1824,7 +1822,6 @@ static NTSTATUS smbd_smb2_request_reply(struct smbd_smb2_request *req) if (req->compound_related) { req->compound_related = false; - req->sconn->smb2.compound_related_in_progress = false; } smb2_setup_nbt_length(req->out.vector, req->out.vector_count); @@ -2541,14 +2538,6 @@ static NTSTATUS smbd_smb2_request_next_incoming(struct smbd_server_connection *s size_t cur_send_queue_len; struct tevent_req *subreq; - if (sconn->smb2.compound_related_in_progress) { - /* - * Can't read another until the related - * compound is done. - */ - return NT_STATUS_OK; - } - if (tevent_queue_length(sconn->smb2.recv_queue) > 0) { /* * if there is already a smbd_smb2_request_read -- 1.8.2.1