s3-smbclient: Fix cli_errstr() usage (part of bug #7864)
[Samba/vl.git] / source3 / libsmb / clilist.c
blob87c66672e101b7d94ebd162405e1e29d70feed09
1 /*
2 Unix SMB/CIFS implementation.
3 client directory list routines
4 Copyright (C) Andrew Tridgell 1994-1998
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "includes.h"
21 #include "async_smb.h"
23 /****************************************************************************
24 Calculate a safe next_entry_offset.
25 ****************************************************************************/
27 static size_t calc_next_entry_offset(const char *base, const char *pdata_end)
29 size_t next_entry_offset = (size_t)IVAL(base,0);
31 if (next_entry_offset == 0 ||
32 base + next_entry_offset < base ||
33 base + next_entry_offset > pdata_end) {
34 next_entry_offset = pdata_end - base;
36 return next_entry_offset;
39 /****************************************************************************
40 Interpret a long filename structure - this is mostly guesses at the moment.
41 The length of the structure is returned
42 The structure of a long filename depends on the info level.
43 SMB_FIND_FILE_BOTH_DIRECTORY_INFO is used
44 by NT and SMB_FIND_EA_SIZE is used by OS/2
45 ****************************************************************************/
47 static size_t interpret_long_filename(TALLOC_CTX *ctx,
48 struct cli_state *cli,
49 int level,
50 const char *base_ptr,
51 uint16_t recv_flags2,
52 const char *p,
53 const char *pdata_end,
54 struct file_info *finfo,
55 uint32 *p_resume_key,
56 DATA_BLOB *p_last_name_raw)
58 int len;
59 size_t ret;
60 const char *base = p;
62 data_blob_free(p_last_name_raw);
64 if (p_resume_key) {
65 *p_resume_key = 0;
67 ZERO_STRUCTP(finfo);
69 switch (level) {
70 case SMB_FIND_INFO_STANDARD: /* OS/2 understands this */
71 /* these dates are converted to GMT by
72 make_unix_date */
73 if (pdata_end - base < 27) {
74 return pdata_end - base;
76 finfo->ctime_ts = convert_time_t_to_timespec(
77 make_unix_date2(p+4, cli->serverzone));
78 finfo->atime_ts = convert_time_t_to_timespec(
79 make_unix_date2(p+8, cli->serverzone));
80 finfo->mtime_ts = convert_time_t_to_timespec(
81 make_unix_date2(p+12, cli->serverzone));
82 finfo->size = IVAL(p,16);
83 finfo->mode = CVAL(p,24);
84 len = CVAL(p, 26);
85 p += 27;
86 p += align_string(base_ptr, p, 0);
88 /* We can safely use len here (which is required by OS/2)
89 * and the NAS-BASIC server instead of +2 or +1 as the
90 * STR_TERMINATE flag below is
91 * actually used as the length calculation.
92 * The len is merely an upper bound.
93 * Due to the explicit 2 byte null termination
94 * in cli_receive_trans/cli_receive_nt_trans
95 * we know this is safe. JRA + kukks
98 if (p + len > pdata_end) {
99 return pdata_end - base;
102 /* the len+2 below looks strange but it is
103 important to cope with the differences
104 between win2000 and win9x for this call
105 (tridge) */
106 ret = clistr_pull_talloc(ctx,
107 base_ptr,
108 recv_flags2,
109 &finfo->name,
111 len+2,
112 STR_TERMINATE);
113 if (ret == (size_t)-1) {
114 return pdata_end - base;
116 p += ret;
117 return PTR_DIFF(p, base);
119 case SMB_FIND_EA_SIZE: /* this is what OS/2 uses mostly */
120 /* these dates are converted to GMT by
121 make_unix_date */
122 if (pdata_end - base < 31) {
123 return pdata_end - base;
125 finfo->ctime_ts = convert_time_t_to_timespec(
126 make_unix_date2(p+4, cli->serverzone));
127 finfo->atime_ts = convert_time_t_to_timespec(
128 make_unix_date2(p+8, cli->serverzone));
129 finfo->mtime_ts = convert_time_t_to_timespec(
130 make_unix_date2(p+12, cli->serverzone));
131 finfo->size = IVAL(p,16);
132 finfo->mode = CVAL(p,24);
133 len = CVAL(p, 30);
134 p += 31;
135 /* check for unisys! */
136 if (p + len + 1 > pdata_end) {
137 return pdata_end - base;
139 ret = clistr_pull_talloc(ctx,
140 base_ptr,
141 recv_flags2,
142 &finfo->name,
144 len,
145 STR_NOALIGN);
146 if (ret == (size_t)-1) {
147 return pdata_end - base;
149 p += ret;
150 return PTR_DIFF(p, base) + 1;
152 case SMB_FIND_FILE_BOTH_DIRECTORY_INFO: /* NT uses this, but also accepts 2 */
154 size_t namelen, slen;
156 if (pdata_end - base < 94) {
157 return pdata_end - base;
160 p += 4; /* next entry offset */
162 if (p_resume_key) {
163 *p_resume_key = IVAL(p,0);
165 p += 4; /* fileindex */
167 /* Offset zero is "create time", not "change time". */
168 p += 8;
169 finfo->atime_ts = interpret_long_date(p);
170 p += 8;
171 finfo->mtime_ts = interpret_long_date(p);
172 p += 8;
173 finfo->ctime_ts = interpret_long_date(p);
174 p += 8;
175 finfo->size = IVAL2_TO_SMB_BIG_UINT(p,0);
176 p += 8;
177 p += 8; /* alloc size */
178 finfo->mode = CVAL(p,0);
179 p += 4;
180 namelen = IVAL(p,0);
181 p += 4;
182 p += 4; /* EA size */
183 slen = SVAL(p, 0);
184 if (slen > 24) {
185 /* Bad short name length. */
186 return pdata_end - base;
188 p += 2;
190 /* stupid NT bugs. grr */
191 int flags = 0;
192 if (p[1] == 0 && namelen > 1) flags |= STR_UNICODE;
193 clistr_pull(base_ptr, finfo->short_name, p,
194 sizeof(finfo->short_name),
195 slen, flags);
197 p += 24; /* short name? */
198 if (p + namelen < p || p + namelen > pdata_end) {
199 return pdata_end - base;
201 ret = clistr_pull_talloc(ctx,
202 base_ptr,
203 recv_flags2,
204 &finfo->name,
206 namelen,
208 if (ret == (size_t)-1) {
209 return pdata_end - base;
212 /* To be robust in the face of unicode conversion failures
213 we need to copy the raw bytes of the last name seen here.
214 Namelen doesn't include the terminating unicode null, so
215 copy it here. */
217 if (p_last_name_raw) {
218 *p_last_name_raw = data_blob(NULL, namelen+2);
219 memcpy(p_last_name_raw->data, p, namelen);
220 SSVAL(p_last_name_raw->data, namelen, 0);
222 return calc_next_entry_offset(base, pdata_end);
226 DEBUG(1,("Unknown long filename format %d\n",level));
227 return calc_next_entry_offset(base, pdata_end);
230 /****************************************************************************
231 Interpret a short filename structure.
232 The length of the structure is returned.
233 ****************************************************************************/
235 static bool interpret_short_filename(TALLOC_CTX *ctx,
236 struct cli_state *cli,
237 char *p,
238 struct file_info *finfo)
240 size_t ret;
241 ZERO_STRUCTP(finfo);
243 finfo->mode = CVAL(p,21);
245 /* this date is converted to GMT by make_unix_date */
246 finfo->ctime_ts.tv_sec = make_unix_date(p+22, cli->serverzone);
247 finfo->ctime_ts.tv_nsec = 0;
248 finfo->mtime_ts.tv_sec = finfo->atime_ts.tv_sec = finfo->ctime_ts.tv_sec;
249 finfo->mtime_ts.tv_nsec = finfo->atime_ts.tv_nsec = 0;
250 finfo->size = IVAL(p,26);
251 ret = clistr_pull_talloc(ctx,
252 cli->inbuf,
253 SVAL(cli->inbuf, smb_flg2),
254 &finfo->name,
255 p+30,
257 STR_ASCII);
258 if (ret == (size_t)-1) {
259 return false;
262 if (finfo->name) {
263 strlcpy(finfo->short_name,
264 finfo->name,
265 sizeof(finfo->short_name));
267 return true;
270 struct cli_list_old_state {
271 struct tevent_context *ev;
272 struct cli_state *cli;
273 uint16_t vwv[2];
274 char *mask;
275 int num_asked;
276 uint16_t attribute;
277 uint8_t search_status[23];
278 bool first;
279 bool done;
280 uint8_t *dirlist;
283 static void cli_list_old_done(struct tevent_req *subreq);
285 static struct tevent_req *cli_list_old_send(TALLOC_CTX *mem_ctx,
286 struct tevent_context *ev,
287 struct cli_state *cli,
288 const char *mask,
289 uint16_t attribute)
291 struct tevent_req *req, *subreq;
292 struct cli_list_old_state *state;
293 uint8_t *bytes;
294 static const uint16_t zero = 0;
296 req = tevent_req_create(mem_ctx, &state, struct cli_list_old_state);
297 if (req == NULL) {
298 return NULL;
300 state->ev = ev;
301 state->cli = cli;
302 state->attribute = attribute;
303 state->first = true;
304 state->mask = talloc_strdup(state, mask);
305 if (tevent_req_nomem(state->mask, req)) {
306 return tevent_req_post(req, ev);
308 state->num_asked = (cli->max_xmit - 100) / DIR_STRUCT_SIZE;
310 SSVAL(state->vwv + 0, 0, state->num_asked);
311 SSVAL(state->vwv + 1, 0, state->attribute);
313 bytes = talloc_array(state, uint8_t, 1);
314 if (tevent_req_nomem(bytes, req)) {
315 return tevent_req_post(req, ev);
317 bytes[0] = 4;
318 bytes = smb_bytes_push_str(bytes, cli_ucs2(cli), mask,
319 strlen(mask)+1, NULL);
321 bytes = smb_bytes_push_bytes(bytes, 5, (uint8_t *)&zero, 2);
322 if (tevent_req_nomem(bytes, req)) {
323 return tevent_req_post(req, ev);
326 subreq = cli_smb_send(state, state->ev, state->cli, SMBsearch,
327 0, 2, state->vwv, talloc_get_size(bytes), bytes);
328 if (tevent_req_nomem(subreq, req)) {
329 return tevent_req_post(req, ev);
331 tevent_req_set_callback(subreq, cli_list_old_done, req);
332 return req;
335 static void cli_list_old_done(struct tevent_req *subreq)
337 struct tevent_req *req = tevent_req_callback_data(
338 subreq, struct tevent_req);
339 struct cli_list_old_state *state = tevent_req_data(
340 req, struct cli_list_old_state);
341 NTSTATUS status;
342 uint8_t cmd;
343 uint8_t wct;
344 uint16_t *vwv;
345 uint32_t num_bytes;
346 uint8_t *bytes;
347 uint16_t received;
348 size_t dirlist_len;
349 uint8_t *tmp;
351 status = cli_smb_recv(subreq, state, NULL, 0, &wct, &vwv, &num_bytes,
352 &bytes);
353 if (!NT_STATUS_IS_OK(status)
354 && !NT_STATUS_EQUAL(status, NT_STATUS_DOS(ERRDOS, ERRnofiles))
355 && !NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
356 TALLOC_FREE(subreq);
357 tevent_req_nterror(req, status);
358 return;
360 if (NT_STATUS_EQUAL(status, NT_STATUS_DOS(ERRDOS, ERRnofiles))
361 || NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
362 received = 0;
363 } else {
364 if (wct < 1) {
365 TALLOC_FREE(subreq);
366 tevent_req_nterror(
367 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
368 return;
370 received = SVAL(vwv + 0, 0);
373 if (received > 0) {
375 * I don't think this can wrap. received is
376 * initialized from a 16-bit value.
378 if (num_bytes < (received * DIR_STRUCT_SIZE + 3)) {
379 TALLOC_FREE(subreq);
380 tevent_req_nterror(
381 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
382 return;
385 dirlist_len = talloc_get_size(state->dirlist);
387 tmp = TALLOC_REALLOC_ARRAY(
388 state, state->dirlist, uint8_t,
389 dirlist_len + received * DIR_STRUCT_SIZE);
390 if (tevent_req_nomem(tmp, req)) {
391 return;
393 state->dirlist = tmp;
394 memcpy(state->dirlist + dirlist_len, bytes + 3,
395 received * DIR_STRUCT_SIZE);
397 SSVAL(state->search_status, 0, 21);
398 memcpy(state->search_status + 2,
399 bytes + 3 + (received-1)*DIR_STRUCT_SIZE, 21);
400 cmd = SMBsearch;
401 } else {
402 if (state->first || state->done) {
403 tevent_req_done(req);
404 return;
406 state->done = true;
407 state->num_asked = 0;
408 cmd = SMBfclose;
410 TALLOC_FREE(subreq);
412 state->first = false;
414 SSVAL(state->vwv + 0, 0, state->num_asked);
415 SSVAL(state->vwv + 1, 0, state->attribute);
417 bytes = talloc_array(state, uint8_t, 1);
418 if (tevent_req_nomem(bytes, req)) {
419 return;
421 bytes[0] = 4;
422 bytes = smb_bytes_push_str(bytes, cli_ucs2(state->cli), "",
423 1, NULL);
424 bytes = smb_bytes_push_bytes(bytes, 5, state->search_status,
425 sizeof(state->search_status));
426 if (tevent_req_nomem(bytes, req)) {
427 return;
429 subreq = cli_smb_send(state, state->ev, state->cli, cmd, 0,
430 2, state->vwv, talloc_get_size(bytes), bytes);
431 if (tevent_req_nomem(subreq, req)) {
432 return;
434 tevent_req_set_callback(subreq, cli_list_old_done, req);
437 static NTSTATUS cli_list_old_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
438 struct file_info **pfinfo)
440 struct cli_list_old_state *state = tevent_req_data(
441 req, struct cli_list_old_state);
442 NTSTATUS status;
443 size_t i, num_received;
444 struct file_info *finfo;
446 if (tevent_req_is_nterror(req, &status)) {
447 return status;
450 num_received = talloc_array_length(state->dirlist) / DIR_STRUCT_SIZE;
452 finfo = TALLOC_ARRAY(mem_ctx, struct file_info, num_received);
453 if (finfo == NULL) {
454 return NT_STATUS_NO_MEMORY;
457 for (i=0; i<num_received; i++) {
458 if (!interpret_short_filename(
459 finfo, state->cli,
460 (char *)state->dirlist + i * DIR_STRUCT_SIZE,
461 &finfo[i])) {
462 TALLOC_FREE(finfo);
463 return NT_STATUS_NO_MEMORY;
466 *pfinfo = finfo;
467 return NT_STATUS_OK;
470 NTSTATUS cli_list_old(struct cli_state *cli, const char *mask,
471 uint16 attribute,
472 NTSTATUS (*fn)(const char *, struct file_info *,
473 const char *, void *), void *state)
475 TALLOC_CTX *frame = talloc_stackframe();
476 struct event_context *ev;
477 struct tevent_req *req;
478 NTSTATUS status = NT_STATUS_NO_MEMORY;
479 struct file_info *finfo;
480 size_t i, num_finfo;
482 if (cli_has_async_calls(cli)) {
484 * Can't use sync call while an async call is in flight
486 status = NT_STATUS_INVALID_PARAMETER;
487 goto fail;
489 ev = event_context_init(frame);
490 if (ev == NULL) {
491 goto fail;
493 req = cli_list_old_send(frame, ev, cli, mask, attribute);
494 if (req == NULL) {
495 goto fail;
497 if (!tevent_req_poll(req, ev)) {
498 status = map_nt_error_from_unix(errno);
499 goto fail;
501 status = cli_list_old_recv(req, frame, &finfo);
502 if (!NT_STATUS_IS_OK(status)) {
503 goto fail;
505 num_finfo = talloc_array_length(finfo);
506 for (i=0; i<num_finfo; i++) {
507 status = fn(cli->dfs_mountpoint, &finfo[i], mask, state);
508 if (!NT_STATUS_IS_OK(status)) {
509 goto fail;
512 fail:
513 TALLOC_FREE(frame);
514 if (!NT_STATUS_IS_OK(status)) {
515 cli_set_error(cli, status);
517 return status;
520 struct cli_list_trans_state {
521 struct tevent_context *ev;
522 struct cli_state *cli;
523 char *mask;
524 uint16_t attribute;
525 uint16_t info_level;
527 int loop_count;
528 int total_received;
529 uint16_t max_matches;
530 bool first;
532 int ff_eos;
533 int ff_dir_handle;
535 uint16_t setup[1];
536 uint8_t *param;
538 struct file_info *finfo;
541 static void cli_list_trans_done(struct tevent_req *subreq);
543 static struct tevent_req *cli_list_trans_send(TALLOC_CTX *mem_ctx,
544 struct tevent_context *ev,
545 struct cli_state *cli,
546 const char *mask,
547 uint16_t attribute,
548 uint16_t info_level)
550 struct tevent_req *req, *subreq;
551 struct cli_list_trans_state *state;
552 size_t nlen, param_len;
553 char *p;
555 req = tevent_req_create(mem_ctx, &state,
556 struct cli_list_trans_state);
557 if (req == NULL) {
558 return NULL;
560 state->ev = ev;
561 state->cli = cli;
562 state->mask = talloc_strdup(state, mask);
563 if (tevent_req_nomem(state->mask, req)) {
564 return tevent_req_post(req, ev);
566 state->attribute = attribute;
567 state->info_level = info_level;
568 state->loop_count = 0;
569 state->first = true;
571 state->max_matches = 1366; /* Match W2k */
573 state->setup[0] = TRANSACT2_FINDFIRST;
575 nlen = 2*(strlen(mask)+1);
576 state->param = TALLOC_ARRAY(state, uint8_t, 12+nlen+2);
577 if (tevent_req_nomem(state->param, req)) {
578 return tevent_req_post(req, ev);
581 SSVAL(state->param, 0, state->attribute);
582 SSVAL(state->param, 2, state->max_matches);
583 SSVAL(state->param, 4,
584 FLAG_TRANS2_FIND_REQUIRE_RESUME
585 |FLAG_TRANS2_FIND_CLOSE_IF_END);
586 SSVAL(state->param, 6, state->info_level);
587 SIVAL(state->param, 8, 0);
589 p = ((char *)state->param)+12;
590 p += clistr_push(state->cli, p, state->mask, nlen,
591 STR_TERMINATE);
592 param_len = PTR_DIFF(p, state->param);
594 subreq = cli_trans_send(state, state->ev, state->cli,
595 SMBtrans2, NULL, -1, 0, 0,
596 state->setup, 1, 0,
597 state->param, param_len, 10,
598 NULL, 0, cli->max_xmit);
599 if (tevent_req_nomem(subreq, req)) {
600 return tevent_req_post(req, ev);
602 tevent_req_set_callback(subreq, cli_list_trans_done, req);
603 return req;
606 static void cli_list_trans_done(struct tevent_req *subreq)
608 struct tevent_req *req = tevent_req_callback_data(
609 subreq, struct tevent_req);
610 struct cli_list_trans_state *state = tevent_req_data(
611 req, struct cli_list_trans_state);
612 NTSTATUS status;
613 uint8_t *param;
614 uint32_t num_param;
615 uint8_t *data;
616 char *data_end;
617 uint32_t num_data;
618 uint32_t min_param;
619 struct file_info *tmp;
620 size_t old_num_finfo;
621 uint16_t recv_flags2;
622 int ff_searchcount;
623 bool ff_eos;
624 char *p, *p2;
625 uint32_t resume_key = 0;
626 int i;
627 DATA_BLOB last_name_raw;
628 struct file_info *finfo = NULL;
629 size_t nlen, param_len;
631 min_param = (state->first ? 6 : 4);
633 status = cli_trans_recv(subreq, talloc_tos(), &recv_flags2,
634 NULL, 0, NULL,
635 &param, min_param, &num_param,
636 &data, 0, &num_data);
637 TALLOC_FREE(subreq);
638 if (!NT_STATUS_IS_OK(status)) {
640 * TODO: retry, OS/2 nofiles
642 tevent_req_nterror(req, status);
643 return;
646 if (state->first) {
647 state->ff_dir_handle = SVAL(param, 0);
648 ff_searchcount = SVAL(param, 2);
649 ff_eos = SVAL(param, 4) != 0;
650 } else {
651 ff_searchcount = SVAL(param, 0);
652 ff_eos = SVAL(param, 2) != 0;
655 old_num_finfo = talloc_array_length(state->finfo);
657 tmp = TALLOC_REALLOC_ARRAY(state, state->finfo, struct file_info,
658 old_num_finfo + ff_searchcount);
659 if (tevent_req_nomem(tmp, req)) {
660 return;
662 state->finfo = tmp;
664 p2 = p = (char *)data;
665 data_end = (char *)data + num_data;
666 last_name_raw = data_blob_null;
668 for (i=0; i<ff_searchcount; i++) {
669 if (p2 >= data_end) {
670 ff_eos = true;
671 break;
673 if ((state->info_level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO)
674 && (i == ff_searchcount-1)) {
675 /* Last entry - fixup the last offset length. */
676 SIVAL(p2, 0, PTR_DIFF((data + num_data), p2));
679 data_blob_free(&last_name_raw);
681 finfo = &state->finfo[old_num_finfo + i];
683 p2 += interpret_long_filename(
684 state->finfo, /* Stick fname to the array as such */
685 state->cli, state->info_level,
686 (char *)data, recv_flags2, p2,
687 data_end, finfo, &resume_key, &last_name_raw);
689 if (finfo->name == NULL) {
690 DEBUG(1, ("cli_list: Error: unable to parse name from "
691 "info level %d\n", state->info_level));
692 ff_eos = true;
693 break;
695 if (!state->first && (state->mask[0] != '\0') &&
696 strcsequal(finfo->name, state->mask)) {
697 DEBUG(1, ("Error: Looping in FIND_NEXT as name %s has "
698 "already been seen?\n", finfo->name));
699 ff_eos = true;
700 break;
704 if (ff_searchcount == 0) {
705 ff_eos = true;
708 TALLOC_FREE(param);
709 TALLOC_FREE(data);
712 * Shrink state->finfo to the real length we received
714 tmp = TALLOC_REALLOC_ARRAY(state, state->finfo, struct file_info,
715 old_num_finfo + i);
716 if (tevent_req_nomem(tmp, req)) {
717 return;
719 state->finfo = tmp;
721 state->first = false;
723 if (ff_eos) {
724 data_blob_free(&last_name_raw);
725 tevent_req_done(req);
726 return;
729 TALLOC_FREE(state->mask);
730 state->mask = talloc_strdup(state, finfo->name);
731 if (tevent_req_nomem(state->mask, req)) {
732 return;
735 state->setup[0] = TRANSACT2_FINDNEXT;
737 nlen = 2*(strlen(state->mask) + 1);
739 param = TALLOC_REALLOC_ARRAY(state, state->param, uint8_t,
740 12 + nlen + last_name_raw.length + 2);
741 if (tevent_req_nomem(param, req)) {
742 return;
744 state->param = param;
746 SSVAL(param, 0, state->ff_dir_handle);
747 SSVAL(param, 2, state->max_matches); /* max count */
748 SSVAL(param, 4, state->info_level);
750 * For W2K servers serving out FAT filesystems we *must* set
751 * the resume key. If it's not FAT then it's returned as zero.
753 SIVAL(param, 6, resume_key); /* ff_resume_key */
755 * NB. *DON'T* use continue here. If you do it seems that W2K
756 * and bretheren can miss filenames. Use last filename
757 * continue instead. JRA
759 SSVAL(param, 10, (FLAG_TRANS2_FIND_REQUIRE_RESUME
760 |FLAG_TRANS2_FIND_CLOSE_IF_END));
761 p = ((char *)param)+12;
762 if (last_name_raw.length) {
763 memcpy(p, last_name_raw.data, last_name_raw.length);
764 p += last_name_raw.length;
765 data_blob_free(&last_name_raw);
766 } else {
767 p += clistr_push(state->cli, p, state->mask, nlen,
768 STR_TERMINATE);
771 param_len = PTR_DIFF(p, param);
773 subreq = cli_trans_send(state, state->ev, state->cli,
774 SMBtrans2, NULL, -1, 0, 0,
775 state->setup, 1, 0,
776 state->param, param_len, 10,
777 NULL, 0, state->cli->max_xmit);
778 if (tevent_req_nomem(subreq, req)) {
779 return;
781 tevent_req_set_callback(subreq, cli_list_trans_done, req);
784 static NTSTATUS cli_list_trans_recv(struct tevent_req *req,
785 TALLOC_CTX *mem_ctx,
786 struct file_info **finfo)
788 struct cli_list_trans_state *state = tevent_req_data(
789 req, struct cli_list_trans_state);
790 NTSTATUS status;
792 if (tevent_req_is_nterror(req, &status)) {
793 return status;
795 *finfo = talloc_move(mem_ctx, &state->finfo);
796 return NT_STATUS_OK;
799 NTSTATUS cli_list_trans(struct cli_state *cli, const char *mask,
800 uint16_t attribute, int info_level,
801 NTSTATUS (*fn)(const char *mnt, struct file_info *finfo,
802 const char *mask, void *private_data),
803 void *private_data)
805 TALLOC_CTX *frame = talloc_stackframe();
806 struct event_context *ev;
807 struct tevent_req *req;
808 int i, num_finfo;
809 struct file_info *finfo = NULL;
810 NTSTATUS status = NT_STATUS_NO_MEMORY;
812 if (cli_has_async_calls(cli)) {
814 * Can't use sync call while an async call is in flight
816 status = NT_STATUS_INVALID_PARAMETER;
817 goto fail;
819 ev = event_context_init(frame);
820 if (ev == NULL) {
821 goto fail;
823 req = cli_list_trans_send(frame, ev, cli, mask, attribute, info_level);
824 if (req == NULL) {
825 goto fail;
827 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
828 goto fail;
830 status = cli_list_trans_recv(req, frame, &finfo);
831 if (!NT_STATUS_IS_OK(status)) {
832 goto fail;
834 num_finfo = talloc_array_length(finfo);
835 for (i=0; i<num_finfo; i++) {
836 status = fn(cli->dfs_mountpoint, &finfo[i], mask, private_data);
837 if (!NT_STATUS_IS_OK(status)) {
838 goto fail;
841 fail:
842 TALLOC_FREE(frame);
843 if (!NT_STATUS_IS_OK(status)) {
844 cli_set_error(cli, status);
846 return status;
849 struct cli_list_state {
850 NTSTATUS (*recv_fn)(struct tevent_req *req, TALLOC_CTX *mem_ctx,
851 struct file_info **finfo);
852 struct file_info *finfo;
855 static void cli_list_done(struct tevent_req *subreq);
857 struct tevent_req *cli_list_send(TALLOC_CTX *mem_ctx,
858 struct tevent_context *ev,
859 struct cli_state *cli,
860 const char *mask,
861 uint16_t attribute,
862 uint16_t info_level)
864 struct tevent_req *req, *subreq;
865 struct cli_list_state *state;
867 req = tevent_req_create(mem_ctx, &state, struct cli_list_state);
868 if (req == NULL) {
869 return NULL;
872 if (cli->protocol <= PROTOCOL_LANMAN1) {
873 subreq = cli_list_old_send(state, ev, cli, mask, attribute);
874 state->recv_fn = cli_list_old_recv;
875 } else {
876 subreq = cli_list_trans_send(state, ev, cli, mask, attribute,
877 info_level);
878 state->recv_fn = cli_list_trans_recv;
880 if (tevent_req_nomem(subreq, req)) {
881 return tevent_req_post(req, ev);
883 tevent_req_set_callback(subreq, cli_list_done, req);
884 return req;
887 static void cli_list_done(struct tevent_req *subreq)
889 struct tevent_req *req = tevent_req_callback_data(
890 subreq, struct tevent_req);
891 struct cli_list_state *state = tevent_req_data(
892 req, struct cli_list_state);
893 NTSTATUS status;
895 status = state->recv_fn(subreq, state, &state->finfo);
896 TALLOC_FREE(subreq);
897 if (!NT_STATUS_IS_OK(status)) {
898 tevent_req_nterror(req, status);
899 return;
901 tevent_req_done(req);
904 NTSTATUS cli_list_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
905 struct file_info **finfo, size_t *num_finfo)
907 struct cli_list_state *state = tevent_req_data(
908 req, struct cli_list_state);
909 NTSTATUS status;
911 if (tevent_req_is_nterror(req, &status)) {
912 return status;
914 *num_finfo = talloc_array_length(state->finfo);
915 *finfo = talloc_move(mem_ctx, &state->finfo);
916 return NT_STATUS_OK;
919 NTSTATUS cli_list(struct cli_state *cli, const char *mask, uint16 attribute,
920 NTSTATUS (*fn)(const char *, struct file_info *, const char *,
921 void *), void *state)
923 TALLOC_CTX *frame = talloc_stackframe();
924 struct event_context *ev;
925 struct tevent_req *req;
926 NTSTATUS status = NT_STATUS_NO_MEMORY;
927 struct file_info *finfo;
928 size_t i, num_finfo;
929 uint16_t info_level;
931 if (cli_has_async_calls(cli)) {
933 * Can't use sync call while an async call is in flight
935 status = NT_STATUS_INVALID_PARAMETER;
936 goto fail;
938 ev = event_context_init(frame);
939 if (ev == NULL) {
940 goto fail;
943 info_level = (cli->capabilities & CAP_NT_SMBS)
944 ? SMB_FIND_FILE_BOTH_DIRECTORY_INFO : SMB_FIND_INFO_STANDARD;
946 req = cli_list_send(frame, ev, cli, mask, attribute, info_level);
947 if (req == NULL) {
948 goto fail;
950 if (!tevent_req_poll(req, ev)) {
951 status = map_nt_error_from_unix(errno);
952 goto fail;
955 status = cli_list_recv(req, frame, &finfo, &num_finfo);
956 if (!NT_STATUS_IS_OK(status)) {
957 goto fail;
960 for (i=0; i<num_finfo; i++) {
961 status = fn(cli->dfs_mountpoint, &finfo[i], mask, state);
962 if (!NT_STATUS_IS_OK(status)) {
963 goto fail;
966 fail:
967 TALLOC_FREE(frame);
968 if (!NT_STATUS_IS_OK(status)) {
969 cli_set_error(cli, status);
971 return status;