s3:smb2cli_base: make use of tevent_req_defer_callback()
authorStefan Metzmacher <metze@samba.org>
Sat, 9 Jul 2011 08:12:11 +0000 (10:12 +0200)
committerStefan Metzmacher <metze@samba.org>
Sat, 9 Jul 2011 10:40:28 +0000 (12:40 +0200)
In order to notify requests of transport layer errors,
we need to defer the triggering of the callbacks,
otherwise we may crash, if one of the callbacks
destroys the cli_state.

metze

source3/libsmb/smb2cli_base.c

index 1b5a8a6798977e5477c4fe1978fd3148ee8070a2..ac7d423b572ac9db1af7eee2c42b9ef9fa495001 100644 (file)
@@ -139,6 +139,36 @@ static bool smb2cli_req_set_pending(struct tevent_req *req)
        return true;
 }
 
+static void smb2cli_notify_pending(struct cli_state *cli, NTSTATUS status)
+{
+       if (cli->fd != -1) {
+               close(cli->fd);
+               cli->fd = -1;
+       }
+
+       /*
+        * Cancel all pending requests. We don't do a for-loop walking
+        * cli->pending because that array changes in
+        * cli_smb_req_destructor().
+        */
+       while (talloc_array_length(cli->pending) > 0) {
+               struct tevent_req *req;
+               struct smb2cli_req_state *state;
+
+               req = cli->pending[0];
+               state = tevent_req_data(req, struct smb2cli_req_state);
+
+               smb2cli_req_unset_pending(req);
+
+               /*
+                * we need to defer the callback, because we may notify more
+                * then one caller.
+                */
+               tevent_req_defer_callback(req, state->ev);
+               tevent_req_nterror(req, status);
+       }
+}
+
 struct tevent_req *smb2cli_req_create(TALLOC_CTX *mem_ctx,
                                      struct tevent_context *ev,
                                      struct cli_state *cli,
@@ -312,11 +342,8 @@ static void smb2cli_writev_done(struct tevent_req *subreq)
        nwritten = writev_recv(subreq, &err);
        TALLOC_FREE(subreq);
        if (nwritten == -1) {
-               if (state->cli->fd != -1) {
-                       close(state->cli->fd);
-                       state->cli->fd = -1;
-               }
-               tevent_req_nterror(req, map_nt_error_from_unix(err));
+               /* here, we need to notify all pending requests */
+               smb2cli_notify_pending(state->cli, map_nt_error_from_unix(err));
                return;
        }
 }
@@ -457,18 +484,28 @@ static void smb2cli_inbuf_received(struct tevent_req *subreq)
        received = read_smb_recv(subreq, frame, &inbuf, &err);
        TALLOC_FREE(subreq);
        if (received == -1) {
-               if (cli->fd != -1) {
-                       close(cli->fd);
-                       cli->fd = -1;
-               }
-               status = map_nt_error_from_unix(err);
-               goto fail;
+               /*
+                * We need to close the connection and notify
+                * all pending requests.
+                */
+               smb2cli_notify_pending(cli, map_nt_error_from_unix(err));
+               TALLOC_FREE(frame);
+               return;
        }
 
        status = smb2cli_inbuf_parse_compound(inbuf, frame,
                                              &iov, &num_iov);
        if (!NT_STATUS_IS_OK(status)) {
-               goto fail;
+               /*
+                * if we cannot parse the incoming pdu,
+                * the connection becomes unusable.
+                *
+                * We need to close the connection and notify
+                * all pending requests.
+                */
+               smb2cli_notify_pending(cli, status);
+               TALLOC_FREE(frame);
+               return;
        }
 
        for (i=1; i<num_iov; i+=3) {
@@ -481,13 +518,26 @@ static void smb2cli_inbuf_received(struct tevent_req *subreq)
                        cli, BVAL(inhdr, SMB2_HDR_MESSAGE_ID));
                if (req == NULL) {
                        /*
-                        * oplock breaks ??
+                        * TODO: handle oplock breaks and async responses
+                        */
+
+                       /*
+                        * We need to close the connection and notify
+                        * all pending requests.
                         */
-                       goto fail;
+                       smb2cli_notify_pending(cli, status);
+                       TALLOC_FREE(frame);
+                       return;
                }
                smb2cli_req_unset_pending(req);
                state = tevent_req_data(req, struct smb2cli_req_state);
 
+               /*
+                * There might be more than one response
+                * we need to defer the notifications
+                */
+               tevent_req_defer_callback(req, state->ev);
+
                /*
                 * Note: here we use talloc_reference() in a way
                 *       that does not expose it to the caller.
@@ -505,20 +555,6 @@ static void smb2cli_inbuf_received(struct tevent_req *subreq)
                tevent_req_done(req);
        }
 
-       TALLOC_FREE(frame);
-       return;
- fail:
-       /*
-        * Cancel all pending requests. We don't do a for-loop walking
-        * cli->pending because that array changes in
-        * cli_smb_req_destructor().
-        */
-       while (talloc_array_length(cli->pending) > 0) {
-               req = cli->pending[0];
-               smb2cli_req_unset_pending(req);
-               tevent_req_nterror(req, status);
-       }
-
        TALLOC_FREE(frame);
 }