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