summaryrefslogtreecommitdiff
path: root/source3/libsmb
diff options
context:
space:
mode:
authorVolker Lendecke <vl@samba.org>2010-08-10 07:44:15 +0200
committerVolker Lendecke <vl@samba.org>2010-08-18 15:14:02 +0200
commitd7c8fb21bb0a29bb7227d4b242aba2f1524f6c48 (patch)
treec305bcfb7a4de6bc565d6629ce8c33fe37b9bb1a /source3/libsmb
parent77761d9adcf34a9d1cd4567422c98efac101b3f6 (diff)
downloadsamba-d7c8fb21bb0a29bb7227d4b242aba2f1524f6c48.tar.gz
samba-d7c8fb21bb0a29bb7227d4b242aba2f1524f6c48.tar.bz2
samba-d7c8fb21bb0a29bb7227d4b242aba2f1524f6c48.zip
s3: async cli_list
Diffstat (limited to 'source3/libsmb')
-rw-r--r--source3/libsmb/clifile.c20
-rw-r--r--source3/libsmb/clilist.c1043
2 files changed, 667 insertions, 396 deletions
diff --git a/source3/libsmb/clifile.c b/source3/libsmb/clifile.c
index 8965f6c71f..d6b2e31de7 100644
--- a/source3/libsmb/clifile.c
+++ b/source3/libsmb/clifile.c
@@ -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).
diff --git a/source3/libsmb/clilist.c b/source3/libsmb/clilist.c
index 1a8b79be34..29b16cb1c5 100644
--- a/source3/libsmb/clilist.c
+++ b/source3/libsmb/clilist.c
@@ -227,289 +227,6 @@ static size_t interpret_long_filename(TALLOC_CTX *ctx,
}
/****************************************************************************
- 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;
+}