libsmb: Handle long-running smb2cli_notify
authorVolker Lendecke <vl@samba.org>
Mon, 30 Oct 2017 13:34:12 +0000 (14:34 +0100)
committerJeremy Allison <jra@samba.org>
Wed, 4 Apr 2018 23:10:10 +0000 (01:10 +0200)
This likely runs into a timeout. Properly cancel the smb2 request,
allowing the higher-level caller to re-issue this request on an existing
handle.

I did not see a proper way to achieve this with tevent_req_set_endtime or
something like that.

Signed-off-by: Volker Lendecke <vl@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
libcli/smb/smb2cli_notify.c

index 0a23cf9ad033f4a7e0bcc4aef89b67dfc6e3ee59..34329ba16cccb23e937f2f80c7116a7c71ff8a0b 100644 (file)
@@ -30,9 +30,12 @@ struct smb2cli_notify_state {
        struct iovec *recv_iov;
        uint8_t *data;
        uint32_t data_length;
+
+       struct tevent_req *subreq;
 };
 
 static void smb2cli_notify_done(struct tevent_req *subreq);
+static void smb2cli_notify_timedout(struct tevent_req *subreq);
 
 struct tevent_req *smb2cli_notify_send(TALLOC_CTX *mem_ctx,
                                       struct tevent_context *ev,
@@ -64,21 +67,50 @@ struct tevent_req *smb2cli_notify_send(TALLOC_CTX *mem_ctx,
        SIVAL(fixed, 24, completion_filter);
        SIVAL(fixed, 28, 0);    /* reserved */
 
-       subreq = smb2cli_req_send(state, ev, conn, SMB2_OP_NOTIFY,
-                                 0, 0, /* flags */
-                                 timeout_msec,
-                                 tcon,
-                                 session,
-                                 state->fixed, sizeof(state->fixed),
-                                 NULL, 0, /* dyn* */
-                                 0); /* max_dyn_len */
+       state->subreq = smb2cli_req_send(state, ev, conn, SMB2_OP_NOTIFY,
+                                        0, 0, /* flags */
+                                        0,     /* timeout_msec */
+                                        tcon,
+                                        session,
+                                        state->fixed, sizeof(state->fixed),
+                                        NULL, 0, /* dyn* */
+                                        0); /* max_dyn_len */
+       if (tevent_req_nomem(state->subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(state->subreq, smb2cli_notify_done, req);
+
+       subreq = tevent_wakeup_send(state, ev,
+                                   timeval_current_ofs_msec(timeout_msec));
        if (tevent_req_nomem(subreq, req)) {
                return tevent_req_post(req, ev);
        }
-       tevent_req_set_callback(subreq, smb2cli_notify_done, req);
+       tevent_req_set_callback(subreq, smb2cli_notify_timedout, req);
+
        return req;
 }
 
+static void smb2cli_notify_timedout(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct smb2cli_notify_state *state = tevent_req_data(
+               req, struct smb2cli_notify_state);
+       bool ok;
+
+       ok = tevent_wakeup_recv(subreq);
+       if (!ok) {
+               tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+               return;
+       }
+
+       ok = tevent_req_cancel(state->subreq);
+       if (!ok) {
+               tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+               return;
+       }
+}
+
 static void smb2cli_notify_done(struct tevent_req *subreq)
 {
        struct tevent_req *req = tevent_req_callback_data(
@@ -98,6 +130,10 @@ static void smb2cli_notify_done(struct tevent_req *subreq)
        status = smb2cli_req_recv(subreq, state, &iov,
                                  expected, ARRAY_SIZE(expected));
        TALLOC_FREE(subreq);
+
+       if (NT_STATUS_EQUAL(status, NT_STATUS_CANCELLED)) {
+               status = NT_STATUS_IO_TIMEOUT;
+       }
        if (tevent_req_nterror(req, status)) {
                return;
        }