s3:libsmb/cli*: make use of cli_state_protocol()
[Samba/gebeck_regimport.git] / source3 / libsmb / clilist.c
blobba3dd2b2b5ea79e968f839906c811eecca7128bf
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 "libsmb/libsmb.h"
22 #include "../lib/util/tevent_ntstatus.h"
23 #include "async_smb.h"
24 #include "trans2.h"
26 /****************************************************************************
27 Calculate a safe next_entry_offset.
28 ****************************************************************************/
30 static size_t calc_next_entry_offset(const char *base, const char *pdata_end)
32 size_t next_entry_offset = (size_t)IVAL(base,0);
34 if (next_entry_offset == 0 ||
35 base + next_entry_offset < base ||
36 base + next_entry_offset > pdata_end) {
37 next_entry_offset = pdata_end - base;
39 return next_entry_offset;
42 /****************************************************************************
43 Interpret a long filename structure - this is mostly guesses at the moment.
44 The length of the structure is returned
45 The structure of a long filename depends on the info level.
46 SMB_FIND_FILE_BOTH_DIRECTORY_INFO is used
47 by NT and SMB_FIND_EA_SIZE is used by OS/2
48 ****************************************************************************/
50 static size_t interpret_long_filename(TALLOC_CTX *ctx,
51 struct cli_state *cli,
52 int level,
53 const char *base_ptr,
54 uint16_t recv_flags2,
55 const char *p,
56 const char *pdata_end,
57 struct file_info *finfo,
58 uint32 *p_resume_key,
59 DATA_BLOB *p_last_name_raw)
61 int len;
62 size_t ret;
63 const char *base = p;
65 data_blob_free(p_last_name_raw);
67 if (p_resume_key) {
68 *p_resume_key = 0;
70 ZERO_STRUCTP(finfo);
72 switch (level) {
73 case SMB_FIND_INFO_STANDARD: /* OS/2 understands this */
74 /* these dates are converted to GMT by
75 make_unix_date */
76 if (pdata_end - base < 27) {
77 return pdata_end - base;
79 finfo->ctime_ts = convert_time_t_to_timespec(
80 make_unix_date2(p+4, cli->serverzone));
81 finfo->atime_ts = convert_time_t_to_timespec(
82 make_unix_date2(p+8, cli->serverzone));
83 finfo->mtime_ts = convert_time_t_to_timespec(
84 make_unix_date2(p+12, cli->serverzone));
85 finfo->size = IVAL(p,16);
86 finfo->mode = CVAL(p,24);
87 len = CVAL(p, 26);
88 p += 27;
89 p += align_string(base_ptr, p, 0);
91 /* We can safely use len here (which is required by OS/2)
92 * and the NAS-BASIC server instead of +2 or +1 as the
93 * STR_TERMINATE flag below is
94 * actually used as the length calculation.
95 * The len is merely an upper bound.
96 * Due to the explicit 2 byte null termination
97 * in cli_receive_trans/cli_receive_nt_trans
98 * we know this is safe. JRA + kukks
101 if (p + len > pdata_end) {
102 return pdata_end - base;
105 /* the len+2 below looks strange but it is
106 important to cope with the differences
107 between win2000 and win9x for this call
108 (tridge) */
109 ret = clistr_pull_talloc(ctx,
110 base_ptr,
111 recv_flags2,
112 &finfo->name,
114 len+2,
115 STR_TERMINATE);
116 if (ret == (size_t)-1) {
117 return pdata_end - base;
119 p += ret;
120 return PTR_DIFF(p, base);
122 case SMB_FIND_EA_SIZE: /* this is what OS/2 uses mostly */
123 /* these dates are converted to GMT by
124 make_unix_date */
125 if (pdata_end - base < 31) {
126 return pdata_end - base;
128 finfo->ctime_ts = convert_time_t_to_timespec(
129 make_unix_date2(p+4, cli->serverzone));
130 finfo->atime_ts = convert_time_t_to_timespec(
131 make_unix_date2(p+8, cli->serverzone));
132 finfo->mtime_ts = convert_time_t_to_timespec(
133 make_unix_date2(p+12, cli->serverzone));
134 finfo->size = IVAL(p,16);
135 finfo->mode = CVAL(p,24);
136 len = CVAL(p, 30);
137 p += 31;
138 /* check for unisys! */
139 if (p + len + 1 > pdata_end) {
140 return pdata_end - base;
142 ret = clistr_pull_talloc(ctx,
143 base_ptr,
144 recv_flags2,
145 &finfo->name,
147 len,
148 STR_NOALIGN);
149 if (ret == (size_t)-1) {
150 return pdata_end - base;
152 p += ret;
153 return PTR_DIFF(p, base) + 1;
155 case SMB_FIND_FILE_BOTH_DIRECTORY_INFO: /* NT uses this, but also accepts 2 */
157 size_t namelen, slen;
159 if (pdata_end - base < 94) {
160 return pdata_end - base;
163 p += 4; /* next entry offset */
165 if (p_resume_key) {
166 *p_resume_key = IVAL(p,0);
168 p += 4; /* fileindex */
170 /* Offset zero is "create time", not "change time". */
171 p += 8;
172 finfo->atime_ts = interpret_long_date(p);
173 p += 8;
174 finfo->mtime_ts = interpret_long_date(p);
175 p += 8;
176 finfo->ctime_ts = interpret_long_date(p);
177 p += 8;
178 finfo->size = IVAL2_TO_SMB_BIG_UINT(p,0);
179 p += 8;
180 p += 8; /* alloc size */
181 finfo->mode = CVAL(p,0);
182 p += 4;
183 namelen = IVAL(p,0);
184 p += 4;
185 p += 4; /* EA size */
186 slen = SVAL(p, 0);
187 if (slen > 24) {
188 /* Bad short name length. */
189 return pdata_end - base;
191 p += 2;
192 ret = clistr_pull_talloc(ctx,
193 base_ptr,
194 recv_flags2,
195 &finfo->short_name,
197 slen,
198 STR_UNICODE);
199 if (ret == (size_t)-1) {
200 return pdata_end - base;
202 p += 24; /* short name? */
203 if (p + namelen < p || p + namelen > pdata_end) {
204 return pdata_end - base;
206 ret = clistr_pull_talloc(ctx,
207 base_ptr,
208 recv_flags2,
209 &finfo->name,
211 namelen,
213 if (ret == (size_t)-1) {
214 return pdata_end - base;
217 /* To be robust in the face of unicode conversion failures
218 we need to copy the raw bytes of the last name seen here.
219 Namelen doesn't include the terminating unicode null, so
220 copy it here. */
222 if (p_last_name_raw) {
223 *p_last_name_raw = data_blob(NULL, namelen+2);
224 memcpy(p_last_name_raw->data, p, namelen);
225 SSVAL(p_last_name_raw->data, namelen, 0);
227 return calc_next_entry_offset(base, pdata_end);
231 DEBUG(1,("Unknown long filename format %d\n",level));
232 return calc_next_entry_offset(base, pdata_end);
235 /****************************************************************************
236 Interpret a short filename structure.
237 The length of the structure is returned.
238 ****************************************************************************/
240 static bool interpret_short_filename(TALLOC_CTX *ctx,
241 struct cli_state *cli,
242 char *p,
243 struct file_info *finfo)
245 size_t ret;
246 ZERO_STRUCTP(finfo);
248 finfo->mode = CVAL(p,21);
250 /* this date is converted to GMT by make_unix_date */
251 finfo->ctime_ts.tv_sec = make_unix_date(p+22, cli->serverzone);
252 finfo->ctime_ts.tv_nsec = 0;
253 finfo->mtime_ts.tv_sec = finfo->atime_ts.tv_sec = finfo->ctime_ts.tv_sec;
254 finfo->mtime_ts.tv_nsec = finfo->atime_ts.tv_nsec = 0;
255 finfo->size = IVAL(p,26);
256 ret = clistr_pull_talloc(ctx,
257 NULL,
259 &finfo->name,
260 p+30,
262 STR_ASCII);
263 if (ret == (size_t)-1) {
264 return false;
267 if (finfo->name) {
268 finfo->short_name = talloc_strdup(ctx, finfo->name);
269 if (finfo->short_name == NULL) {
270 return false;
273 return true;
276 struct cli_list_old_state {
277 struct tevent_context *ev;
278 struct cli_state *cli;
279 uint16_t vwv[2];
280 char *mask;
281 int num_asked;
282 uint16_t attribute;
283 uint8_t search_status[23];
284 bool first;
285 bool done;
286 uint8_t *dirlist;
289 static void cli_list_old_done(struct tevent_req *subreq);
291 static struct tevent_req *cli_list_old_send(TALLOC_CTX *mem_ctx,
292 struct tevent_context *ev,
293 struct cli_state *cli,
294 const char *mask,
295 uint16_t attribute)
297 struct tevent_req *req, *subreq;
298 struct cli_list_old_state *state;
299 uint8_t *bytes;
300 static const uint16_t zero = 0;
302 req = tevent_req_create(mem_ctx, &state, struct cli_list_old_state);
303 if (req == NULL) {
304 return NULL;
306 state->ev = ev;
307 state->cli = cli;
308 state->attribute = attribute;
309 state->first = true;
310 state->mask = talloc_strdup(state, mask);
311 if (tevent_req_nomem(state->mask, req)) {
312 return tevent_req_post(req, ev);
314 state->num_asked = (cli->max_xmit - 100) / DIR_STRUCT_SIZE;
316 SSVAL(state->vwv + 0, 0, state->num_asked);
317 SSVAL(state->vwv + 1, 0, state->attribute);
319 bytes = talloc_array(state, uint8_t, 1);
320 if (tevent_req_nomem(bytes, req)) {
321 return tevent_req_post(req, ev);
323 bytes[0] = 4;
324 bytes = smb_bytes_push_str(bytes, cli_ucs2(cli), mask,
325 strlen(mask)+1, NULL);
327 bytes = smb_bytes_push_bytes(bytes, 5, (const uint8_t *)&zero, 2);
328 if (tevent_req_nomem(bytes, req)) {
329 return tevent_req_post(req, ev);
332 subreq = cli_smb_send(state, state->ev, state->cli, SMBsearch,
333 0, 2, state->vwv, talloc_get_size(bytes), bytes);
334 if (tevent_req_nomem(subreq, req)) {
335 return tevent_req_post(req, ev);
337 tevent_req_set_callback(subreq, cli_list_old_done, req);
338 return req;
341 static void cli_list_old_done(struct tevent_req *subreq)
343 struct tevent_req *req = tevent_req_callback_data(
344 subreq, struct tevent_req);
345 struct cli_list_old_state *state = tevent_req_data(
346 req, struct cli_list_old_state);
347 NTSTATUS status;
348 uint8_t cmd;
349 uint8_t wct;
350 uint16_t *vwv;
351 uint32_t num_bytes;
352 uint8_t *bytes;
353 uint16_t received;
354 size_t dirlist_len;
355 uint8_t *tmp;
357 status = cli_smb_recv(subreq, state, NULL, 0, &wct, &vwv, &num_bytes,
358 &bytes);
359 if (!NT_STATUS_IS_OK(status)
360 && !NT_STATUS_EQUAL(status, NT_STATUS_DOS(ERRDOS, ERRnofiles))
361 && !NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
362 TALLOC_FREE(subreq);
363 tevent_req_nterror(req, status);
364 return;
366 if (NT_STATUS_EQUAL(status, NT_STATUS_DOS(ERRDOS, ERRnofiles))
367 || NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
368 received = 0;
369 } else {
370 if (wct < 1) {
371 TALLOC_FREE(subreq);
372 tevent_req_nterror(
373 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
374 return;
376 received = SVAL(vwv + 0, 0);
379 if (received > 0) {
381 * I don't think this can wrap. received is
382 * initialized from a 16-bit value.
384 if (num_bytes < (received * DIR_STRUCT_SIZE + 3)) {
385 TALLOC_FREE(subreq);
386 tevent_req_nterror(
387 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
388 return;
391 dirlist_len = talloc_get_size(state->dirlist);
393 tmp = talloc_realloc(
394 state, state->dirlist, uint8_t,
395 dirlist_len + received * DIR_STRUCT_SIZE);
396 if (tevent_req_nomem(tmp, req)) {
397 return;
399 state->dirlist = tmp;
400 memcpy(state->dirlist + dirlist_len, bytes + 3,
401 received * DIR_STRUCT_SIZE);
403 SSVAL(state->search_status, 0, 21);
404 memcpy(state->search_status + 2,
405 bytes + 3 + (received-1)*DIR_STRUCT_SIZE, 21);
406 cmd = SMBsearch;
407 } else {
408 if (state->first || state->done) {
409 tevent_req_done(req);
410 return;
412 state->done = true;
413 state->num_asked = 0;
414 cmd = SMBfclose;
416 TALLOC_FREE(subreq);
418 state->first = false;
420 SSVAL(state->vwv + 0, 0, state->num_asked);
421 SSVAL(state->vwv + 1, 0, state->attribute);
423 bytes = talloc_array(state, uint8_t, 1);
424 if (tevent_req_nomem(bytes, req)) {
425 return;
427 bytes[0] = 4;
428 bytes = smb_bytes_push_str(bytes, cli_ucs2(state->cli), "",
429 1, NULL);
430 bytes = smb_bytes_push_bytes(bytes, 5, state->search_status,
431 sizeof(state->search_status));
432 if (tevent_req_nomem(bytes, req)) {
433 return;
435 subreq = cli_smb_send(state, state->ev, state->cli, cmd, 0,
436 2, state->vwv, talloc_get_size(bytes), bytes);
437 if (tevent_req_nomem(subreq, req)) {
438 return;
440 tevent_req_set_callback(subreq, cli_list_old_done, req);
443 static NTSTATUS cli_list_old_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
444 struct file_info **pfinfo)
446 struct cli_list_old_state *state = tevent_req_data(
447 req, struct cli_list_old_state);
448 NTSTATUS status;
449 size_t i, num_received;
450 struct file_info *finfo;
452 if (tevent_req_is_nterror(req, &status)) {
453 return status;
456 num_received = talloc_array_length(state->dirlist) / DIR_STRUCT_SIZE;
458 finfo = talloc_array(mem_ctx, struct file_info, num_received);
459 if (finfo == NULL) {
460 return NT_STATUS_NO_MEMORY;
463 for (i=0; i<num_received; i++) {
464 if (!interpret_short_filename(
465 finfo, state->cli,
466 (char *)state->dirlist + i * DIR_STRUCT_SIZE,
467 &finfo[i])) {
468 TALLOC_FREE(finfo);
469 return NT_STATUS_NO_MEMORY;
472 *pfinfo = finfo;
473 return NT_STATUS_OK;
476 NTSTATUS cli_list_old(struct cli_state *cli, const char *mask,
477 uint16 attribute,
478 NTSTATUS (*fn)(const char *, struct file_info *,
479 const char *, void *), void *state)
481 TALLOC_CTX *frame = talloc_stackframe();
482 struct event_context *ev;
483 struct tevent_req *req;
484 NTSTATUS status = NT_STATUS_NO_MEMORY;
485 struct file_info *finfo;
486 size_t i, num_finfo;
488 if (cli_has_async_calls(cli)) {
490 * Can't use sync call while an async call is in flight
492 status = NT_STATUS_INVALID_PARAMETER;
493 goto fail;
495 ev = event_context_init(frame);
496 if (ev == NULL) {
497 goto fail;
499 req = cli_list_old_send(frame, ev, cli, mask, attribute);
500 if (req == NULL) {
501 goto fail;
503 if (!tevent_req_poll(req, ev)) {
504 status = map_nt_error_from_unix(errno);
505 goto fail;
507 status = cli_list_old_recv(req, frame, &finfo);
508 if (!NT_STATUS_IS_OK(status)) {
509 goto fail;
511 num_finfo = talloc_array_length(finfo);
512 for (i=0; i<num_finfo; i++) {
513 status = fn(cli->dfs_mountpoint, &finfo[i], mask, state);
514 if (!NT_STATUS_IS_OK(status)) {
515 goto fail;
518 fail:
519 TALLOC_FREE(frame);
520 return status;
523 struct cli_list_trans_state {
524 struct tevent_context *ev;
525 struct cli_state *cli;
526 char *mask;
527 uint16_t attribute;
528 uint16_t info_level;
530 int loop_count;
531 int total_received;
532 uint16_t max_matches;
533 bool first;
535 int ff_eos;
536 int ff_dir_handle;
538 uint16_t setup[1];
539 uint8_t *param;
541 struct file_info *finfo;
544 static void cli_list_trans_done(struct tevent_req *subreq);
546 static struct tevent_req *cli_list_trans_send(TALLOC_CTX *mem_ctx,
547 struct tevent_context *ev,
548 struct cli_state *cli,
549 const char *mask,
550 uint16_t attribute,
551 uint16_t info_level)
553 struct tevent_req *req, *subreq;
554 struct cli_list_trans_state *state;
555 size_t param_len;
557 req = tevent_req_create(mem_ctx, &state,
558 struct cli_list_trans_state);
559 if (req == NULL) {
560 return NULL;
562 state->ev = ev;
563 state->cli = cli;
564 state->mask = talloc_strdup(state, mask);
565 if (tevent_req_nomem(state->mask, req)) {
566 return tevent_req_post(req, ev);
568 state->attribute = attribute;
569 state->info_level = info_level;
570 state->loop_count = 0;
571 state->first = true;
573 state->max_matches = 1366; /* Match W2k */
575 SSVAL(&state->setup[0], 0, TRANSACT2_FINDFIRST);
577 state->param = talloc_array(state, uint8_t, 12);
578 if (tevent_req_nomem(state->param, req)) {
579 return tevent_req_post(req, ev);
582 SSVAL(state->param, 0, state->attribute);
583 SSVAL(state->param, 2, state->max_matches);
584 SSVAL(state->param, 4,
585 FLAG_TRANS2_FIND_REQUIRE_RESUME
586 |FLAG_TRANS2_FIND_CLOSE_IF_END);
587 SSVAL(state->param, 6, state->info_level);
588 SIVAL(state->param, 8, 0);
590 state->param = trans2_bytes_push_str(state->param, cli_ucs2(cli),
591 state->mask, strlen(state->mask)+1,
592 NULL);
593 if (tevent_req_nomem(state->param, req)) {
594 return tevent_req_post(req, ev);
596 param_len = talloc_get_size(state->param);
598 subreq = cli_trans_send(state, state->ev, state->cli,
599 SMBtrans2, NULL, -1, 0, 0,
600 state->setup, 1, 0,
601 state->param, param_len, 10,
602 NULL, 0, cli->max_xmit);
603 if (tevent_req_nomem(subreq, req)) {
604 return tevent_req_post(req, ev);
606 tevent_req_set_callback(subreq, cli_list_trans_done, req);
607 return req;
610 static void cli_list_trans_done(struct tevent_req *subreq)
612 struct tevent_req *req = tevent_req_callback_data(
613 subreq, struct tevent_req);
614 struct cli_list_trans_state *state = tevent_req_data(
615 req, struct cli_list_trans_state);
616 NTSTATUS status;
617 uint8_t *param;
618 uint32_t num_param;
619 uint8_t *data;
620 char *data_end;
621 uint32_t num_data;
622 uint32_t min_param;
623 struct file_info *tmp;
624 size_t old_num_finfo;
625 uint16_t recv_flags2;
626 int ff_searchcount;
627 bool ff_eos;
628 char *p, *p2;
629 uint32_t resume_key = 0;
630 int i;
631 DATA_BLOB last_name_raw;
632 struct file_info *finfo = NULL;
633 size_t param_len;
635 min_param = (state->first ? 6 : 4);
637 status = cli_trans_recv(subreq, talloc_tos(), &recv_flags2,
638 NULL, 0, NULL,
639 &param, min_param, &num_param,
640 &data, 0, &num_data);
641 TALLOC_FREE(subreq);
642 if (!NT_STATUS_IS_OK(status)) {
644 * TODO: retry, OS/2 nofiles
646 tevent_req_nterror(req, status);
647 return;
650 if (state->first) {
651 state->ff_dir_handle = SVAL(param, 0);
652 ff_searchcount = SVAL(param, 2);
653 ff_eos = SVAL(param, 4) != 0;
654 } else {
655 ff_searchcount = SVAL(param, 0);
656 ff_eos = SVAL(param, 2) != 0;
659 old_num_finfo = talloc_array_length(state->finfo);
661 tmp = talloc_realloc(state, state->finfo, struct file_info,
662 old_num_finfo + ff_searchcount);
663 if (tevent_req_nomem(tmp, req)) {
664 return;
666 state->finfo = tmp;
668 p2 = p = (char *)data;
669 data_end = (char *)data + num_data;
670 last_name_raw = data_blob_null;
672 for (i=0; i<ff_searchcount; i++) {
673 if (p2 >= data_end) {
674 ff_eos = true;
675 break;
677 if ((state->info_level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO)
678 && (i == ff_searchcount-1)) {
679 /* Last entry - fixup the last offset length. */
680 SIVAL(p2, 0, PTR_DIFF((data + num_data), p2));
683 data_blob_free(&last_name_raw);
685 finfo = &state->finfo[old_num_finfo + i];
687 p2 += interpret_long_filename(
688 state->finfo, /* Stick fname to the array as such */
689 state->cli, state->info_level,
690 (char *)data, recv_flags2, p2,
691 data_end, finfo, &resume_key, &last_name_raw);
693 if (finfo->name == NULL) {
694 DEBUG(1, ("cli_list: Error: unable to parse name from "
695 "info level %d\n", state->info_level));
696 ff_eos = true;
697 break;
699 if (!state->first && (state->mask[0] != '\0') &&
700 strcsequal(finfo->name, state->mask)) {
701 DEBUG(1, ("Error: Looping in FIND_NEXT as name %s has "
702 "already been seen?\n", finfo->name));
703 ff_eos = true;
704 break;
708 if (ff_searchcount == 0) {
709 ff_eos = true;
712 TALLOC_FREE(param);
713 TALLOC_FREE(data);
716 * Shrink state->finfo to the real length we received
718 tmp = talloc_realloc(state, state->finfo, struct file_info,
719 old_num_finfo + i);
720 if (tevent_req_nomem(tmp, req)) {
721 return;
723 state->finfo = tmp;
725 state->first = false;
727 if (ff_eos) {
728 data_blob_free(&last_name_raw);
729 tevent_req_done(req);
730 return;
733 TALLOC_FREE(state->mask);
734 state->mask = talloc_strdup(state, finfo->name);
735 if (tevent_req_nomem(state->mask, req)) {
736 return;
739 SSVAL(&state->setup[0], 0, TRANSACT2_FINDNEXT);
741 param = talloc_realloc(state, state->param, uint8_t, 12);
742 if (tevent_req_nomem(param, req)) {
743 return;
745 state->param = param;
747 SSVAL(param, 0, state->ff_dir_handle);
748 SSVAL(param, 2, state->max_matches); /* max count */
749 SSVAL(param, 4, state->info_level);
751 * For W2K servers serving out FAT filesystems we *must* set
752 * the resume key. If it's not FAT then it's returned as zero.
754 SIVAL(param, 6, resume_key); /* ff_resume_key */
756 * NB. *DON'T* use continue here. If you do it seems that W2K
757 * and bretheren can miss filenames. Use last filename
758 * continue instead. JRA
760 SSVAL(param, 10, (FLAG_TRANS2_FIND_REQUIRE_RESUME
761 |FLAG_TRANS2_FIND_CLOSE_IF_END));
762 if (last_name_raw.length) {
763 state->param = trans2_bytes_push_bytes(state->param,
764 last_name_raw.data,
765 last_name_raw.length);
766 if (tevent_req_nomem(state->param, req)) {
767 return;
769 data_blob_free(&last_name_raw);
770 } else {
771 state->param = trans2_bytes_push_str(state->param,
772 cli_ucs2(state->cli),
773 state->mask,
774 strlen(state->mask)+1,
775 NULL);
776 if (tevent_req_nomem(state->param, req)) {
777 return;
780 param_len = talloc_get_size(state->param);
782 subreq = cli_trans_send(state, state->ev, state->cli,
783 SMBtrans2, NULL, -1, 0, 0,
784 state->setup, 1, 0,
785 state->param, param_len, 10,
786 NULL, 0, state->cli->max_xmit);
787 if (tevent_req_nomem(subreq, req)) {
788 return;
790 tevent_req_set_callback(subreq, cli_list_trans_done, req);
793 static NTSTATUS cli_list_trans_recv(struct tevent_req *req,
794 TALLOC_CTX *mem_ctx,
795 struct file_info **finfo)
797 struct cli_list_trans_state *state = tevent_req_data(
798 req, struct cli_list_trans_state);
799 NTSTATUS status;
801 if (tevent_req_is_nterror(req, &status)) {
802 return status;
804 *finfo = talloc_move(mem_ctx, &state->finfo);
805 return NT_STATUS_OK;
808 NTSTATUS cli_list_trans(struct cli_state *cli, const char *mask,
809 uint16_t attribute, int info_level,
810 NTSTATUS (*fn)(const char *mnt, struct file_info *finfo,
811 const char *mask, void *private_data),
812 void *private_data)
814 TALLOC_CTX *frame = talloc_stackframe();
815 struct event_context *ev;
816 struct tevent_req *req;
817 int i, num_finfo;
818 struct file_info *finfo = NULL;
819 NTSTATUS status = NT_STATUS_NO_MEMORY;
821 if (cli_has_async_calls(cli)) {
823 * Can't use sync call while an async call is in flight
825 status = NT_STATUS_INVALID_PARAMETER;
826 goto fail;
828 ev = event_context_init(frame);
829 if (ev == NULL) {
830 goto fail;
832 req = cli_list_trans_send(frame, ev, cli, mask, attribute, info_level);
833 if (req == NULL) {
834 goto fail;
836 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
837 goto fail;
839 status = cli_list_trans_recv(req, frame, &finfo);
840 if (!NT_STATUS_IS_OK(status)) {
841 goto fail;
843 num_finfo = talloc_array_length(finfo);
844 for (i=0; i<num_finfo; i++) {
845 status = fn(cli->dfs_mountpoint, &finfo[i], mask, private_data);
846 if (!NT_STATUS_IS_OK(status)) {
847 goto fail;
850 fail:
851 TALLOC_FREE(frame);
852 return status;
855 struct cli_list_state {
856 NTSTATUS (*recv_fn)(struct tevent_req *req, TALLOC_CTX *mem_ctx,
857 struct file_info **finfo);
858 struct file_info *finfo;
861 static void cli_list_done(struct tevent_req *subreq);
863 struct tevent_req *cli_list_send(TALLOC_CTX *mem_ctx,
864 struct tevent_context *ev,
865 struct cli_state *cli,
866 const char *mask,
867 uint16_t attribute,
868 uint16_t info_level)
870 struct tevent_req *req, *subreq;
871 struct cli_list_state *state;
873 req = tevent_req_create(mem_ctx, &state, struct cli_list_state);
874 if (req == NULL) {
875 return NULL;
878 if (cli_state_protocol(cli) <= PROTOCOL_LANMAN1) {
879 subreq = cli_list_old_send(state, ev, cli, mask, attribute);
880 state->recv_fn = cli_list_old_recv;
881 } else {
882 subreq = cli_list_trans_send(state, ev, cli, mask, attribute,
883 info_level);
884 state->recv_fn = cli_list_trans_recv;
886 if (tevent_req_nomem(subreq, req)) {
887 return tevent_req_post(req, ev);
889 tevent_req_set_callback(subreq, cli_list_done, req);
890 return req;
893 static void cli_list_done(struct tevent_req *subreq)
895 struct tevent_req *req = tevent_req_callback_data(
896 subreq, struct tevent_req);
897 struct cli_list_state *state = tevent_req_data(
898 req, struct cli_list_state);
899 NTSTATUS status;
901 status = state->recv_fn(subreq, state, &state->finfo);
902 TALLOC_FREE(subreq);
903 if (!NT_STATUS_IS_OK(status)) {
904 tevent_req_nterror(req, status);
905 return;
907 tevent_req_done(req);
910 NTSTATUS cli_list_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
911 struct file_info **finfo, size_t *num_finfo)
913 struct cli_list_state *state = tevent_req_data(
914 req, struct cli_list_state);
915 NTSTATUS status;
917 if (tevent_req_is_nterror(req, &status)) {
918 return status;
920 *num_finfo = talloc_array_length(state->finfo);
921 *finfo = talloc_move(mem_ctx, &state->finfo);
922 return NT_STATUS_OK;
925 NTSTATUS cli_list(struct cli_state *cli, const char *mask, uint16 attribute,
926 NTSTATUS (*fn)(const char *, struct file_info *, const char *,
927 void *), void *state)
929 TALLOC_CTX *frame = talloc_stackframe();
930 struct event_context *ev;
931 struct tevent_req *req;
932 NTSTATUS status = NT_STATUS_NO_MEMORY;
933 struct file_info *finfo;
934 size_t i, num_finfo;
935 uint16_t info_level;
937 if (cli_has_async_calls(cli)) {
939 * Can't use sync call while an async call is in flight
941 status = NT_STATUS_INVALID_PARAMETER;
942 goto fail;
944 ev = event_context_init(frame);
945 if (ev == NULL) {
946 goto fail;
949 info_level = (cli_state_capabilities(cli) & CAP_NT_SMBS)
950 ? SMB_FIND_FILE_BOTH_DIRECTORY_INFO : SMB_FIND_INFO_STANDARD;
952 req = cli_list_send(frame, ev, cli, mask, attribute, info_level);
953 if (req == NULL) {
954 goto fail;
956 if (!tevent_req_poll(req, ev)) {
957 status = map_nt_error_from_unix(errno);
958 goto fail;
961 status = cli_list_recv(req, frame, &finfo, &num_finfo);
962 if (!NT_STATUS_IS_OK(status)) {
963 goto fail;
966 for (i=0; i<num_finfo; i++) {
967 status = fn(cli->dfs_mountpoint, &finfo[i], mask, state);
968 if (!NT_STATUS_IS_OK(status)) {
969 goto fail;
972 fail:
973 TALLOC_FREE(frame);
974 return status;