s3:smb2_sesssetup: cancel and wait for pending requests on logoff
authorStefan Metzmacher <metze@samba.org>
Mon, 10 Mar 2014 08:53:18 +0000 (09:53 +0100)
committerJeremy Allison <jra@samba.org>
Wed, 12 Mar 2014 16:27:38 +0000 (09:27 -0700)
Bug: https://bugzilla.samba.org/show_bug.cgi?id=10344

Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
source3/smbd/smb2_sesssetup.c

index 786bfe83b5c44aad7d9d95120134f6692c8a2aba..391b1003fa5029e5ac399928fe2791cf7c54dcbd 100644 (file)
@@ -862,15 +862,19 @@ static void smbd_smb2_request_logoff_done(struct tevent_req *subreq)
 
 struct smbd_smb2_logout_state {
        struct smbd_smb2_request *smb2req;
+       struct tevent_queue *wait_queue;
 };
 
+static void smbd_smb2_logoff_wait_done(struct tevent_req *subreq);
+
 static struct tevent_req *smbd_smb2_logoff_send(TALLOC_CTX *mem_ctx,
                                        struct tevent_context *ev,
                                        struct smbd_smb2_request *smb2req)
 {
        struct tevent_req *req;
        struct smbd_smb2_logout_state *state;
-       NTSTATUS status;
+       struct tevent_req *subreq;
+       struct smbd_smb2_request *preq;
 
        req = tevent_req_create(mem_ctx, &state,
                        struct smbd_smb2_logout_state);
@@ -879,12 +883,83 @@ static struct tevent_req *smbd_smb2_logoff_send(TALLOC_CTX *mem_ctx,
        }
        state->smb2req = smb2req;
 
+       state->wait_queue = tevent_queue_create(state, "logoff_wait_queue");
+       if (tevent_req_nomem(state->wait_queue, req)) {
+               return tevent_req_post(req, ev);
+       }
+
        /*
-        * TODO: cancel all outstanding requests on the session
+        * Make sure that no new request will be able to use this session.
         */
+       smb2req->session->status = NT_STATUS_USER_SESSION_DELETED;
+
+       for (preq = smb2req->sconn->smb2.requests; preq != NULL; preq = preq->next) {
+               if (preq == smb2req) {
+                       /* Can't cancel current request. */
+                       continue;
+               }
+               if (preq->session != smb2req->session) {
+                       /* Request on different session. */
+                       continue;
+               }
+
+               /*
+                * Never cancel anything in a compound
+                * request. Way too hard to deal with
+                * the result.
+                */
+               if (!preq->compound_related && preq->subreq != NULL) {
+                       tevent_req_cancel(preq->subreq);
+               }
+
+               /*
+                * Now wait until the request is finished.
+                *
+                * We don't set a callback, as we just want to block the
+                * wait queue and the talloc_free() of the request will
+                * remove the item from the wait queue.
+                */
+               subreq = tevent_queue_wait_send(preq, ev, state->wait_queue);
+               if (tevent_req_nomem(subreq, req)) {
+                       return tevent_req_post(req, ev);
+               }
+       }
+
+       /*
+        * Now we add our own waiter to the end of the queue,
+        * this way we get notified when all pending requests are finished
+        * and send to the socket.
+        */
+       subreq = tevent_queue_wait_send(state, ev, state->wait_queue);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, smbd_smb2_logoff_wait_done, req);
+
+       return req;
+}
+
+static void smbd_smb2_logoff_wait_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct smbd_smb2_logout_state *state = tevent_req_data(
+               req, struct smbd_smb2_logout_state);
+       NTSTATUS status;
+
+       tevent_queue_wait_recv(subreq);
+       TALLOC_FREE(subreq);
+
+       /*
+        * As we've been awoken, we may have changed
+        * uid in the meantime. Ensure we're still
+        * root (SMB2_OP_LOGOFF has .as_root = true).
+        */
+       change_to_root_user();
+
        status = smbXsrv_session_logoff(state->smb2req->session);
        if (tevent_req_nterror(req, status)) {
-               return tevent_req_post(req, ev);
+               return;
        }
 
        /*
@@ -894,7 +969,6 @@ static struct tevent_req *smbd_smb2_logoff_send(TALLOC_CTX *mem_ctx,
        talloc_steal(state->smb2req, state->smb2req->session);
 
        tevent_req_done(req);
-       return tevent_req_post(req, ev);
 }
 
 static NTSTATUS smbd_smb2_logoff_recv(struct tevent_req *req)