This reverts commit 378c4b221a6be75e1d32cb6fb3a773ce5de6dbda.
[Samba.git] / source3 / libsmb / clilist.c
blob552de0024283744a05fa04efb94e9d72200b7a77
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"
22 #include "trans2.h"
24 /****************************************************************************
25 Calculate a safe next_entry_offset.
26 ****************************************************************************/
28 static size_t calc_next_entry_offset(const char *base, const char *pdata_end)
30 size_t next_entry_offset = (size_t)IVAL(base,0);
32 if (next_entry_offset == 0 ||
33 base + next_entry_offset < base ||
34 base + next_entry_offset > pdata_end) {
35 next_entry_offset = pdata_end - base;
37 return next_entry_offset;
40 /****************************************************************************
41 Interpret a long filename structure - this is mostly guesses at the moment.
42 The length of the structure is returned
43 The structure of a long filename depends on the info level.
44 SMB_FIND_FILE_BOTH_DIRECTORY_INFO is used
45 by NT and SMB_FIND_EA_SIZE is used by OS/2
46 ****************************************************************************/
48 static size_t interpret_long_filename(TALLOC_CTX *ctx,
49 struct cli_state *cli,
50 int level,
51 const char *base_ptr,
52 uint16_t recv_flags2,
53 const char *p,
54 const char *pdata_end,
55 struct file_info *finfo,
56 uint32 *p_resume_key,
57 DATA_BLOB *p_last_name_raw)
59 int len;
60 size_t ret;
61 const char *base = p;
63 data_blob_free(p_last_name_raw);
65 if (p_resume_key) {
66 *p_resume_key = 0;
68 ZERO_STRUCTP(finfo);
70 switch (level) {
71 case SMB_FIND_INFO_STANDARD: /* OS/2 understands this */
72 /* these dates are converted to GMT by
73 make_unix_date */
74 if (pdata_end - base < 27) {
75 return pdata_end - base;
77 finfo->ctime_ts = convert_time_t_to_timespec(
78 make_unix_date2(p+4, cli->serverzone));
79 finfo->atime_ts = convert_time_t_to_timespec(
80 make_unix_date2(p+8, cli->serverzone));
81 finfo->mtime_ts = convert_time_t_to_timespec(
82 make_unix_date2(p+12, cli->serverzone));
83 finfo->size = IVAL(p,16);
84 finfo->mode = CVAL(p,24);
85 len = CVAL(p, 26);
86 p += 27;
87 p += align_string(base_ptr, p, 0);
89 /* We can safely use len here (which is required by OS/2)
90 * and the NAS-BASIC server instead of +2 or +1 as the
91 * STR_TERMINATE flag below is
92 * actually used as the length calculation.
93 * The len is merely an upper bound.
94 * Due to the explicit 2 byte null termination
95 * in cli_receive_trans/cli_receive_nt_trans
96 * we know this is safe. JRA + kukks
99 if (p + len > pdata_end) {
100 return pdata_end - base;
103 /* the len+2 below looks strange but it is
104 important to cope with the differences
105 between win2000 and win9x for this call
106 (tridge) */
107 ret = clistr_pull_talloc(ctx,
108 base_ptr,
109 recv_flags2,
110 &finfo->name,
112 len+2,
113 STR_TERMINATE);
114 if (ret == (size_t)-1) {
115 return pdata_end - base;
117 p += ret;
118 return PTR_DIFF(p, base);
120 case SMB_FIND_EA_SIZE: /* this is what OS/2 uses mostly */
121 /* these dates are converted to GMT by
122 make_unix_date */
123 if (pdata_end - base < 31) {
124 return pdata_end - base;
126 finfo->ctime_ts = convert_time_t_to_timespec(
127 make_unix_date2(p+4, cli->serverzone));
128 finfo->atime_ts = convert_time_t_to_timespec(
129 make_unix_date2(p+8, cli->serverzone));
130 finfo->mtime_ts = convert_time_t_to_timespec(
131 make_unix_date2(p+12, cli->serverzone));
132 finfo->size = IVAL(p,16);
133 finfo->mode = CVAL(p,24);
134 len = CVAL(p, 30);
135 p += 31;
136 /* check for unisys! */
137 if (p + len + 1 > pdata_end) {
138 return pdata_end - base;
140 ret = clistr_pull_talloc(ctx,
141 base_ptr,
142 recv_flags2,
143 &finfo->name,
145 len,
146 STR_NOALIGN);
147 if (ret == (size_t)-1) {
148 return pdata_end - base;
150 p += ret;
151 return PTR_DIFF(p, base) + 1;
153 case SMB_FIND_FILE_BOTH_DIRECTORY_INFO: /* NT uses this, but also accepts 2 */
155 size_t namelen, slen;
157 if (pdata_end - base < 94) {
158 return pdata_end - base;
161 p += 4; /* next entry offset */
163 if (p_resume_key) {
164 *p_resume_key = IVAL(p,0);
166 p += 4; /* fileindex */
168 /* Offset zero is "create time", not "change time". */
169 p += 8;
170 finfo->atime_ts = interpret_long_date(p);
171 p += 8;
172 finfo->mtime_ts = interpret_long_date(p);
173 p += 8;
174 finfo->ctime_ts = interpret_long_date(p);
175 p += 8;
176 finfo->size = IVAL2_TO_SMB_BIG_UINT(p,0);
177 p += 8;
178 p += 8; /* alloc size */
179 finfo->mode = CVAL(p,0);
180 p += 4;
181 namelen = IVAL(p,0);
182 p += 4;
183 p += 4; /* EA size */
184 slen = SVAL(p, 0);
185 if (slen > 24) {
186 /* Bad short name length. */
187 return pdata_end - base;
189 p += 2;
191 /* stupid NT bugs. grr */
192 int flags = 0;
193 if (p[1] == 0 && namelen > 1) flags |= STR_UNICODE;
194 clistr_pull(base_ptr, finfo->short_name, p,
195 sizeof(finfo->short_name),
196 slen, flags);
198 p += 24; /* short name? */
199 if (p + namelen < p || p + namelen > pdata_end) {
200 return pdata_end - base;
202 ret = clistr_pull_talloc(ctx,
203 base_ptr,
204 recv_flags2,
205 &finfo->name,
207 namelen,
209 if (ret == (size_t)-1) {
210 return pdata_end - base;
213 /* To be robust in the face of unicode conversion failures
214 we need to copy the raw bytes of the last name seen here.
215 Namelen doesn't include the terminating unicode null, so
216 copy it here. */
218 if (p_last_name_raw) {
219 *p_last_name_raw = data_blob(NULL, namelen+2);
220 memcpy(p_last_name_raw->data, p, namelen);
221 SSVAL(p_last_name_raw->data, namelen, 0);
223 return calc_next_entry_offset(base, pdata_end);
227 DEBUG(1,("Unknown long filename format %d\n",level));
228 return calc_next_entry_offset(base, pdata_end);
231 /****************************************************************************
232 Interpret a short filename structure.
233 The length of the structure is returned.
234 ****************************************************************************/
236 static bool interpret_short_filename(TALLOC_CTX *ctx,
237 struct cli_state *cli,
238 char *p,
239 struct file_info *finfo)
241 size_t ret;
242 ZERO_STRUCTP(finfo);
244 finfo->mode = CVAL(p,21);
246 /* this date is converted to GMT by make_unix_date */
247 finfo->ctime_ts.tv_sec = make_unix_date(p+22, cli->serverzone);
248 finfo->ctime_ts.tv_nsec = 0;
249 finfo->mtime_ts.tv_sec = finfo->atime_ts.tv_sec = finfo->ctime_ts.tv_sec;
250 finfo->mtime_ts.tv_nsec = finfo->atime_ts.tv_nsec = 0;
251 finfo->size = IVAL(p,26);
252 ret = clistr_pull_talloc(ctx,
253 cli->inbuf,
254 SVAL(cli->inbuf, smb_flg2),
255 &finfo->name,
256 p+30,
258 STR_ASCII);
259 if (ret == (size_t)-1) {
260 return false;
263 if (finfo->name) {
264 strlcpy(finfo->short_name,
265 finfo->name,
266 sizeof(finfo->short_name));
268 return true;
271 struct cli_list_old_state {
272 struct tevent_context *ev;
273 struct cli_state *cli;
274 uint16_t vwv[2];
275 char *mask;
276 int num_asked;
277 uint16_t attribute;
278 uint8_t search_status[23];
279 bool first;
280 bool done;
281 uint8_t *dirlist;
284 static void cli_list_old_done(struct tevent_req *subreq);
286 static struct tevent_req *cli_list_old_send(TALLOC_CTX *mem_ctx,
287 struct tevent_context *ev,
288 struct cli_state *cli,
289 const char *mask,
290 uint16_t attribute)
292 struct tevent_req *req, *subreq;
293 struct cli_list_old_state *state;
294 uint8_t *bytes;
295 static const uint16_t zero = 0;
297 req = tevent_req_create(mem_ctx, &state, struct cli_list_old_state);
298 if (req == NULL) {
299 return NULL;
301 state->ev = ev;
302 state->cli = cli;
303 state->attribute = attribute;
304 state->first = true;
305 state->mask = talloc_strdup(state, mask);
306 if (tevent_req_nomem(state->mask, req)) {
307 return tevent_req_post(req, ev);
309 state->num_asked = (cli->max_xmit - 100) / DIR_STRUCT_SIZE;
311 SSVAL(state->vwv + 0, 0, state->num_asked);
312 SSVAL(state->vwv + 1, 0, state->attribute);
314 bytes = talloc_array(state, uint8_t, 1);
315 if (tevent_req_nomem(bytes, req)) {
316 return tevent_req_post(req, ev);
318 bytes[0] = 4;
319 bytes = smb_bytes_push_str(bytes, cli_ucs2(cli), mask,
320 strlen(mask)+1, NULL);
322 bytes = smb_bytes_push_bytes(bytes, 5, (uint8_t *)&zero, 2);
323 if (tevent_req_nomem(bytes, req)) {
324 return tevent_req_post(req, ev);
327 subreq = cli_smb_send(state, state->ev, state->cli, SMBsearch,
328 0, 2, state->vwv, talloc_get_size(bytes), bytes);
329 if (tevent_req_nomem(subreq, req)) {
330 return tevent_req_post(req, ev);
332 tevent_req_set_callback(subreq, cli_list_old_done, req);
333 return req;
336 static void cli_list_old_done(struct tevent_req *subreq)
338 struct tevent_req *req = tevent_req_callback_data(
339 subreq, struct tevent_req);
340 struct cli_list_old_state *state = tevent_req_data(
341 req, struct cli_list_old_state);
342 NTSTATUS status;
343 uint8_t cmd;
344 uint8_t wct;
345 uint16_t *vwv;
346 uint32_t num_bytes;
347 uint8_t *bytes;
348 uint16_t received;
349 size_t dirlist_len;
350 uint8_t *tmp;
352 status = cli_smb_recv(subreq, state, NULL, 0, &wct, &vwv, &num_bytes,
353 &bytes);
354 if (!NT_STATUS_IS_OK(status)
355 && !NT_STATUS_EQUAL(status, NT_STATUS_DOS(ERRDOS, ERRnofiles))
356 && !NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
357 TALLOC_FREE(subreq);
358 tevent_req_nterror(req, status);
359 return;
361 if (NT_STATUS_EQUAL(status, NT_STATUS_DOS(ERRDOS, ERRnofiles))
362 || NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
363 received = 0;
364 } else {
365 if (wct < 1) {
366 TALLOC_FREE(subreq);
367 tevent_req_nterror(
368 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
369 return;
371 received = SVAL(vwv + 0, 0);
374 if (received > 0) {
376 * I don't think this can wrap. received is
377 * initialized from a 16-bit value.
379 if (num_bytes < (received * DIR_STRUCT_SIZE + 3)) {
380 TALLOC_FREE(subreq);
381 tevent_req_nterror(
382 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
383 return;
386 dirlist_len = talloc_get_size(state->dirlist);
388 tmp = TALLOC_REALLOC_ARRAY(
389 state, state->dirlist, uint8_t,
390 dirlist_len + received * DIR_STRUCT_SIZE);
391 if (tevent_req_nomem(tmp, req)) {
392 return;
394 state->dirlist = tmp;
395 memcpy(state->dirlist + dirlist_len, bytes + 3,
396 received * DIR_STRUCT_SIZE);
398 SSVAL(state->search_status, 0, 21);
399 memcpy(state->search_status + 2,
400 bytes + 3 + (received-1)*DIR_STRUCT_SIZE, 21);
401 cmd = SMBsearch;
402 } else {
403 if (state->first || state->done) {
404 tevent_req_done(req);
405 return;
407 state->done = true;
408 state->num_asked = 0;
409 cmd = SMBfclose;
411 TALLOC_FREE(subreq);
413 state->first = false;
415 SSVAL(state->vwv + 0, 0, state->num_asked);
416 SSVAL(state->vwv + 1, 0, state->attribute);
418 bytes = talloc_array(state, uint8_t, 1);
419 if (tevent_req_nomem(bytes, req)) {
420 return;
422 bytes[0] = 4;
423 bytes = smb_bytes_push_str(bytes, cli_ucs2(state->cli), "",
424 1, NULL);
425 bytes = smb_bytes_push_bytes(bytes, 5, state->search_status,
426 sizeof(state->search_status));
427 if (tevent_req_nomem(bytes, req)) {
428 return;
430 subreq = cli_smb_send(state, state->ev, state->cli, cmd, 0,
431 2, state->vwv, talloc_get_size(bytes), bytes);
432 if (tevent_req_nomem(subreq, req)) {
433 return;
435 tevent_req_set_callback(subreq, cli_list_old_done, req);
438 static NTSTATUS cli_list_old_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
439 struct file_info **pfinfo)
441 struct cli_list_old_state *state = tevent_req_data(
442 req, struct cli_list_old_state);
443 NTSTATUS status;
444 size_t i, num_received;
445 struct file_info *finfo;
447 if (tevent_req_is_nterror(req, &status)) {
448 return status;
451 num_received = talloc_array_length(state->dirlist) / DIR_STRUCT_SIZE;
453 finfo = TALLOC_ARRAY(mem_ctx, struct file_info, num_received);
454 if (finfo == NULL) {
455 return NT_STATUS_NO_MEMORY;
458 for (i=0; i<num_received; i++) {
459 if (!interpret_short_filename(
460 finfo, state->cli,
461 (char *)state->dirlist + i * DIR_STRUCT_SIZE,
462 &finfo[i])) {
463 TALLOC_FREE(finfo);
464 return NT_STATUS_NO_MEMORY;
467 *pfinfo = finfo;
468 return NT_STATUS_OK;
471 NTSTATUS cli_list_old(struct cli_state *cli, const char *mask,
472 uint16 attribute,
473 NTSTATUS (*fn)(const char *, struct file_info *,
474 const char *, void *), void *state)
476 TALLOC_CTX *frame = talloc_stackframe();
477 struct event_context *ev;
478 struct tevent_req *req;
479 NTSTATUS status = NT_STATUS_NO_MEMORY;
480 struct file_info *finfo;
481 size_t i, num_finfo;
483 if (cli_has_async_calls(cli)) {
485 * Can't use sync call while an async call is in flight
487 status = NT_STATUS_INVALID_PARAMETER;
488 goto fail;
490 ev = event_context_init(frame);
491 if (ev == NULL) {
492 goto fail;
494 req = cli_list_old_send(frame, ev, cli, mask, attribute);
495 if (req == NULL) {
496 goto fail;
498 if (!tevent_req_poll(req, ev)) {
499 status = map_nt_error_from_unix(errno);
500 goto fail;
502 status = cli_list_old_recv(req, frame, &finfo);
503 if (!NT_STATUS_IS_OK(status)) {
504 goto fail;
506 num_finfo = talloc_array_length(finfo);
507 for (i=0; i<num_finfo; i++) {
508 status = fn(cli->dfs_mountpoint, &finfo[i], mask, state);
509 if (!NT_STATUS_IS_OK(status)) {
510 goto fail;
513 fail:
514 TALLOC_FREE(frame);
515 if (!NT_STATUS_IS_OK(status)) {
516 cli_set_error(cli, status);
518 return status;
521 struct cli_list_trans_state {
522 struct tevent_context *ev;
523 struct cli_state *cli;
524 char *mask;
525 uint16_t attribute;
526 uint16_t info_level;
528 int loop_count;
529 int total_received;
530 uint16_t max_matches;
531 bool first;
533 int ff_eos;
534 int ff_dir_handle;
536 uint16_t setup[1];
537 uint8_t *param;
539 struct file_info *finfo;
542 static void cli_list_trans_done(struct tevent_req *subreq);
544 static struct tevent_req *cli_list_trans_send(TALLOC_CTX *mem_ctx,
545 struct tevent_context *ev,
546 struct cli_state *cli,
547 const char *mask,
548 uint16_t attribute,
549 uint16_t info_level)
551 struct tevent_req *req, *subreq;
552 struct cli_list_trans_state *state;
553 size_t nlen, param_len;
554 char *p;
556 req = tevent_req_create(mem_ctx, &state,
557 struct cli_list_trans_state);
558 if (req == NULL) {
559 return NULL;
561 state->ev = ev;
562 state->cli = cli;
563 state->mask = talloc_strdup(state, mask);
564 if (tevent_req_nomem(state->mask, req)) {
565 return tevent_req_post(req, ev);
567 state->attribute = attribute;
568 state->info_level = info_level;
569 state->loop_count = 0;
570 state->first = true;
572 state->max_matches = 1366; /* Match W2k */
574 state->setup[0] = TRANSACT2_FINDFIRST;
576 nlen = 2*(strlen(mask)+1);
577 state->param = TALLOC_ARRAY(state, uint8_t, 12+nlen+2);
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 p = ((char *)state->param)+12;
591 p += clistr_push(state->cli, p, state->mask, nlen,
592 STR_TERMINATE);
593 param_len = PTR_DIFF(p, state->param);
595 subreq = cli_trans_send(state, state->ev, state->cli,
596 SMBtrans2, NULL, -1, 0, 0,
597 state->setup, 1, 0,
598 state->param, param_len, 10,
599 NULL, 0, cli->max_xmit);
600 if (tevent_req_nomem(subreq, req)) {
601 return tevent_req_post(req, ev);
603 tevent_req_set_callback(subreq, cli_list_trans_done, req);
604 return req;
607 static void cli_list_trans_done(struct tevent_req *subreq)
609 struct tevent_req *req = tevent_req_callback_data(
610 subreq, struct tevent_req);
611 struct cli_list_trans_state *state = tevent_req_data(
612 req, struct cli_list_trans_state);
613 NTSTATUS status;
614 uint8_t *param;
615 uint32_t num_param;
616 uint8_t *data;
617 char *data_end;
618 uint32_t num_data;
619 uint32_t min_param;
620 struct file_info *tmp;
621 size_t old_num_finfo;
622 uint16_t recv_flags2;
623 int ff_searchcount;
624 bool ff_eos;
625 char *p, *p2;
626 uint32_t resume_key = 0;
627 int i;
628 DATA_BLOB last_name_raw;
629 struct file_info *finfo = NULL;
630 size_t nlen, param_len;
632 min_param = (state->first ? 6 : 4);
634 status = cli_trans_recv(subreq, talloc_tos(), &recv_flags2,
635 NULL, 0, NULL,
636 &param, min_param, &num_param,
637 &data, 0, &num_data);
638 TALLOC_FREE(subreq);
639 if (!NT_STATUS_IS_OK(status)) {
641 * TODO: retry, OS/2 nofiles
643 tevent_req_nterror(req, status);
644 return;
647 if (state->first) {
648 state->ff_dir_handle = SVAL(param, 0);
649 ff_searchcount = SVAL(param, 2);
650 ff_eos = SVAL(param, 4) != 0;
651 } else {
652 ff_searchcount = SVAL(param, 0);
653 ff_eos = SVAL(param, 2) != 0;
656 old_num_finfo = talloc_array_length(state->finfo);
658 tmp = TALLOC_REALLOC_ARRAY(state, state->finfo, struct file_info,
659 old_num_finfo + ff_searchcount);
660 if (tevent_req_nomem(tmp, req)) {
661 return;
663 state->finfo = tmp;
665 p2 = p = (char *)data;
666 data_end = (char *)data + num_data;
667 last_name_raw = data_blob_null;
669 for (i=0; i<ff_searchcount; i++) {
670 if (p2 >= data_end) {
671 ff_eos = true;
672 break;
674 if ((state->info_level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO)
675 && (i == ff_searchcount-1)) {
676 /* Last entry - fixup the last offset length. */
677 SIVAL(p2, 0, PTR_DIFF((data + num_data), p2));
680 data_blob_free(&last_name_raw);
682 finfo = &state->finfo[old_num_finfo + i];
684 p2 += interpret_long_filename(
685 state->finfo, /* Stick fname to the array as such */
686 state->cli, state->info_level,
687 (char *)data, recv_flags2, p2,
688 data_end, finfo, &resume_key, &last_name_raw);
690 if (finfo->name == NULL) {
691 DEBUG(1, ("cli_list: Error: unable to parse name from "
692 "info level %d\n", state->info_level));
693 ff_eos = true;
694 break;
696 if (!state->first && (state->mask[0] != '\0') &&
697 strcsequal(finfo->name, state->mask)) {
698 DEBUG(1, ("Error: Looping in FIND_NEXT as name %s has "
699 "already been seen?\n", finfo->name));
700 ff_eos = true;
701 break;
705 if (ff_searchcount == 0) {
706 ff_eos = true;
709 TALLOC_FREE(param);
710 TALLOC_FREE(data);
713 * Shrink state->finfo to the real length we received
715 tmp = TALLOC_REALLOC_ARRAY(state, state->finfo, struct file_info,
716 old_num_finfo + i);
717 if (tevent_req_nomem(tmp, req)) {
718 return;
720 state->finfo = tmp;
722 state->first = false;
724 if (ff_eos) {
725 data_blob_free(&last_name_raw);
726 tevent_req_done(req);
727 return;
730 TALLOC_FREE(state->mask);
731 state->mask = talloc_strdup(state, finfo->name);
732 if (tevent_req_nomem(state->mask, req)) {
733 return;
736 state->setup[0] = TRANSACT2_FINDNEXT;
738 nlen = 2*(strlen(state->mask) + 1);
740 param = TALLOC_REALLOC_ARRAY(state, state->param, uint8_t,
741 12 + nlen + last_name_raw.length + 2);
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 p = ((char *)param)+12;
763 if (last_name_raw.length) {
764 memcpy(p, last_name_raw.data, last_name_raw.length);
765 p += last_name_raw.length;
766 data_blob_free(&last_name_raw);
767 } else {
768 p += clistr_push(state->cli, p, state->mask, nlen,
769 STR_TERMINATE);
772 param_len = PTR_DIFF(p, param);
774 subreq = cli_trans_send(state, state->ev, state->cli,
775 SMBtrans2, NULL, -1, 0, 0,
776 state->setup, 1, 0,
777 state->param, param_len, 10,
778 NULL, 0, state->cli->max_xmit);
779 if (tevent_req_nomem(subreq, req)) {
780 return;
782 tevent_req_set_callback(subreq, cli_list_trans_done, req);
785 static NTSTATUS cli_list_trans_recv(struct tevent_req *req,
786 TALLOC_CTX *mem_ctx,
787 struct file_info **finfo)
789 struct cli_list_trans_state *state = tevent_req_data(
790 req, struct cli_list_trans_state);
791 NTSTATUS status;
793 if (tevent_req_is_nterror(req, &status)) {
794 return status;
796 *finfo = talloc_move(mem_ctx, &state->finfo);
797 return NT_STATUS_OK;
800 NTSTATUS cli_list_trans(struct cli_state *cli, const char *mask,
801 uint16_t attribute, int info_level,
802 NTSTATUS (*fn)(const char *mnt, struct file_info *finfo,
803 const char *mask, void *private_data),
804 void *private_data)
806 TALLOC_CTX *frame = talloc_stackframe();
807 struct event_context *ev;
808 struct tevent_req *req;
809 int i, num_finfo;
810 struct file_info *finfo = NULL;
811 NTSTATUS status = NT_STATUS_NO_MEMORY;
813 if (cli_has_async_calls(cli)) {
815 * Can't use sync call while an async call is in flight
817 status = NT_STATUS_INVALID_PARAMETER;
818 goto fail;
820 ev = event_context_init(frame);
821 if (ev == NULL) {
822 goto fail;
824 req = cli_list_trans_send(frame, ev, cli, mask, attribute, info_level);
825 if (req == NULL) {
826 goto fail;
828 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
829 goto fail;
831 status = cli_list_trans_recv(req, frame, &finfo);
832 if (!NT_STATUS_IS_OK(status)) {
833 goto fail;
835 num_finfo = talloc_array_length(finfo);
836 for (i=0; i<num_finfo; i++) {
837 status = fn(cli->dfs_mountpoint, &finfo[i], mask, private_data);
838 if (!NT_STATUS_IS_OK(status)) {
839 goto fail;
842 fail:
843 TALLOC_FREE(frame);
844 if (!NT_STATUS_IS_OK(status)) {
845 cli_set_error(cli, status);
847 return status;
850 struct cli_list_state {
851 NTSTATUS (*recv_fn)(struct tevent_req *req, TALLOC_CTX *mem_ctx,
852 struct file_info **finfo);
853 struct file_info *finfo;
856 static void cli_list_done(struct tevent_req *subreq);
858 struct tevent_req *cli_list_send(TALLOC_CTX *mem_ctx,
859 struct tevent_context *ev,
860 struct cli_state *cli,
861 const char *mask,
862 uint16_t attribute,
863 uint16_t info_level)
865 struct tevent_req *req, *subreq;
866 struct cli_list_state *state;
868 req = tevent_req_create(mem_ctx, &state, struct cli_list_state);
869 if (req == NULL) {
870 return NULL;
873 if (cli->protocol <= PROTOCOL_LANMAN1) {
874 subreq = cli_list_old_send(state, ev, cli, mask, attribute);
875 state->recv_fn = cli_list_old_recv;
876 } else {
877 subreq = cli_list_trans_send(state, ev, cli, mask, attribute,
878 info_level);
879 state->recv_fn = cli_list_trans_recv;
881 if (tevent_req_nomem(subreq, req)) {
882 return tevent_req_post(req, ev);
884 tevent_req_set_callback(subreq, cli_list_done, req);
885 return req;
888 static void cli_list_done(struct tevent_req *subreq)
890 struct tevent_req *req = tevent_req_callback_data(
891 subreq, struct tevent_req);
892 struct cli_list_state *state = tevent_req_data(
893 req, struct cli_list_state);
894 NTSTATUS status;
896 status = state->recv_fn(subreq, state, &state->finfo);
897 TALLOC_FREE(subreq);
898 if (!NT_STATUS_IS_OK(status)) {
899 tevent_req_nterror(req, status);
900 return;
902 tevent_req_done(req);
905 NTSTATUS cli_list_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
906 struct file_info **finfo, size_t *num_finfo)
908 struct cli_list_state *state = tevent_req_data(
909 req, struct cli_list_state);
910 NTSTATUS status;
912 if (tevent_req_is_nterror(req, &status)) {
913 return status;
915 *num_finfo = talloc_array_length(state->finfo);
916 *finfo = talloc_move(mem_ctx, &state->finfo);
917 return NT_STATUS_OK;
920 NTSTATUS cli_list(struct cli_state *cli, const char *mask, uint16 attribute,
921 NTSTATUS (*fn)(const char *, struct file_info *, const char *,
922 void *), void *state)
924 TALLOC_CTX *frame = talloc_stackframe();
925 struct event_context *ev;
926 struct tevent_req *req;
927 NTSTATUS status = NT_STATUS_NO_MEMORY;
928 struct file_info *finfo;
929 size_t i, num_finfo;
930 uint16_t info_level;
932 if (cli_has_async_calls(cli)) {
934 * Can't use sync call while an async call is in flight
936 status = NT_STATUS_INVALID_PARAMETER;
937 goto fail;
939 ev = event_context_init(frame);
940 if (ev == NULL) {
941 goto fail;
944 info_level = (cli->capabilities & CAP_NT_SMBS)
945 ? SMB_FIND_FILE_BOTH_DIRECTORY_INFO : SMB_FIND_INFO_STANDARD;
947 req = cli_list_send(frame, ev, cli, mask, attribute, info_level);
948 if (req == NULL) {
949 goto fail;
951 if (!tevent_req_poll(req, ev)) {
952 status = map_nt_error_from_unix(errno);
953 goto fail;
956 status = cli_list_recv(req, frame, &finfo, &num_finfo);
957 if (!NT_STATUS_IS_OK(status)) {
958 goto fail;
961 for (i=0; i<num_finfo; i++) {
962 status = fn(cli->dfs_mountpoint, &finfo[i], mask, state);
963 if (!NT_STATUS_IS_OK(status)) {
964 goto fail;
967 fail:
968 TALLOC_FREE(frame);
969 if (!NT_STATUS_IS_OK(status)) {
970 cli_set_error(cli, status);
972 return status;