s3: async cli_list
authorVolker Lendecke <vl@samba.org>
Tue, 10 Aug 2010 05:44:15 +0000 (07:44 +0200)
committerVolker Lendecke <vl@samba.org>
Wed, 18 Aug 2010 13:14:02 +0000 (15:14 +0200)
source3/include/proto.h
source3/libsmb/clifile.c
source3/libsmb/clilist.c
source3/torture/masktest.c
source3/torture/torture.c

index 631d2868ec4e1d2539ee0c6afd627156d63f7925..4f63a77e992b0bff82c0e7e81d11840177f2aba5 100644 (file)
@@ -2070,6 +2070,8 @@ NTSTATUS cli_ntcreate(struct cli_state *cli,
                      uint16_t *pfid);
 uint8_t *smb_bytes_push_str(uint8_t *buf, bool ucs2, const char *str,
                            size_t str_len, size_t *pconverted_size);
+uint8_t *smb_bytes_push_bytes(uint8_t *buf, uint8_t prefix,
+                             const uint8_t *bytes, size_t num_bytes);
 struct tevent_req *cli_open_create(TALLOC_CTX *mem_ctx,
                                   struct event_context *ev,
                                   struct cli_state *cli, const char *fname,
@@ -2334,12 +2336,22 @@ bool unwrap_pac(TALLOC_CTX *mem_ctx, DATA_BLOB *auth_data, DATA_BLOB *unwrapped_
 
 /* The following definitions come from libsmb/clilist.c  */
 
-int cli_list_new(struct cli_state *cli,const char *Mask,uint16 attribute,
-                void (*fn)(const char *, struct file_info *, const char *,
-                           void *), void *state);
-int cli_list_old(struct cli_state *cli,const char *Mask,uint16 attribute,
-                void (*fn)(const char *, struct file_info *, const char *,
-                           void *), void *state);
+NTSTATUS cli_list_old(struct cli_state *cli,const char *Mask,uint16 attribute,
+                     void (*fn)(const char *, struct file_info *,
+                                const char *, void *), void *state);
+NTSTATUS cli_list_trans(struct cli_state *cli, const char *mask,
+                       uint16_t attribute, int info_level,
+                       void (*fn)(const char *mnt, struct file_info *finfo,
+                                  const char *mask, void *private_data),
+                       void *private_data);
+struct tevent_req *cli_list_send(TALLOC_CTX *mem_ctx,
+                                struct tevent_context *ev,
+                                struct cli_state *cli,
+                                const char *mask,
+                                uint16_t attribute,
+                                uint16_t info_level);
+NTSTATUS cli_list_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+                      struct file_info **finfo, size_t *num_finfo);
 NTSTATUS cli_list(struct cli_state *cli,const char *Mask,uint16 attribute,
                  void (*fn)(const char *, struct file_info *, const char *,
                             void *), void *state);
index 8965f6c71fb6cc8c0fa8224d6c4fa356f21d6449..d6b2e31de70db7d9b172357ea0d9ca3f8a2b0ca2 100644 (file)
@@ -91,6 +91,26 @@ uint8_t *smb_bytes_push_str(uint8_t *buf, bool ucs2,
                        true, pconverted_size);
 }
 
+uint8_t *smb_bytes_push_bytes(uint8_t *buf, uint8_t prefix,
+                             const uint8_t *bytes, size_t num_bytes)
+{
+       size_t buflen;
+
+       if (buf == NULL) {
+               return NULL;
+       }
+       buflen = talloc_get_size(buf);
+
+       buf = TALLOC_REALLOC_ARRAY(NULL, buf, uint8_t,
+                                  buflen + 1 + num_bytes);
+       if (buf == NULL) {
+               return NULL;
+       }
+       buf[buflen] = prefix;
+       memcpy(&buf[buflen+1], bytes, num_bytes);
+       return buf;
+}
+
 /***********************************************************
  Same as smb_bytes_push_str(), but without the odd byte
  align for ucs2 (we're pushing into a param or data block).
index 1a8b79be340b0f5a4054da13916c494b2c7f8195..29b16cb1c5f50e0b8be41c995e05bc1e8dbb4063 100644 (file)
@@ -226,289 +226,6 @@ static size_t interpret_long_filename(TALLOC_CTX *ctx,
        return calc_next_entry_offset(base, pdata_end);
 }
 
-/****************************************************************************
- Do a directory listing, calling fn on each file found.
-****************************************************************************/
-
-int cli_list_new(struct cli_state *cli,const char *Mask,uint16 attribute,
-                void (*fn)(const char *, struct file_info *, const char *,
-                           void *), void *state)
-{
-#if 1
-       int max_matches = 1366; /* Match W2k - was 512. */
-#else
-       int max_matches = 512;
-#endif
-       int info_level;
-       char *p, *p2, *rdata_end;
-       char *mask = NULL;
-       struct file_info finfo;
-       int i;
-       char *dirlist = NULL;
-       int dirlist_len = 0;
-       int total_received = -1;
-       bool First = True;
-       int ff_searchcount=0;
-       int ff_eos=0;
-       int ff_dir_handle=0;
-       int loop_count = 0;
-       char *rparam=NULL, *rdata=NULL;
-       unsigned int param_len, data_len;
-       uint16 setup;
-       char *param;
-       uint32 resume_key = 0;
-       TALLOC_CTX *frame = talloc_stackframe();
-       DATA_BLOB last_name_raw = data_blob_null;
-
-       /* NT uses SMB_FIND_FILE_BOTH_DIRECTORY_INFO,
-          OS/2 uses SMB_FIND_EA_SIZE. Both accept SMB_FIND_INFO_STANDARD. */
-       info_level = (cli->capabilities&CAP_NT_SMBS)?
-               SMB_FIND_FILE_BOTH_DIRECTORY_INFO : SMB_FIND_INFO_STANDARD;
-
-       mask = SMB_STRDUP(Mask);
-       if (!mask) {
-               TALLOC_FREE(frame);
-               return -1;
-       }
-
-       ZERO_STRUCT(finfo);
-
-       while (ff_eos == 0) {
-               size_t nlen = 2*(strlen(mask)+1);
-
-               loop_count++;
-               if (loop_count > 200) {
-                       DEBUG(0,("Error: Looping in FIND_NEXT??\n"));
-                       break;
-               }
-
-               param = SMB_MALLOC_ARRAY(char, 12+nlen+last_name_raw.length+2);
-               if (!param) {
-                       break;
-               }
-
-               if (First) {
-                       setup = TRANSACT2_FINDFIRST;
-                       SSVAL(param,0,attribute); /* attribute */
-                       SSVAL(param,2,max_matches); /* max count */
-                       SSVAL(param,4,(FLAG_TRANS2_FIND_REQUIRE_RESUME|FLAG_TRANS2_FIND_CLOSE_IF_END)); /* resume required + close on end */
-                       SSVAL(param,6,info_level);
-                       SIVAL(param,8,0);
-                       p = param+12;
-                       p += clistr_push(cli, param+12, mask,
-                                        nlen, STR_TERMINATE);
-               } else {
-                       setup = TRANSACT2_FINDNEXT;
-                       SSVAL(param,0,ff_dir_handle);
-                       SSVAL(param,2,max_matches); /* max count */
-                       SSVAL(param,4,info_level);
-                       /* For W2K servers serving out FAT filesystems we *must* set the
-                          resume key. If it's not FAT then it's returned as zero. */
-                       SIVAL(param,6,resume_key); /* ff_resume_key */
-                       /* NB. *DON'T* use continue here. If you do it seems that W2K and bretheren
-                          can miss filenames. Use last filename continue instead. JRA */
-                       SSVAL(param,10,(FLAG_TRANS2_FIND_REQUIRE_RESUME|FLAG_TRANS2_FIND_CLOSE_IF_END));        /* resume required + close on end */
-                       p = param+12;
-                       if (last_name_raw.length) {
-                               memcpy(p, last_name_raw.data, last_name_raw.length);
-                               p += last_name_raw.length;
-                       } else {
-                               p += clistr_push(cli, param+12, mask,
-                                               nlen, STR_TERMINATE);
-                       }
-               }
-
-               param_len = PTR_DIFF(p, param);
-
-               if (!cli_send_trans(cli, SMBtrans2,
-                                   NULL,                   /* Name */
-                                   -1, 0,                  /* fid, flags */
-                                   &setup, 1, 0,           /* setup, length, max */
-                                   param, param_len, 10,   /* param, length, max */
-                                   NULL, 0,
-#if 0
-                                   /* w2k value. */
-                                   MIN(16384,cli->max_xmit) /* data, length, max. */
-#else
-                                   cli->max_xmit           /* data, length, max. */
-#endif
-                                   )) {
-                       SAFE_FREE(param);
-                       TALLOC_FREE(frame);
-                       break;
-               }
-
-               SAFE_FREE(param);
-
-               if (!cli_receive_trans(cli, SMBtrans2,
-                                      &rparam, &param_len,
-                                      &rdata, &data_len) &&
-                    cli_is_dos_error(cli)) {
-                       /* We need to work around a Win95 bug - sometimes
-                          it gives ERRSRV/ERRerror temprarily */
-                       uint8 eclass;
-                       uint32 ecode;
-
-                       SAFE_FREE(rdata);
-                       SAFE_FREE(rparam);
-
-                       cli_dos_error(cli, &eclass, &ecode);
-
-                       /*
-                        * OS/2 might return "no more files",
-                        * which just tells us, that searchcount is zero
-                        * in this search.
-                        * Guenter Kukkukk <linux@kukkukk.com>
-                        */
-
-                       if (eclass == ERRDOS && ecode == ERRnofiles) {
-                               ff_searchcount = 0;
-                               cli_reset_error(cli);
-                               break;
-                       }
-
-                       if (eclass != ERRSRV || ecode != ERRerror)
-                               break;
-                       smb_msleep(100);
-                       continue;
-               }
-
-                if (cli_is_error(cli) || !rdata || !rparam) {
-                       SAFE_FREE(rdata);
-                       SAFE_FREE(rparam);
-                       break;
-               }
-
-               if (total_received == -1)
-                       total_received = 0;
-
-               /* parse out some important return info */
-               p = rparam;
-               if (First) {
-                       ff_dir_handle = SVAL(p,0);
-                       ff_searchcount = SVAL(p,2);
-                       ff_eos = SVAL(p,4);
-               } else {
-                       ff_searchcount = SVAL(p,0);
-                       ff_eos = SVAL(p,2);
-               }
-
-               if (ff_searchcount == 0) {
-                       SAFE_FREE(rdata);
-                       SAFE_FREE(rparam);
-                       break;
-               }
-
-               /* point to the data bytes */
-               p = rdata;
-               rdata_end = rdata + data_len;
-
-               /* we might need the lastname for continuations */
-               for (p2=p,i=0;i<ff_searchcount && p2 < rdata_end;i++) {
-                       if ((info_level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO) &&
-                                       (i == ff_searchcount-1)) {
-                               /* Last entry - fixup the last offset length. */
-                               SIVAL(p2,0,PTR_DIFF((rdata + data_len),p2));
-                       }
-                       p2 += interpret_long_filename(frame,
-                                                       cli,
-                                                       info_level,
-                                                       cli->inbuf,
-                                                       SVAL(cli->inbuf, smb_flg2),
-                                                       p2,
-                                                       rdata_end,
-                                                       &finfo,
-                                                       &resume_key,
-                                                       &last_name_raw);
-
-                       if (!finfo.name) {
-                               DEBUG(0,("cli_list_new: Error: unable to parse name from info level %d\n",
-                                       info_level));
-                               ff_eos = 1;
-                               break;
-                       }
-                       if (!First && *mask && strcsequal(finfo.name, mask)) {
-                               DEBUG(0,("Error: Looping in FIND_NEXT as name %s has already been seen?\n",
-                                       finfo.name));
-                               ff_eos = 1;
-                               break;
-                       }
-               }
-
-               SAFE_FREE(mask);
-               if (ff_searchcount > 0 && ff_eos == 0 && finfo.name) {
-                       mask = SMB_STRDUP(finfo.name);
-               } else {
-                       mask = SMB_STRDUP("");
-               }
-               if (!mask) {
-                       SAFE_FREE(rdata);
-                       SAFE_FREE(rparam);
-                       break;
-               }
-
-               /* grab the data for later use */
-               /* and add them to the dirlist pool */
-               dirlist = (char *)SMB_REALLOC(dirlist,dirlist_len + data_len);
-
-               if (!dirlist) {
-                       DEBUG(0,("cli_list_new: Failed to expand dirlist\n"));
-                       SAFE_FREE(rdata);
-                       SAFE_FREE(rparam);
-                       break;
-               }
-
-               memcpy(dirlist+dirlist_len,p,data_len);
-               dirlist_len += data_len;
-
-               total_received += ff_searchcount;
-
-               SAFE_FREE(rdata);
-               SAFE_FREE(rparam);
-
-               DEBUG(3,("received %d entries (eos=%d)\n",
-                        ff_searchcount,ff_eos));
-
-               if (ff_searchcount > 0)
-                       loop_count = 0;
-
-               First = False;
-       }
-
-        /* see if the server disconnected or the connection otherwise failed */
-        if (cli_is_error(cli)) {
-                total_received = -1;
-        } else {
-                /* no connection problem.  let user function add each entry */
-               rdata_end = dirlist + dirlist_len;
-                for (p=dirlist,i=0;i<total_received;i++) {
-                        p += interpret_long_filename(frame,
-                                                       cli,
-                                                       info_level,
-                                                       cli->inbuf,
-                                                       SVAL(cli->inbuf, smb_flg2),
-                                                       p,
-                                                       rdata_end,
-                                                       &finfo,
-                                                       NULL,
-                                                       NULL);
-                       if (!finfo.name) {
-                               DEBUG(0,("cli_list_new: unable to parse name from info level %d\n",
-                                       info_level));
-                               break;
-                       }
-                        fn(cli->dfs_mountpoint, &finfo, Mask, state);
-                }
-        }
-
-       /* free up the dirlist buffer and last name raw blob */
-       SAFE_FREE(dirlist);
-       data_blob_free(&last_name_raw);
-       SAFE_FREE(mask);
-       TALLOC_FREE(frame);
-       return(total_received);
-}
-
 /****************************************************************************
  Interpret a short filename structure.
  The length of the structure is returned.
@@ -549,163 +266,697 @@ static bool interpret_short_filename(TALLOC_CTX *ctx,
        return true;
 }
 
-/****************************************************************************
- Do a directory listing, calling fn on each file found.
- this uses the old SMBsearch interface. It is needed for testing Samba,
- but should otherwise not be used.
-****************************************************************************/
+struct cli_list_old_state {
+       struct tevent_context *ev;
+       struct cli_state *cli;
+       uint16_t vwv[2];
+       char *mask;
+       int num_asked;
+       uint16_t attribute;
+       uint8_t search_status[23];
+       bool first;
+       bool done;
+       uint8_t *dirlist;
+};
+
+static void cli_list_old_done(struct tevent_req *subreq);
+
+static struct tevent_req *cli_list_old_send(TALLOC_CTX *mem_ctx,
+                                           struct tevent_context *ev,
+                                           struct cli_state *cli,
+                                           const char *mask,
+                                           uint16_t attribute)
+{
+       struct tevent_req *req, *subreq;
+       struct cli_list_old_state *state;
+       uint8_t *bytes;
+       static const uint16_t zero = 0;
+
+       req = tevent_req_create(mem_ctx, &state, struct cli_list_old_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->ev = ev;
+       state->cli = cli;
+       state->attribute = attribute;
+       state->first = true;
+       state->mask = talloc_strdup(state, mask);
+       if (tevent_req_nomem(state->mask, req)) {
+               return tevent_req_post(req, ev);
+       }
+       state->num_asked = (cli->max_xmit - 100) / DIR_STRUCT_SIZE;
+
+       SSVAL(state->vwv + 0, 0, state->num_asked);
+       SSVAL(state->vwv + 1, 0, state->attribute);
+
+       bytes = talloc_array(state, uint8_t, 1);
+       if (tevent_req_nomem(bytes, req)) {
+               return tevent_req_post(req, ev);
+       }
+       bytes[0] = 4;
+       bytes = smb_bytes_push_str(bytes, cli_ucs2(cli), mask,
+                                  strlen(mask)+1, NULL);
 
-int cli_list_old(struct cli_state *cli,const char *Mask,uint16 attribute,
-                void (*fn)(const char *, struct file_info *, const char *,
-                           void *), void *state)
+       bytes = smb_bytes_push_bytes(bytes, 5, (uint8_t *)&zero, 2);
+       if (tevent_req_nomem(bytes, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       subreq = cli_smb_send(state, state->ev, state->cli, SMBsearch,
+                             0, 2, state->vwv, talloc_get_size(bytes), bytes);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, cli_list_old_done, req);
+       return req;
+}
+
+static void cli_list_old_done(struct tevent_req *subreq)
 {
-       char *p;
-       int received = 0;
-       bool first = True;
-       char status[21];
-       int num_asked = (cli->max_xmit - 100)/DIR_STRUCT_SIZE;
-       int num_received = 0;
-       int i;
-       char *dirlist = NULL;
-       char *mask = NULL;
-       TALLOC_CTX *frame = NULL;
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct cli_list_old_state *state = tevent_req_data(
+               req, struct cli_list_old_state);
+       NTSTATUS status;
+       uint8_t cmd;
+       uint8_t wct;
+       uint16_t *vwv;
+       uint32_t num_bytes;
+       uint8_t *bytes;
+       uint16_t received;
+       size_t dirlist_len;
+       uint8_t *tmp;
+
+       status = cli_smb_recv(subreq, state, NULL, 0, &wct, &vwv, &num_bytes,
+                             &bytes);
+       if (!NT_STATUS_IS_OK(status)
+           && !NT_STATUS_EQUAL(status, NT_STATUS_DOS(ERRDOS, ERRnofiles))
+           && !NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
+               TALLOC_FREE(subreq);
+               tevent_req_nterror(req, status);
+               return;
+       }
+       if (NT_STATUS_EQUAL(status, NT_STATUS_DOS(ERRDOS, ERRnofiles))
+           || NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
+               received = 0;
+       } else {
+               if (wct < 1) {
+                       TALLOC_FREE(subreq);
+                       tevent_req_nterror(
+                               req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+                       return;
+               }
+               received = SVAL(vwv + 0, 0);
+       }
+
+       if (received > 0) {
+               /*
+                * I don't think this can wrap. received is
+                * initialized from a 16-bit value.
+                */
+               if (num_bytes < (received * DIR_STRUCT_SIZE + 3)) {
+                       TALLOC_FREE(subreq);
+                       tevent_req_nterror(
+                               req, NT_STATUS_INVALID_NETWORK_RESPONSE);
+                       return;
+               }
 
-       ZERO_ARRAY(status);
+               dirlist_len = talloc_get_size(state->dirlist);
 
-       mask = SMB_STRDUP(Mask);
-       if (!mask) {
-               return -1;
+               tmp = TALLOC_REALLOC_ARRAY(
+                       state, state->dirlist, uint8_t,
+                       dirlist_len + received * DIR_STRUCT_SIZE);
+               if (tevent_req_nomem(tmp, req)) {
+                       return;
+               }
+               state->dirlist = tmp;
+               memcpy(state->dirlist + dirlist_len, bytes + 3,
+                      received * DIR_STRUCT_SIZE);
+
+               SSVAL(state->search_status, 0, 21);
+               memcpy(state->search_status + 2,
+                      bytes + 3 + (received-1)*DIR_STRUCT_SIZE, 21);
+               cmd = SMBsearch;
+       } else {
+               if (state->first || state->done) {
+                       tevent_req_done(req);
+                       return;
+               }
+               state->done = true;
+               state->num_asked = 0;
+               cmd = SMBfclose;
        }
+       TALLOC_FREE(subreq);
 
-       while (1) {
-               memset(cli->outbuf,'\0',smb_size);
-               memset(cli->inbuf,'\0',smb_size);
+       state->first = false;
 
-               cli_set_message(cli->outbuf,2,0,True);
+       SSVAL(state->vwv + 0, 0, state->num_asked);
+       SSVAL(state->vwv + 1, 0, state->attribute);
 
-               SCVAL(cli->outbuf,smb_com,SMBsearch);
+       bytes = talloc_array(state, uint8_t, 1);
+       if (tevent_req_nomem(bytes, req)) {
+               return;
+       }
+       bytes[0] = 4;
+       bytes = smb_bytes_push_str(bytes, cli_ucs2(state->cli), "",
+                                  1, NULL);
+       bytes = smb_bytes_push_bytes(bytes, 5, state->search_status,
+                                    sizeof(state->search_status));
+       if (tevent_req_nomem(bytes, req)) {
+               return;
+       }
+       subreq = cli_smb_send(state, state->ev, state->cli, cmd, 0,
+                             2, state->vwv, talloc_get_size(bytes), bytes);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, cli_list_old_done, req);
+}
 
-               SSVAL(cli->outbuf,smb_tid,cli->cnum);
-               cli_setup_packet(cli);
+static NTSTATUS cli_list_old_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+                                 struct file_info **pfinfo)
+{
+       struct cli_list_old_state *state = tevent_req_data(
+               req, struct cli_list_old_state);
+       NTSTATUS status;
+       size_t i, num_received;
+       struct file_info *finfo;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               return status;
+       }
 
-               SSVAL(cli->outbuf,smb_vwv0,num_asked);
-               SSVAL(cli->outbuf,smb_vwv1,attribute);
+       num_received = talloc_array_length(state->dirlist) / DIR_STRUCT_SIZE;
 
-               p = smb_buf(cli->outbuf);
-               *p++ = 4;
+       finfo = TALLOC_ARRAY(mem_ctx, struct file_info, num_received);
+       if (finfo == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
 
-               p += clistr_push(cli, p, first?mask:"",
-                               cli->bufsize - PTR_DIFF(p,cli->outbuf),
-                               STR_TERMINATE);
-               *p++ = 5;
-               if (first) {
-                       SSVAL(p,0,0);
-                       p += 2;
-               } else {
-                       SSVAL(p,0,21);
-                       p += 2;
-                       memcpy(p,status,21);
-                       p += 21;
+       for (i=0; i<num_received; i++) {
+               if (!interpret_short_filename(
+                           finfo, state->cli,
+                           (char *)state->dirlist + i * DIR_STRUCT_SIZE,
+                           &finfo[i])) {
+                       TALLOC_FREE(finfo);
+                       return NT_STATUS_NO_MEMORY;
                }
+       }
+       *pfinfo = finfo;
+       return NT_STATUS_OK;
+}
 
-               cli_setup_bcc(cli, p);
-               cli_send_smb(cli);
-               if (!cli_receive_smb(cli)) break;
+NTSTATUS cli_list_old(struct cli_state *cli, const char *mask,
+                     uint16 attribute,
+                     void (*fn)(const char *, struct file_info *,
+                                const char *, void *), void *state)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct event_context *ev;
+       struct tevent_req *req;
+       NTSTATUS status = NT_STATUS_NO_MEMORY;
+       struct file_info *finfo;
+       size_t i, num_finfo;
+
+       if (cli_has_async_calls(cli)) {
+               /*
+                * Can't use sync call while an async call is in flight
+                */
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto fail;
+       }
+       ev = event_context_init(frame);
+       if (ev == NULL) {
+               goto fail;
+       }
+       req = cli_list_old_send(frame, ev, cli, mask, attribute);
+       if (req == NULL) {
+               goto fail;
+       }
+       if (!tevent_req_poll(req, ev)) {
+               status = map_nt_error_from_unix(errno);
+               goto fail;
+       }
+       status = cli_list_old_recv(req, frame, &finfo);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto fail;
+       }
+       num_finfo = talloc_array_length(finfo);
+       for (i=0; i<num_finfo; i++) {
+               fn(cli->dfs_mountpoint, &finfo[i], mask, state);
+       }
+ fail:
+       TALLOC_FREE(frame);
+       if (!NT_STATUS_IS_OK(status)) {
+               cli_set_error(cli, status);
+       }
+       return status;
+}
+
+struct cli_list_trans_state {
+       struct tevent_context *ev;
+       struct cli_state *cli;
+       char *mask;
+       uint16_t attribute;
+       uint16_t info_level;
+
+       int loop_count;
+       int total_received;
+       uint16_t max_matches;
+       bool first;
+
+       int ff_eos;
+       int ff_dir_handle;
+
+       uint16_t setup[1];
+       uint8_t *param;
+
+       struct file_info *finfo;
+};
+
+static void cli_list_trans_done(struct tevent_req *subreq);
+
+static struct tevent_req *cli_list_trans_send(TALLOC_CTX *mem_ctx,
+                                             struct tevent_context *ev,
+                                             struct cli_state *cli,
+                                             const char *mask,
+                                             uint16_t attribute,
+                                             uint16_t info_level)
+{
+       struct tevent_req *req, *subreq;
+       struct cli_list_trans_state *state;
+       size_t nlen, param_len;
+       char *p;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct cli_list_trans_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->ev = ev;
+       state->cli = cli;
+       state->mask = talloc_strdup(state, mask);
+       if (tevent_req_nomem(state->mask, req)) {
+               return tevent_req_post(req, ev);
+       }
+       state->attribute = attribute;
+       state->info_level = info_level;
+       state->loop_count = 0;
+       state->first = true;
 
-               received = SVAL(cli->inbuf,smb_vwv0);
-               if (received <= 0) break;
+       state->max_matches = 1366; /* Match W2k */
+
+       state->setup[0] = TRANSACT2_FINDFIRST;
+
+       nlen = 2*(strlen(mask)+1);
+       state->param = TALLOC_ARRAY(state, uint8_t, 12+nlen+2);
+       if (tevent_req_nomem(state->param, req)) {
+               return tevent_req_post(req, ev);
+       }
 
-               /* Ensure we received enough data. */
-               if ((cli->inbuf+4+smb_len(cli->inbuf) - (smb_buf(cli->inbuf)+3)) <
-                               received*DIR_STRUCT_SIZE) {
+       SSVAL(state->param, 0, state->attribute);
+       SSVAL(state->param, 2, state->max_matches);
+       SSVAL(state->param, 4,
+             FLAG_TRANS2_FIND_REQUIRE_RESUME
+             |FLAG_TRANS2_FIND_CLOSE_IF_END);
+       SSVAL(state->param, 6, state->info_level);
+       SIVAL(state->param, 8, 0);
+
+       p = ((char *)state->param)+12;
+       p += clistr_push(state->cli, p, state->mask, nlen,
+                        STR_TERMINATE);
+       param_len = PTR_DIFF(p, state->param);
+
+       subreq = cli_trans_send(state, state->ev, state->cli,
+                               SMBtrans2, NULL, -1, 0, 0,
+                               state->setup, 1, 0,
+                               state->param, param_len, 10,
+                               NULL, 0, cli->max_xmit);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, cli_list_trans_done, req);
+       return req;
+}
+
+static void cli_list_trans_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct cli_list_trans_state *state = tevent_req_data(
+               req, struct cli_list_trans_state);
+       NTSTATUS status;
+       uint8_t *param;
+       uint32_t num_param;
+       uint8_t *data;
+       char *data_end;
+       uint32_t num_data;
+       uint32_t min_param;
+       struct file_info *tmp;
+       size_t old_num_finfo;
+       uint16_t recv_flags2;
+       int ff_searchcount;
+       bool ff_eos;
+       char *p, *p2;
+       uint32_t resume_key;
+       int i;
+       DATA_BLOB last_name_raw;
+       struct file_info *finfo;
+       size_t nlen, param_len;
+
+       min_param = (state->first ? 6 : 4);
+
+       status = cli_trans_recv(subreq, talloc_tos(), &recv_flags2,
+                               NULL, 0, NULL,
+                               &param, min_param, &num_param,
+                               &data, 0, &num_data);
+       TALLOC_FREE(subreq);
+       if (!NT_STATUS_IS_OK(status)) {
+               /*
+                * TODO: retry, OS/2 nofiles
+                */
+               tevent_req_nterror(req, status);
+               return;
+       }
+
+       if (state->first) {
+               state->ff_dir_handle = SVAL(param, 0);
+               ff_searchcount = SVAL(param, 2);
+               ff_eos = SVAL(param, 4) != 0;
+       } else {
+               ff_searchcount = SVAL(param, 0);
+               ff_eos = SVAL(param, 2) != 0;
+       }
+
+       old_num_finfo = talloc_array_length(state->finfo);
+
+       tmp = TALLOC_REALLOC_ARRAY(state, state->finfo, struct file_info,
+                                  old_num_finfo + ff_searchcount);
+       if (tevent_req_nomem(tmp, req)) {
+               return;
+       }
+       state->finfo = tmp;
+
+       p2 = p = (char *)data;
+       data_end = (char *)data + num_data;
+       last_name_raw = data_blob_null;
+
+       for (i=0; i<ff_searchcount; i++) {
+               if (p2 >= data_end) {
+                       ff_eos = true;
                        break;
                }
+               if ((state->info_level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO)
+                   && (i == ff_searchcount-1)) {
+                       /* Last entry - fixup the last offset length. */
+                       SIVAL(p2, 0, PTR_DIFF((data + num_data), p2));
+               }
+
+               data_blob_free(&last_name_raw);
 
-               first = False;
+               finfo = &state->finfo[old_num_finfo + i];
 
-               dirlist = (char *)SMB_REALLOC(
-                       dirlist,(num_received + received)*DIR_STRUCT_SIZE);
-               if (!dirlist) {
-                       DEBUG(0,("cli_list_old: failed to expand dirlist"));
-                       SAFE_FREE(mask);
-                       return 0;
+               p2 += interpret_long_filename(
+                       state->finfo, /* Stick fname to the array as such */
+                       state->cli, state->info_level,
+                       (char *)data, recv_flags2, p2,
+                       data_end, finfo, &resume_key, &last_name_raw);
+
+               if (finfo->name == NULL) {
+                       DEBUG(1, ("cli_list: Error: unable to parse name from "
+                                 "info level %d\n", state->info_level));
+                       ff_eos = true;
+                       break;
                }
+               if (!state->first && (state->mask[0] != '\0') &&
+                   strcsequal(finfo->name, state->mask)) {
+                       DEBUG(1, ("Error: Looping in FIND_NEXT as name %s has "
+                                 "already been seen?\n", finfo->name));
+                       ff_eos = true;
+                       break;
+               }
+       }
+
+       if (ff_searchcount == 0) {
+               ff_eos = true;
+       }
 
-               p = smb_buf(cli->inbuf) + 3;
+       TALLOC_FREE(param);
+       TALLOC_FREE(data);
 
-               memcpy(dirlist+num_received*DIR_STRUCT_SIZE,
-                      p,received*DIR_STRUCT_SIZE);
+       /*
+        * Shrink state->finfo to the real length we received
+        */
+       tmp = TALLOC_REALLOC_ARRAY(state, state->finfo, struct file_info,
+                                  old_num_finfo + i);
+       if (tevent_req_nomem(tmp, req)) {
+               return;
+       }
+       state->finfo = tmp;
 
-               memcpy(status,p + ((received-1)*DIR_STRUCT_SIZE),21);
+       state->first = false;
 
-               num_received += received;
+       if (ff_eos) {
+               data_blob_free(&last_name_raw);
+               tevent_req_done(req);
+               return;
+       }
 
-               if (cli_is_error(cli)) break;
+       TALLOC_FREE(state->mask);
+       state->mask = talloc_strdup(state, finfo->name);
+       if (tevent_req_nomem(state->mask, req)) {
+               return;
        }
 
-       if (!first) {
-               memset(cli->outbuf,'\0',smb_size);
-               memset(cli->inbuf,'\0',smb_size);
+       state->setup[0] = TRANSACT2_FINDNEXT;
 
-               cli_set_message(cli->outbuf,2,0,True);
-               SCVAL(cli->outbuf,smb_com,SMBfclose);
-               SSVAL(cli->outbuf,smb_tid,cli->cnum);
-               cli_setup_packet(cli);
+       nlen = 2*(strlen(state->mask) + 1);
 
-               SSVAL(cli->outbuf, smb_vwv0, 0); /* find count? */
-               SSVAL(cli->outbuf, smb_vwv1, attribute);
+       param = TALLOC_REALLOC_ARRAY(state, state->param, uint8_t,
+                                    12 + nlen + last_name_raw.length + 2);
+       if (tevent_req_nomem(param, req)) {
+               return;
+       }
+       state->param = param;
+
+       SSVAL(param, 0, state->ff_dir_handle);
+       SSVAL(param, 2, state->max_matches); /* max count */
+       SSVAL(param, 4, state->info_level);
+       /*
+        * For W2K servers serving out FAT filesystems we *must* set
+        * the resume key. If it's not FAT then it's returned as zero.
+        */
+       SIVAL(param, 6, resume_key); /* ff_resume_key */
+       /*
+        * NB. *DON'T* use continue here. If you do it seems that W2K
+        * and bretheren can miss filenames. Use last filename
+        * continue instead. JRA
+        */
+       SSVAL(param, 10, (FLAG_TRANS2_FIND_REQUIRE_RESUME
+                         |FLAG_TRANS2_FIND_CLOSE_IF_END));
+       p = ((char *)param)+12;
+       if (last_name_raw.length) {
+               memcpy(p, last_name_raw.data, last_name_raw.length);
+               p += last_name_raw.length;
+               data_blob_free(&last_name_raw);
+       } else {
+               p += clistr_push(state->cli, p, state->mask, nlen,
+                                STR_TERMINATE);
+       }
 
-               p = smb_buf(cli->outbuf);
-               *p++ = 4;
-               fstrcpy(p, "");
-               p += strlen(p) + 1;
-               *p++ = 5;
-               SSVAL(p, 0, 21);
-               p += 2;
-               memcpy(p,status,21);
-               p += 21;
+       param_len = PTR_DIFF(p, param);
 
-               cli_setup_bcc(cli, p);
-               cli_send_smb(cli);
-               if (!cli_receive_smb(cli)) {
-                       DEBUG(0,("Error closing search: %s\n",cli_errstr(cli)));
-               }
+       subreq = cli_trans_send(state, state->ev, state->cli,
+                               SMBtrans2, NULL, -1, 0, 0,
+                               state->setup, 1, 0,
+                               state->param, param_len, 10,
+                               NULL, 0, state->cli->max_xmit);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
        }
+       tevent_req_set_callback(subreq, cli_list_trans_done, req);
+}
 
-       frame = talloc_stackframe();
-       for (p=dirlist,i=0;i<num_received;i++) {
-               struct file_info finfo;
-               if (!interpret_short_filename(frame, cli, p, &finfo)) {
-                       break;
-               }
-               p += DIR_STRUCT_SIZE;
-               fn("\\", &finfo, Mask, state);
+static NTSTATUS cli_list_trans_recv(struct tevent_req *req,
+                                   TALLOC_CTX *mem_ctx,
+                                   struct file_info **finfo)
+{
+       struct cli_list_trans_state *state = tevent_req_data(
+               req, struct cli_list_trans_state);
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               return status;
        }
-       TALLOC_FREE(frame);
+       *finfo = talloc_move(mem_ctx, &state->finfo);
+       return NT_STATUS_OK;
+}
 
-       SAFE_FREE(mask);
-       SAFE_FREE(dirlist);
-       return(num_received);
+NTSTATUS cli_list_trans(struct cli_state *cli, const char *mask,
+                       uint16_t attribute, int info_level,
+                       void (*fn)(const char *mnt, struct file_info *finfo,
+                                  const char *mask, void *private_data),
+                       void *private_data)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct event_context *ev;
+       struct tevent_req *req;
+       int i, num_finfo;
+       struct file_info *finfo;
+       NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+       if (cli_has_async_calls(cli)) {
+               /*
+                * Can't use sync call while an async call is in flight
+                */
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto fail;
+       }
+       ev = event_context_init(frame);
+       if (ev == NULL) {
+               goto fail;
+       }
+       req = cli_list_trans_send(frame, ev, cli, mask, attribute, info_level);
+       if (req == NULL) {
+               goto fail;
+       }
+       if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+               goto fail;
+       }
+       status = cli_list_trans_recv(req, frame, &finfo);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto fail;
+       }
+       num_finfo = talloc_array_length(finfo);
+       for (i=0; i<num_finfo; i++) {
+               fn(cli->dfs_mountpoint, &finfo[i], mask, private_data);
+       }
+ fail:
+       TALLOC_FREE(frame);
+       if (!NT_STATUS_IS_OK(status)) {
+               cli_set_error(cli, status);
+       }
+       return status;
 }
 
-/****************************************************************************
- Do a directory listing, calling fn on each file found.
- This auto-switches between old and new style.
-****************************************************************************/
+struct cli_list_state {
+       NTSTATUS (*recv_fn)(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+                           struct file_info **finfo);
+       struct file_info *finfo;
+};
 
-NTSTATUS cli_list(struct cli_state *cli,const char *Mask,uint16 attribute,
-                 void (*fn)(const char *, struct file_info *, const char *,
-                            void *), void *state)
+static void cli_list_done(struct tevent_req *subreq);
+
+struct tevent_req *cli_list_send(TALLOC_CTX *mem_ctx,
+                                struct tevent_context *ev,
+                                struct cli_state *cli,
+                                const char *mask,
+                                uint16_t attribute,
+                                uint16_t info_level)
 {
-       int rec;
+       struct tevent_req *req, *subreq;
+       struct cli_list_state *state;
+
+       req = tevent_req_create(mem_ctx, &state, struct cli_list_state);
+       if (req == NULL) {
+               return NULL;
+       }
 
        if (cli->protocol <= PROTOCOL_LANMAN1) {
-               rec = cli_list_old(cli, Mask, attribute, fn, state);
+               subreq = cli_list_old_send(state, ev, cli, mask, attribute);
+               state->recv_fn = cli_list_old_recv;
        } else {
-               rec = cli_list_new(cli, Mask, attribute, fn, state);
+               subreq = cli_list_trans_send(state, ev, cli, mask, attribute,
+                                            info_level);
+               state->recv_fn = cli_list_trans_recv;
+       }
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, cli_list_done, req);
+       return req;
+}
+
+static void cli_list_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct cli_list_state *state = tevent_req_data(
+               req, struct cli_list_state);
+       NTSTATUS status;
+
+       status = state->recv_fn(subreq, state, &state->finfo);
+       TALLOC_FREE(subreq);
+       if (!NT_STATUS_IS_OK(status)) {
+               tevent_req_nterror(req, status);
+               return;
        }
-       if (rec == -1) {
-               return cli_nt_error(cli);
+       tevent_req_done(req);
+}
+
+NTSTATUS cli_list_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+                      struct file_info **finfo, size_t *num_finfo)
+{
+       struct cli_list_state *state = tevent_req_data(
+               req, struct cli_list_state);
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               return status;
        }
+       *num_finfo = talloc_array_length(state->finfo);
+       *finfo = talloc_move(mem_ctx, &state->finfo);
        return NT_STATUS_OK;
 }
+
+NTSTATUS cli_list(struct cli_state *cli, const char *mask, uint16 attribute,
+                 void (*fn)(const char *, struct file_info *, const char *,
+                            void *), void *state)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct event_context *ev;
+       struct tevent_req *req;
+       NTSTATUS status = NT_STATUS_NO_MEMORY;
+       struct file_info *finfo;
+       size_t i, num_finfo;
+       uint16_t info_level;
+
+       if (cli_has_async_calls(cli)) {
+               /*
+                * Can't use sync call while an async call is in flight
+                */
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto fail;
+       }
+       ev = event_context_init(frame);
+       if (ev == NULL) {
+               goto fail;
+       }
+
+       info_level = (cli->capabilities & CAP_NT_SMBS)
+               ? SMB_FIND_FILE_BOTH_DIRECTORY_INFO : SMB_FIND_INFO_STANDARD;
+
+       req = cli_list_send(frame, ev, cli, mask, attribute, info_level);
+       if (req == NULL) {
+               goto fail;
+       }
+       if (!tevent_req_poll(req, ev)) {
+               status = map_nt_error_from_unix(errno);
+               goto fail;
+       }
+
+       status = cli_list_recv(req, frame, &finfo, &num_finfo);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto fail;
+       }
+
+       for (i=0; i<num_finfo; i++) {
+               fn(cli->dfs_mountpoint, &finfo[i], mask, state);
+       }
+ fail:
+       TALLOC_FREE(frame);
+       if (!NT_STATUS_IS_OK(status)) {
+               cli_set_error(cli, status);
+       }
+       return status;
+}
index 41c472a95124683fca0dc5a80ed34b8846320b29..51de6d9505a6e755b76afc623568753bf3307e71 100644 (file)
@@ -263,11 +263,16 @@ static struct cli_state *connect_one(char *share)
 }
 
 static char *resultp;
-static struct file_info *f_info;
+
+struct rn_state {
+       char **pp_long_name;
+       char *short_name;
+};
 
 static void listfn(const char *mnt, struct file_info *f, const char *s,
-                  void *state)
+                  void *private_data)
 {
+       struct rn_state *state = (struct rn_state *)private_data;
        if (strcmp(f->name,".") == 0) {
                resultp[0] = '+';
        } else if (strcmp(f->name,"..") == 0) {
@@ -275,28 +280,38 @@ static void listfn(const char *mnt, struct file_info *f, const char *s,
        } else {
                resultp[2] = '+';
        }
-       f_info = f;
+
+       if ((state == NULL) || ISDOT(f->name) || ISDOTDOT(f->name))  {
+               return;
+       }
+
+       fstrcpy(state->short_name, f->short_name);
+       strlower_m(state->short_name);
+       *state->pp_long_name = SMB_STRDUP(f->name);
+       if (!*state->pp_long_name) {
+               return;
+       }
+       strlower_m(*state->pp_long_name);
 }
 
 static void get_real_name(struct cli_state *cli,
                          char **pp_long_name, fstring short_name)
 {
+       struct rn_state state;
+
+       state.pp_long_name = pp_long_name;
+       state.short_name = short_name;
+
        *pp_long_name = NULL;
        /* nasty hack to force level 260 listings - tridge */
-       cli->capabilities |= CAP_NT_SMBS;
        if (max_protocol <= PROTOCOL_LANMAN1) {
-               cli_list_new(cli, "\\masktest\\*.*", aHIDDEN | aDIR, listfn, NULL);
+               cli_list_trans(cli, "\\masktest\\*.*", aHIDDEN | aDIR,
+                              SMB_FIND_FILE_BOTH_DIRECTORY_INFO, listfn,
+                              &state);
        } else {
-               cli_list_new(cli, "\\masktest\\*", aHIDDEN | aDIR, listfn, NULL);
-       }
-       if (f_info) {
-               fstrcpy(short_name, f_info->short_name);
-               strlower_m(short_name);
-               *pp_long_name = SMB_STRDUP(f_info->name);
-               if (!*pp_long_name) {
-                       return;
-               }
-               strlower_m(*pp_long_name);
+               cli_list_trans(cli, "\\masktest\\*", aHIDDEN | aDIR,
+                              SMB_FIND_FILE_BOTH_DIRECTORY_INFO,
+                              listfn, &state);
        }
 
        if (*short_name == 0) {
@@ -331,12 +346,10 @@ static void testpair(struct cli_state *cli, const char *mask, const char *file)
 
        resultp = res1;
        fstrcpy(short_name, "");
-       f_info = NULL;
        get_real_name(cli, &long_name, short_name);
        if (!long_name) {
                return;
        }
-       f_info = NULL;
        fstrcpy(res1, "---");
        cli_list(cli, mask, aHIDDEN | aDIR, listfn, NULL);
 
index 01a085d89aea4d1c9206a2de5d80b0df20f27eec..0e3ee18a37337d4e80c7ef803f64a659947791e3 100644 (file)
@@ -5183,7 +5183,8 @@ static bool run_dirtest1(int dummy)
        }
 
        /* Now ensure that doing an old list sees both files and directories. */
-       num_seen = cli_list_old(cli, "\\LISTDIR\\*", aDIR, list_fn, NULL);
+       num_seen = 0;
+       cli_list_old(cli, "\\LISTDIR\\*", aDIR, list_fn, &num_seen);
        printf("num_seen = %d\n", num_seen );
        /* We should see 100 files + 1000 directories + . and .. */
        if (num_seen != 2002)
@@ -5192,12 +5193,14 @@ static bool run_dirtest1(int dummy)
        /* Ensure if we have the "must have" bits we only see the
         * relevent entries.
         */
-       num_seen = cli_list_old(cli, "\\LISTDIR\\*", (aDIR<<8)|aDIR, list_fn, NULL);
+       num_seen = 0;
+       cli_list_old(cli, "\\LISTDIR\\*", (aDIR<<8)|aDIR, list_fn, &num_seen);
        printf("num_seen = %d\n", num_seen );
        if (num_seen != 1002)
                correct = False;
 
-       num_seen = cli_list_old(cli, "\\LISTDIR\\*", (aARCH<<8)|aDIR, list_fn, NULL);
+       num_seen = 0;
+       cli_list_old(cli, "\\LISTDIR\\*", (aARCH<<8)|aDIR, list_fn, &num_seen);
        printf("num_seen = %d\n", num_seen );
        if (num_seen != 1000)
                correct = False;