Activate code to enable chained requests
authorVolker Lendecke <vl@samba.org>
Mon, 25 Aug 2008 13:59:36 +0000 (15:59 +0200)
committerVolker Lendecke <vl@samba.org>
Thu, 28 Aug 2008 16:15:59 +0000 (18:15 +0200)
Add the CHAIN1 torture test
(This used to be commit 82992d74a99b056bbfe90e1b79190e0b7c0bf2bd)

source3/include/async_smb.h
source3/include/client.h
source3/libsmb/async_smb.c
source3/torture/torture.c

index 031ab233dd49276f5dbbd3df0fdd049934363a63..1053de29422a8ced20107805e7654734c0178066 100644 (file)
 
 #include "includes.h"
 
+/**
+ * struct cli_request is the state holder for an async client request we sent
+ * to the server. It can consist of more than one struct async_req that we
+ * have to server if the application did a cli_chain_cork() and
+ * cli_chain_uncork()
+ */
+
 struct cli_request {
        /**
         * "prev" and "next" form the doubly linked list in
@@ -30,9 +37,15 @@ struct cli_request {
        struct cli_request *prev, *next;
 
        /**
-        * "our" struct async_req;
+        * num_async: How many chained requests do we serve?
+        */
+       int num_async;
+
+       /**
+        * async: This is the list of chained requests that were queued up by
+        * cli_request_chain before we sent out this request
         */
-       struct async_req *async;
+       struct async_req **async;
 
        /**
         * The client connection for this request
@@ -92,6 +105,10 @@ struct async_req *cli_request_send(TALLOC_CTX *mem_ctx,
                                   uint8_t wct, const uint16_t *vwv,
                                   uint16_t num_bytes, const uint8_t *bytes);
 
+bool cli_chain_cork(struct cli_state *cli, struct event_context *ev,
+                   size_t size_hint);
+void cli_chain_uncork(struct cli_state *cli);
+
 /*
  * Convenience function to get the SMB part out of an async_req
  */
index 6a6e1a2faa000331d66154ebb91fcc2d8adc18ae..9b564fc48e7fe65ebd077ab10c6d938cb435fdb4 100644 (file)
@@ -208,7 +208,8 @@ struct cli_state {
         * fd_event is around while we have async requests outstanding or are
         * building a chained request.
         *
-        * (fd_event!=NULL) && (outstanding_request!=NULL)
+        * (fd_event!=NULL) &&
+        *  ((outstanding_request!=NULL)||(chain_accumulator!=NULL))
         *
         * should always be true, as well as the reverse: If both cli_request
         * pointers are NULL, no fd_event is around.
@@ -220,6 +221,11 @@ struct cli_state {
         * A linked list of requests that are waiting for a reply
         */
        struct cli_request *outstanding_requests;
+
+       /**
+        * The place to build up the list of chained requests.
+        */
+       struct cli_request *chain_accumulator;
 };
 
 typedef struct file_info {
index e7641474328612e481afadd0e117188fbe1fae4e..4d6c32edfa2572719a4b1cf38f252b2cebfeb026 100644 (file)
@@ -135,8 +135,6 @@ static int cli_request_destructor(struct cli_request *req)
        return 0;
 }
 
-#if 0
-
 /**
  * Is the SMB command able to hold an AND_X successor
  * @param[in] cmd      The SMB command in question
@@ -667,176 +665,6 @@ NTSTATUS cli_pull_reply(struct async_req *req,
        return NT_STATUS_OK;
 }
 
-#endif
-
-/*
- * Create a fresh async smb request
- */
-
-static struct async_req *cli_request_new(TALLOC_CTX *mem_ctx,
-                                        struct event_context *ev,
-                                        struct cli_state *cli,
-                                        uint8_t num_words, size_t num_bytes,
-                                        struct cli_request **preq)
-{
-       struct async_req *result;
-       struct cli_request *cli_req;
-       size_t bufsize = smb_size + num_words * 2 + num_bytes;
-
-       result = async_req_new(mem_ctx, ev);
-       if (result == NULL) {
-               return NULL;
-       }
-
-       cli_req = (struct cli_request *)talloc_size(
-               result, sizeof(*cli_req) + bufsize);
-       if (cli_req == NULL) {
-               TALLOC_FREE(result);
-               return NULL;
-       }
-       talloc_set_name_const(cli_req, "struct cli_request");
-       result->private_data = cli_req;
-       result->print = cli_request_print;
-
-       cli_req->async = result;
-       cli_req->cli = cli;
-       cli_req->outbuf = ((char *)cli_req + sizeof(*cli_req));
-       cli_req->sent = 0;
-       cli_req->mid = cli_new_mid(cli);
-       cli_req->inbuf = NULL;
-       cli_req->enc_state = NULL;
-
-       SCVAL(cli_req->outbuf, smb_wct, num_words);
-       SSVAL(cli_req->outbuf, smb_vwv + num_words * 2, num_bytes);
-
-       DLIST_ADD_END(cli->outstanding_requests, cli_req,
-                     struct cli_request *);
-       talloc_set_destructor(cli_req, cli_request_destructor);
-
-       DEBUG(10, ("cli_request_new: mid=%d\n", cli_req->mid));
-
-       *preq = cli_req;
-       return result;
-}
-
-/*
- * Ship a new smb request to the server
- */
-struct async_req *cli_request_send(TALLOC_CTX *mem_ctx,
-                                  struct event_context *ev,
-                                  struct cli_state *cli,
-                                  uint8_t smb_command,
-                                  uint8_t additional_flags,
-                                  uint8_t wct, const uint16_t *vwv,
-                                  uint16_t num_bytes, const uint8_t *bytes)
-{
-       struct async_req *result;
-       struct cli_request *req;
-
-       if (cli->fd_event == NULL) {
-               SMB_ASSERT(cli->outstanding_requests == NULL);
-               cli->fd_event = event_add_fd(ev, cli, cli->fd,
-                                            EVENT_FD_READ,
-                                            cli_state_handler, cli);
-               if (cli->fd_event == NULL) {
-                       return NULL;
-               }
-       }
-
-       result = cli_request_new(mem_ctx, ev, cli, wct, num_bytes, &req);
-       if (result == NULL) {
-               DEBUG(0, ("cli_request_new failed\n"));
-               TALLOC_FREE(cli->fd_event);
-               return NULL;
-       }
-
-       cli_set_message(req->outbuf, wct, num_bytes, false);
-       SCVAL(req->outbuf, smb_com, smb_command);
-       SSVAL(req->outbuf, smb_tid, cli->cnum);
-       cli_setup_packet_buf(cli, req->outbuf);
-
-       memcpy(req->outbuf + smb_vwv0, vwv, sizeof(uint16_t) * wct);
-       memcpy(smb_buf(req->outbuf), bytes, num_bytes);
-       SSVAL(req->outbuf, smb_mid, req->mid);
-       SCVAL(cli->outbuf, smb_flg,
-             CVAL(cli->outbuf,smb_flg) | additional_flags);
-
-       cli_calculate_sign_mac(cli, req->outbuf);
-
-       if (cli_encryption_on(cli)) {
-               NTSTATUS status;
-               char *enc_buf;
-
-               status = cli_encrypt_message(cli, req->outbuf, &enc_buf);
-               if (!NT_STATUS_IS_OK(status)) {
-                       DEBUG(0, ("Error in encrypting client message. "
-                                 "Error %s\n", nt_errstr(status)));
-                       TALLOC_FREE(req);
-                       return NULL;
-               }
-               req->outbuf = enc_buf;
-               req->enc_state = cli->trans_enc_state;
-       }
-
-       event_fd_set_writeable(cli->fd_event);
-
-       return result;
-}
-
-/**
- * @brief Pull reply data out of a request
- * @param[in] req              The request that we just received a reply for
- * @param[out] pwct            How many words did the server send?
- * @param[out] pvwv            The words themselves
- * @param[out] pnum_bytes      How many bytes did the server send?
- * @param[out] pbytes          The bytes themselves
- * @retval Was the reply formally correct?
- */
-
-NTSTATUS cli_pull_reply(struct async_req *req,
-                       uint8_t *pwct, uint16_t **pvwv,
-                       uint16_t *pnum_bytes, uint8_t **pbytes)
-{
-       struct cli_request *cli_req = cli_request_get(req);
-       uint8_t wct, cmd;
-       uint16_t num_bytes;
-       size_t wct_ofs, bytes_offset;
-       NTSTATUS status;
-
-       status = cli_pull_error(cli_req->inbuf);
-
-       if (NT_STATUS_IS_ERR(status)) {
-               cli_set_error(cli_req->cli, status);
-               return status;
-       }
-
-       cmd = CVAL(cli_req->inbuf, smb_com);
-       wct_ofs = smb_wct;
-
-       wct = CVAL(cli_req->inbuf, wct_ofs);
-
-       bytes_offset = wct_ofs + 1 + wct * sizeof(uint16_t);
-       num_bytes = SVAL(cli_req->inbuf, bytes_offset);
-
-       /*
-        * wct_ofs is a 16-bit value plus 4, wct is a 8-bit value, num_bytes
-        * is a 16-bit value. So bytes_offset being size_t should be far from
-        * wrapping.
-        */
-
-       if ((bytes_offset + 2 > talloc_get_size(cli_req->inbuf))
-           || (bytes_offset > 0xffff)) {
-               return NT_STATUS_INVALID_NETWORK_RESPONSE;
-       }
-
-       *pwct = wct;
-       *pvwv = (uint16_t *)(cli_req->inbuf + wct_ofs + 1);
-       *pnum_bytes = num_bytes;
-       *pbytes = (uint8_t *)cli_req->inbuf + bytes_offset + 2;
-
-       return NT_STATUS_OK;
-}
-
 /**
  * Convenience function to get the SMB part out of an async_req
  * @param[in] req      The request to look at
@@ -862,8 +690,11 @@ static void handle_incoming_pdu(struct cli_state *cli)
        uint16_t mid;
        size_t raw_pdu_len, buf_len, pdu_len, rest_len;
        char *pdu;
+       int i;
        NTSTATUS status;
 
+       int num_async;
+
        /*
         * The encrypted PDU len might differ from the unencrypted one
         */
@@ -976,7 +807,24 @@ static void handle_incoming_pdu(struct cli_state *cli)
 
        req->inbuf = talloc_move(req, &pdu);
 
-       async_req_done(req->async);
+       /*
+        * Freeing the last async_req will free the req (see
+        * cli_async_req_destructor). So make a copy of req->num_async, we
+        * can't reference it in the last round.
+        */
+
+       num_async = req->num_async;
+
+       for (i=0; i<num_async; i++) {
+               /**
+                * A request might have been talloc_free()'ed before we arrive
+                * here. It will have removed itself from req->async via its
+                * destructor cli_async_req_destructor().
+                */
+               if (req->async[i] != NULL) {
+                       async_req_done(req->async[i]);
+               }
+       }
        return;
 
  invalidate_requests:
@@ -985,7 +833,7 @@ static void handle_incoming_pdu(struct cli_state *cli)
                   nt_errstr(status)));
 
        for (req = cli->outstanding_requests; req; req = req->next) {
-               async_req_error(req->async, status);
+               async_req_error(req->async[0], status);
        }
        return;
 }
@@ -1101,8 +949,11 @@ static void cli_state_handler(struct event_context *event_ctx,
 
  sock_error:
        for (req = cli->outstanding_requests; req; req = req->next) {
-               req->async->state = ASYNC_REQ_ERROR;
-               req->async->status = map_nt_error_from_unix(errno);
+               int i;
+               for (i=0; i<req->num_async; i++) {
+                       req->async[i]->state = ASYNC_REQ_ERROR;
+                       req->async[i]->status = map_nt_error_from_unix(errno);
+               }
        }
        TALLOC_FREE(cli->fd_event);
        close(cli->fd);
index 75a5b30e30432901931a12312efeec7444b0657f..d159ffbac35c42701e4772323d85b0af80757c63 100644 (file)
@@ -4897,6 +4897,85 @@ static bool subst_test(const char *str, const char *user, const char *domain,
        return result;
 }
 
+static void chain1_open_completion(struct async_req *req)
+{
+       int fnum;
+       NTSTATUS status;
+
+       status = cli_open_recv(req, &fnum);
+       TALLOC_FREE(req);
+
+       d_printf("cli_open_recv returned %s: %d\n",
+                nt_errstr(status),
+                NT_STATUS_IS_OK(status) ? fnum : -1);
+}
+
+static void chain1_read_completion(struct async_req *req)
+{
+       NTSTATUS status;
+       ssize_t received;
+       uint8_t *rcvbuf;
+
+       status = cli_read_andx_recv(req, &received, &rcvbuf);
+       if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(req);
+               d_printf("cli_read_andx_recv returned %s\n",
+                        nt_errstr(status));
+               return;
+       }
+
+       d_printf("got %d bytes: %.*s\n", (int)received, (int)received,
+                (char *)rcvbuf);
+       TALLOC_FREE(req);
+}
+
+static void chain1_close_completion(struct async_req *req)
+{
+       NTSTATUS status;
+
+       status = cli_close_recv(req);
+       *((bool *)(req->async.priv)) = true;
+
+       TALLOC_FREE(req);
+
+       d_printf("cli_close returned %s\n", nt_errstr(status));
+}
+
+static bool run_chain1(int dummy)
+{
+       struct cli_state *cli1;
+       struct event_context *evt = event_context_init(NULL);
+       struct async_req *reqs[4];
+       bool done = false;
+
+       printf("starting chain1 test\n");
+       if (!torture_open_connection(&cli1, 0)) {
+               return False;
+       }
+
+       cli_sockopt(cli1, sockops);
+
+       cli_chain_cork(cli1, evt, 0);
+       reqs[0] = cli_open_send(talloc_tos(), evt, cli1, "\\test",
+                               O_CREAT|O_RDWR, 0);
+       reqs[0]->async.fn = chain1_open_completion;
+       reqs[1] = cli_read_andx_send(talloc_tos(), evt, cli1, 0, 0, 10);
+       reqs[1]->async.fn = chain1_read_completion;
+       reqs[2] = cli_read_andx_send(talloc_tos(), evt, cli1, 0, 1, 10);
+       reqs[2]->async.fn = chain1_read_completion;
+       reqs[3] = cli_close_send(talloc_tos(), evt, cli1, 0);
+       reqs[3]->async.fn = chain1_close_completion;
+       reqs[3]->async.priv = (void *)&done;
+       cli_chain_uncork(cli1);
+
+       while (!done) {
+               event_loop_once(evt);
+       }
+
+       torture_close_connection(cli1);
+       return True;
+}
+
 static bool run_local_substitute(int dummy)
 {
        bool ok = true;
@@ -5394,6 +5473,7 @@ static struct {
        {"FDSESS", run_fdsesstest, 0},
        { "EATEST", run_eatest, 0},
        { "SESSSETUP_BENCH", run_sesssetup_bench, 0},
+       { "CHAIN1", run_chain1, 0},
        { "LOCAL-SUBSTITUTE", run_local_substitute, 0},
        { "LOCAL-GENCACHE", run_local_gencache, 0},
        { "LOCAL-RBTREE", run_local_rbtree, 0},