s3:smb2_server: always send STATUS_PENDING responses, but delayed by 0.5 milliseconds
authorStefan Metzmacher <metze@samba.org>
Wed, 9 Nov 2011 10:47:33 +0000 (11:47 +0100)
committerStefan Metzmacher <metze@samba.org>
Tue, 15 Nov 2011 16:14:13 +0000 (17:14 +0100)
In future we'll pass the delay from the caller.

metze

source3/smbd/globals.h
source3/smbd/smb2_create.c
source3/smbd/smb2_read.c
source3/smbd/smb2_server.c
source3/smbd/smb2_write.c

index 2e94b55a043fda0f79a55f9b3c0c7ee3784bdad8..77eed19289b10eeafca35002ee22a178bb52e616 100644 (file)
@@ -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;
 
index 29696dcdb4eec81dc3dd7ce31f5738c1f7d42736..eee252ba36eee15f22e7987f71ef26845d96e6ea 100644 (file)
@@ -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);
 
index 405e82d0f7f78812649982d00e84b7777abb6b02..75494373c64c9c6298ce777484ac9d2003ba4f3e 100644 (file)
@@ -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;
        }
 
index 33e95addbede20fb62feaf0b7ea5912e81e76312..01269c927ddb1cdacbba5562ab273ac2476c61b0 100644 (file)
@@ -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;
 
index edf61768212edb3324b4d88aef68c0bc385c9e16..0ddb0b1ef0d9f52f0a293993d2ed25bbcfc4e221 100644 (file)
@@ -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;
        }