Fix bug #7700 - Improvement of return code of smbclient
[idra/samba.git] / source3 / libsmb / clilist.c
1 /*
2    Unix SMB/CIFS implementation.
3    client directory list routines
4    Copyright (C) Andrew Tridgell 1994-1998
5
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.
10
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.
15
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/>.
18 */
19
20 #include "includes.h"
21 #include "async_smb.h"
22
23 /****************************************************************************
24  Calculate a safe next_entry_offset.
25 ****************************************************************************/
26
27 static size_t calc_next_entry_offset(const char *base, const char *pdata_end)
28 {
29         size_t next_entry_offset = (size_t)IVAL(base,0);
30
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;
35         }
36         return next_entry_offset;
37 }
38
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 ****************************************************************************/
46
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)
57 {
58         int len;
59         size_t ret;
60         const char *base = p;
61
62         data_blob_free(p_last_name_raw);
63
64         if (p_resume_key) {
65                 *p_resume_key = 0;
66         }
67         ZERO_STRUCTP(finfo);
68
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;
75                         }
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);
87
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
96                          */
97
98                         if (p + len > pdata_end) {
99                                 return pdata_end - base;
100                         }
101
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,
110                                                 p,
111                                                 len+2,
112                                                 STR_TERMINATE);
113                         if (ret == (size_t)-1) {
114                                 return pdata_end - base;
115                         }
116                         p += ret;
117                         return PTR_DIFF(p, base);
118
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;
124                         }
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;
138                         }
139                         ret = clistr_pull_talloc(ctx,
140                                                 base_ptr,
141                                                 recv_flags2,
142                                                 &finfo->name,
143                                                 p,
144                                                 len,
145                                                 STR_NOALIGN);
146                         if (ret == (size_t)-1) {
147                                 return pdata_end - base;
148                         }
149                         p += ret;
150                         return PTR_DIFF(p, base) + 1;
151
152                 case SMB_FIND_FILE_BOTH_DIRECTORY_INFO: /* NT uses this, but also accepts 2 */
153                 {
154                         size_t namelen, slen;
155
156                         if (pdata_end - base < 94) {
157                                 return pdata_end - base;
158                         }
159
160                         p += 4; /* next entry offset */
161
162                         if (p_resume_key) {
163                                 *p_resume_key = IVAL(p,0);
164                         }
165                         p += 4; /* fileindex */
166
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;
187                         }
188                         p += 2;
189                         {
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);
196                         }
197                         p += 24; /* short name? */
198                         if (p + namelen < p || p + namelen > pdata_end) {
199                                 return pdata_end - base;
200                         }
201                         ret = clistr_pull_talloc(ctx,
202                                                 base_ptr,
203                                                 recv_flags2,
204                                                 &finfo->name,
205                                                 p,
206                                                 namelen,
207                                                 0);
208                         if (ret == (size_t)-1) {
209                                 return pdata_end - base;
210                         }
211
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. */
216
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);
221                         }
222                         return calc_next_entry_offset(base, pdata_end);
223                 }
224         }
225
226         DEBUG(1,("Unknown long filename format %d\n",level));
227         return calc_next_entry_offset(base, pdata_end);
228 }
229
230 /****************************************************************************
231  Interpret a short filename structure.
232  The length of the structure is returned.
233 ****************************************************************************/
234
235 static bool interpret_short_filename(TALLOC_CTX *ctx,
236                                 struct cli_state *cli,
237                                 char *p,
238                                 struct file_info *finfo)
239 {
240         size_t ret;
241         ZERO_STRUCTP(finfo);
242
243         finfo->mode = CVAL(p,21);
244
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,
256                         12,
257                         STR_ASCII);
258         if (ret == (size_t)-1) {
259                 return false;
260         }
261
262         if (finfo->name) {
263                 strlcpy(finfo->short_name,
264                         finfo->name,
265                         sizeof(finfo->short_name));
266         }
267         return true;
268 }
269
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;
281 };
282
283 static void cli_list_old_done(struct tevent_req *subreq);
284
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)
290 {
291         struct tevent_req *req, *subreq;
292         struct cli_list_old_state *state;
293         uint8_t *bytes;
294         static const uint16_t zero = 0;
295
296         req = tevent_req_create(mem_ctx, &state, struct cli_list_old_state);
297         if (req == NULL) {
298                 return NULL;
299         }
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);
307         }
308         state->num_asked = (cli->max_xmit - 100) / DIR_STRUCT_SIZE;
309
310         SSVAL(state->vwv + 0, 0, state->num_asked);
311         SSVAL(state->vwv + 1, 0, state->attribute);
312
313         bytes = talloc_array(state, uint8_t, 1);
314         if (tevent_req_nomem(bytes, req)) {
315                 return tevent_req_post(req, ev);
316         }
317         bytes[0] = 4;
318         bytes = smb_bytes_push_str(bytes, cli_ucs2(cli), mask,
319                                    strlen(mask)+1, NULL);
320
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);
324         }
325
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);
330         }
331         tevent_req_set_callback(subreq, cli_list_old_done, req);
332         return req;
333 }
334
335 static void cli_list_old_done(struct tevent_req *subreq)
336 {
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;
350
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;
359         }
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;
369                 }
370                 received = SVAL(vwv + 0, 0);
371         }
372
373         if (received > 0) {
374                 /*
375                  * I don't think this can wrap. received is
376                  * initialized from a 16-bit value.
377                  */
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;
383                 }
384
385                 dirlist_len = talloc_get_size(state->dirlist);
386
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;
392                 }
393                 state->dirlist = tmp;
394                 memcpy(state->dirlist + dirlist_len, bytes + 3,
395                        received * DIR_STRUCT_SIZE);
396
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;
405                 }
406                 state->done = true;
407                 state->num_asked = 0;
408                 cmd = SMBfclose;
409         }
410         TALLOC_FREE(subreq);
411
412         state->first = false;
413
414         SSVAL(state->vwv + 0, 0, state->num_asked);
415         SSVAL(state->vwv + 1, 0, state->attribute);
416
417         bytes = talloc_array(state, uint8_t, 1);
418         if (tevent_req_nomem(bytes, req)) {
419                 return;
420         }
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;
428         }
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;
433         }
434         tevent_req_set_callback(subreq, cli_list_old_done, req);
435 }
436
437 static NTSTATUS cli_list_old_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
438                                   struct file_info **pfinfo)
439 {
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;
445
446         if (tevent_req_is_nterror(req, &status)) {
447                 return status;
448         }
449
450         num_received = talloc_array_length(state->dirlist) / DIR_STRUCT_SIZE;
451
452         finfo = TALLOC_ARRAY(mem_ctx, struct file_info, num_received);
453         if (finfo == NULL) {
454                 return NT_STATUS_NO_MEMORY;
455         }
456
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;
464                 }
465         }
466         *pfinfo = finfo;
467         return NT_STATUS_OK;
468 }
469
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)
474 {
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;
481
482         if (cli_has_async_calls(cli)) {
483                 /*
484                  * Can't use sync call while an async call is in flight
485                  */
486                 status = NT_STATUS_INVALID_PARAMETER;
487                 goto fail;
488         }
489         ev = event_context_init(frame);
490         if (ev == NULL) {
491                 goto fail;
492         }
493         req = cli_list_old_send(frame, ev, cli, mask, attribute);
494         if (req == NULL) {
495                 goto fail;
496         }
497         if (!tevent_req_poll(req, ev)) {
498                 status = map_nt_error_from_unix(errno);
499                 goto fail;
500         }
501         status = cli_list_old_recv(req, frame, &finfo);
502         if (!NT_STATUS_IS_OK(status)) {
503                 goto fail;
504         }
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;
510                 }
511         }
512  fail:
513         TALLOC_FREE(frame);
514         if (!NT_STATUS_IS_OK(status)) {
515                 cli_set_error(cli, status);
516         }
517         return status;
518 }
519
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;
526
527         int loop_count;
528         int total_received;
529         uint16_t max_matches;
530         bool first;
531
532         int ff_eos;
533         int ff_dir_handle;
534
535         uint16_t setup[1];
536         uint8_t *param;
537
538         struct file_info *finfo;
539 };
540
541 static void cli_list_trans_done(struct tevent_req *subreq);
542
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)
549 {
550         struct tevent_req *req, *subreq;
551         struct cli_list_trans_state *state;
552         size_t nlen, param_len;
553         char *p;
554
555         req = tevent_req_create(mem_ctx, &state,
556                                 struct cli_list_trans_state);
557         if (req == NULL) {
558                 return NULL;
559         }
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);
565         }
566         state->attribute = attribute;
567         state->info_level = info_level;
568         state->loop_count = 0;
569         state->first = true;
570
571         state->max_matches = 1366; /* Match W2k */
572
573         state->setup[0] = TRANSACT2_FINDFIRST;
574
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);
579         }
580
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);
588
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);
593
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);
601         }
602         tevent_req_set_callback(subreq, cli_list_trans_done, req);
603         return req;
604 }
605
606 static void cli_list_trans_done(struct tevent_req *subreq)
607 {
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;
630
631         min_param = (state->first ? 6 : 4);
632
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)) {
639                 /*
640                  * TODO: retry, OS/2 nofiles
641                  */
642                 tevent_req_nterror(req, status);
643                 return;
644         }
645
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;
653         }
654
655         old_num_finfo = talloc_array_length(state->finfo);
656
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;
661         }
662         state->finfo = tmp;
663
664         p2 = p = (char *)data;
665         data_end = (char *)data + num_data;
666         last_name_raw = data_blob_null;
667
668         for (i=0; i<ff_searchcount; i++) {
669                 if (p2 >= data_end) {
670                         ff_eos = true;
671                         break;
672                 }
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));
677                 }
678
679                 data_blob_free(&last_name_raw);
680
681                 finfo = &state->finfo[old_num_finfo + i];
682
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);
688
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;
694                 }
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;
701                 }
702         }
703
704         if (ff_searchcount == 0) {
705                 ff_eos = true;
706         }
707
708         TALLOC_FREE(param);
709         TALLOC_FREE(data);
710
711         /*
712          * Shrink state->finfo to the real length we received
713          */
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;
718         }
719         state->finfo = tmp;
720
721         state->first = false;
722
723         if (ff_eos) {
724                 data_blob_free(&last_name_raw);
725                 tevent_req_done(req);
726                 return;
727         }
728
729         TALLOC_FREE(state->mask);
730         state->mask = talloc_strdup(state, finfo->name);
731         if (tevent_req_nomem(state->mask, req)) {
732                 return;
733         }
734
735         state->setup[0] = TRANSACT2_FINDNEXT;
736
737         nlen = 2*(strlen(state->mask) + 1);
738
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;
743         }
744         state->param = param;
745
746         SSVAL(param, 0, state->ff_dir_handle);
747         SSVAL(param, 2, state->max_matches); /* max count */
748         SSVAL(param, 4, state->info_level);
749         /*
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.
752          */
753         SIVAL(param, 6, resume_key); /* ff_resume_key */
754         /*
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
758          */
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);
769         }
770
771         param_len = PTR_DIFF(p, param);
772
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;
780         }
781         tevent_req_set_callback(subreq, cli_list_trans_done, req);
782 }
783
784 static NTSTATUS cli_list_trans_recv(struct tevent_req *req,
785                                     TALLOC_CTX *mem_ctx,
786                                     struct file_info **finfo)
787 {
788         struct cli_list_trans_state *state = tevent_req_data(
789                 req, struct cli_list_trans_state);
790         NTSTATUS status;
791
792         if (tevent_req_is_nterror(req, &status)) {
793                 return status;
794         }
795         *finfo = talloc_move(mem_ctx, &state->finfo);
796         return NT_STATUS_OK;
797 }
798
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)
804 {
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;
811
812         if (cli_has_async_calls(cli)) {
813                 /*
814                  * Can't use sync call while an async call is in flight
815                  */
816                 status = NT_STATUS_INVALID_PARAMETER;
817                 goto fail;
818         }
819         ev = event_context_init(frame);
820         if (ev == NULL) {
821                 goto fail;
822         }
823         req = cli_list_trans_send(frame, ev, cli, mask, attribute, info_level);
824         if (req == NULL) {
825                 goto fail;
826         }
827         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
828                 goto fail;
829         }
830         status = cli_list_trans_recv(req, frame, &finfo);
831         if (!NT_STATUS_IS_OK(status)) {
832                 goto fail;
833         }
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;
839                 }
840         }
841  fail:
842         TALLOC_FREE(frame);
843         if (!NT_STATUS_IS_OK(status)) {
844                 cli_set_error(cli, status);
845         }
846         return status;
847 }
848
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;
853 };
854
855 static void cli_list_done(struct tevent_req *subreq);
856
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)
863 {
864         struct tevent_req *req, *subreq;
865         struct cli_list_state *state;
866
867         req = tevent_req_create(mem_ctx, &state, struct cli_list_state);
868         if (req == NULL) {
869                 return NULL;
870         }
871
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;
879         }
880         if (tevent_req_nomem(subreq, req)) {
881                 return tevent_req_post(req, ev);
882         }
883         tevent_req_set_callback(subreq, cli_list_done, req);
884         return req;
885 }
886
887 static void cli_list_done(struct tevent_req *subreq)
888 {
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;
894
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;
900         }
901         tevent_req_done(req);
902 }
903
904 NTSTATUS cli_list_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
905                        struct file_info **finfo, size_t *num_finfo)
906 {
907         struct cli_list_state *state = tevent_req_data(
908                 req, struct cli_list_state);
909         NTSTATUS status;
910
911         if (tevent_req_is_nterror(req, &status)) {
912                 return status;
913         }
914         *num_finfo = talloc_array_length(state->finfo);
915         *finfo = talloc_move(mem_ctx, &state->finfo);
916         return NT_STATUS_OK;
917 }
918
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)
922 {
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;
930
931         if (cli_has_async_calls(cli)) {
932                 /*
933                  * Can't use sync call while an async call is in flight
934                  */
935                 status = NT_STATUS_INVALID_PARAMETER;
936                 goto fail;
937         }
938         ev = event_context_init(frame);
939         if (ev == NULL) {
940                 goto fail;
941         }
942
943         info_level = (cli->capabilities & CAP_NT_SMBS)
944                 ? SMB_FIND_FILE_BOTH_DIRECTORY_INFO : SMB_FIND_INFO_STANDARD;
945
946         req = cli_list_send(frame, ev, cli, mask, attribute, info_level);
947         if (req == NULL) {
948                 goto fail;
949         }
950         if (!tevent_req_poll(req, ev)) {
951                 status = map_nt_error_from_unix(errno);
952                 goto fail;
953         }
954
955         status = cli_list_recv(req, frame, &finfo, &num_finfo);
956         if (!NT_STATUS_IS_OK(status)) {
957                 goto fail;
958         }
959
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;
964                 }
965         }
966  fail:
967         TALLOC_FREE(frame);
968         if (!NT_STATUS_IS_OK(status)) {
969                 cli_set_error(cli, status);
970         }
971         return status;
972 }