s3:libsmb/cli*: use CLI_BUFFER_SIZE instead of cli->max_xmit
[metze/samba/wip.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 "libsmb/libsmb.h"
22 #include "../lib/util/tevent_ntstatus.h"
23 #include "async_smb.h"
24 #include "trans2.h"
25
26 /****************************************************************************
27  Calculate a safe next_entry_offset.
28 ****************************************************************************/
29
30 static size_t calc_next_entry_offset(const char *base, const char *pdata_end)
31 {
32         size_t next_entry_offset = (size_t)IVAL(base,0);
33
34         if (next_entry_offset == 0 ||
35                         base + next_entry_offset < base ||
36                         base + next_entry_offset > pdata_end) {
37                 next_entry_offset = pdata_end - base;
38         }
39         return next_entry_offset;
40 }
41
42 /****************************************************************************
43  Interpret a long filename structure - this is mostly guesses at the moment.
44  The length of the structure is returned
45  The structure of a long filename depends on the info level.
46  SMB_FIND_FILE_BOTH_DIRECTORY_INFO is used
47  by NT and SMB_FIND_EA_SIZE is used by OS/2
48 ****************************************************************************/
49
50 static size_t interpret_long_filename(TALLOC_CTX *ctx,
51                                         struct cli_state *cli,
52                                         int level,
53                                         const char *base_ptr,
54                                         uint16_t recv_flags2,
55                                         const char *p,
56                                         const char *pdata_end,
57                                         struct file_info *finfo,
58                                         uint32 *p_resume_key,
59                                         DATA_BLOB *p_last_name_raw)
60 {
61         int len;
62         size_t ret;
63         const char *base = p;
64
65         data_blob_free(p_last_name_raw);
66
67         if (p_resume_key) {
68                 *p_resume_key = 0;
69         }
70         ZERO_STRUCTP(finfo);
71
72         switch (level) {
73                 case SMB_FIND_INFO_STANDARD: /* OS/2 understands this */
74                         /* these dates are converted to GMT by
75                            make_unix_date */
76                         if (pdata_end - base < 27) {
77                                 return pdata_end - base;
78                         }
79                         finfo->ctime_ts = convert_time_t_to_timespec(
80                                 make_unix_date2(p+4, cli->serverzone));
81                         finfo->atime_ts = convert_time_t_to_timespec(
82                                 make_unix_date2(p+8, cli->serverzone));
83                         finfo->mtime_ts = convert_time_t_to_timespec(
84                                 make_unix_date2(p+12, cli->serverzone));
85                         finfo->size = IVAL(p,16);
86                         finfo->mode = CVAL(p,24);
87                         len = CVAL(p, 26);
88                         p += 27;
89                         p += align_string(base_ptr, p, 0);
90
91                         /* We can safely use len here (which is required by OS/2)
92                          * and the NAS-BASIC server instead of +2 or +1 as the
93                          * STR_TERMINATE flag below is
94                          * actually used as the length calculation.
95                          * The len is merely an upper bound.
96                          * Due to the explicit 2 byte null termination
97                          * in cli_receive_trans/cli_receive_nt_trans
98                          * we know this is safe. JRA + kukks
99                          */
100
101                         if (p + len > pdata_end) {
102                                 return pdata_end - base;
103                         }
104
105                         /* the len+2 below looks strange but it is
106                            important to cope with the differences
107                            between win2000 and win9x for this call
108                            (tridge) */
109                         ret = clistr_pull_talloc(ctx,
110                                                 base_ptr,
111                                                 recv_flags2,
112                                                 &finfo->name,
113                                                 p,
114                                                 len+2,
115                                                 STR_TERMINATE);
116                         if (ret == (size_t)-1) {
117                                 return pdata_end - base;
118                         }
119                         p += ret;
120                         return PTR_DIFF(p, base);
121
122                 case SMB_FIND_EA_SIZE: /* this is what OS/2 uses mostly */
123                         /* these dates are converted to GMT by
124                            make_unix_date */
125                         if (pdata_end - base < 31) {
126                                 return pdata_end - base;
127                         }
128                         finfo->ctime_ts = convert_time_t_to_timespec(
129                                 make_unix_date2(p+4, cli->serverzone));
130                         finfo->atime_ts = convert_time_t_to_timespec(
131                                 make_unix_date2(p+8, cli->serverzone));
132                         finfo->mtime_ts = convert_time_t_to_timespec(
133                                 make_unix_date2(p+12, cli->serverzone));
134                         finfo->size = IVAL(p,16);
135                         finfo->mode = CVAL(p,24);
136                         len = CVAL(p, 30);
137                         p += 31;
138                         /* check for unisys! */
139                         if (p + len + 1 > pdata_end) {
140                                 return pdata_end - base;
141                         }
142                         ret = clistr_pull_talloc(ctx,
143                                                 base_ptr,
144                                                 recv_flags2,
145                                                 &finfo->name,
146                                                 p,
147                                                 len,
148                                                 STR_NOALIGN);
149                         if (ret == (size_t)-1) {
150                                 return pdata_end - base;
151                         }
152                         p += ret;
153                         return PTR_DIFF(p, base) + 1;
154
155                 case SMB_FIND_FILE_BOTH_DIRECTORY_INFO: /* NT uses this, but also accepts 2 */
156                 {
157                         size_t namelen, slen;
158
159                         if (pdata_end - base < 94) {
160                                 return pdata_end - base;
161                         }
162
163                         p += 4; /* next entry offset */
164
165                         if (p_resume_key) {
166                                 *p_resume_key = IVAL(p,0);
167                         }
168                         p += 4; /* fileindex */
169
170                         /* Offset zero is "create time", not "change time". */
171                         p += 8;
172                         finfo->atime_ts = interpret_long_date(p);
173                         p += 8;
174                         finfo->mtime_ts = interpret_long_date(p);
175                         p += 8;
176                         finfo->ctime_ts = interpret_long_date(p);
177                         p += 8;
178                         finfo->size = IVAL2_TO_SMB_BIG_UINT(p,0);
179                         p += 8;
180                         p += 8; /* alloc size */
181                         finfo->mode = CVAL(p,0);
182                         p += 4;
183                         namelen = IVAL(p,0);
184                         p += 4;
185                         p += 4; /* EA size */
186                         slen = SVAL(p, 0);
187                         if (slen > 24) {
188                                 /* Bad short name length. */
189                                 return pdata_end - base;
190                         }
191                         p += 2;
192                         ret = clistr_pull_talloc(ctx,
193                                                 base_ptr,
194                                                 recv_flags2,
195                                                 &finfo->short_name,
196                                                 p,
197                                                 slen,
198                                                 STR_UNICODE);
199                         if (ret == (size_t)-1) {
200                                 return pdata_end - base;
201                         }
202                         p += 24; /* short name? */
203                         if (p + namelen < p || p + namelen > pdata_end) {
204                                 return pdata_end - base;
205                         }
206                         ret = clistr_pull_talloc(ctx,
207                                                 base_ptr,
208                                                 recv_flags2,
209                                                 &finfo->name,
210                                                 p,
211                                                 namelen,
212                                                 0);
213                         if (ret == (size_t)-1) {
214                                 return pdata_end - base;
215                         }
216
217                         /* To be robust in the face of unicode conversion failures
218                            we need to copy the raw bytes of the last name seen here.
219                            Namelen doesn't include the terminating unicode null, so
220                            copy it here. */
221
222                         if (p_last_name_raw) {
223                                 *p_last_name_raw = data_blob(NULL, namelen+2);
224                                 memcpy(p_last_name_raw->data, p, namelen);
225                                 SSVAL(p_last_name_raw->data, namelen, 0);
226                         }
227                         return calc_next_entry_offset(base, pdata_end);
228                 }
229         }
230
231         DEBUG(1,("Unknown long filename format %d\n",level));
232         return calc_next_entry_offset(base, pdata_end);
233 }
234
235 /****************************************************************************
236  Interpret a short filename structure.
237  The length of the structure is returned.
238 ****************************************************************************/
239
240 static bool interpret_short_filename(TALLOC_CTX *ctx,
241                                 struct cli_state *cli,
242                                 char *p,
243                                 struct file_info *finfo)
244 {
245         size_t ret;
246         ZERO_STRUCTP(finfo);
247
248         finfo->mode = CVAL(p,21);
249
250         /* this date is converted to GMT by make_unix_date */
251         finfo->ctime_ts.tv_sec = make_unix_date(p+22, cli->serverzone);
252         finfo->ctime_ts.tv_nsec = 0;
253         finfo->mtime_ts.tv_sec = finfo->atime_ts.tv_sec = finfo->ctime_ts.tv_sec;
254         finfo->mtime_ts.tv_nsec = finfo->atime_ts.tv_nsec = 0;
255         finfo->size = IVAL(p,26);
256         ret = clistr_pull_talloc(ctx,
257                         NULL,
258                         0,
259                         &finfo->name,
260                         p+30,
261                         12,
262                         STR_ASCII);
263         if (ret == (size_t)-1) {
264                 return false;
265         }
266
267         if (finfo->name) {
268                 finfo->short_name = talloc_strdup(ctx, finfo->name);
269                 if (finfo->short_name == NULL) {
270                         return false;
271                 }
272         }
273         return true;
274 }
275
276 struct cli_list_old_state {
277         struct tevent_context *ev;
278         struct cli_state *cli;
279         uint16_t vwv[2];
280         char *mask;
281         int num_asked;
282         uint16_t attribute;
283         uint8_t search_status[23];
284         bool first;
285         bool done;
286         uint8_t *dirlist;
287 };
288
289 static void cli_list_old_done(struct tevent_req *subreq);
290
291 static struct tevent_req *cli_list_old_send(TALLOC_CTX *mem_ctx,
292                                             struct tevent_context *ev,
293                                             struct cli_state *cli,
294                                             const char *mask,
295                                             uint16_t attribute)
296 {
297         struct tevent_req *req, *subreq;
298         struct cli_list_old_state *state;
299         uint8_t *bytes;
300         static const uint16_t zero = 0;
301
302         req = tevent_req_create(mem_ctx, &state, struct cli_list_old_state);
303         if (req == NULL) {
304                 return NULL;
305         }
306         state->ev = ev;
307         state->cli = cli;
308         state->attribute = attribute;
309         state->first = true;
310         state->mask = talloc_strdup(state, mask);
311         if (tevent_req_nomem(state->mask, req)) {
312                 return tevent_req_post(req, ev);
313         }
314         state->num_asked = (cli->max_xmit - 100) / DIR_STRUCT_SIZE;
315
316         SSVAL(state->vwv + 0, 0, state->num_asked);
317         SSVAL(state->vwv + 1, 0, state->attribute);
318
319         bytes = talloc_array(state, uint8_t, 1);
320         if (tevent_req_nomem(bytes, req)) {
321                 return tevent_req_post(req, ev);
322         }
323         bytes[0] = 4;
324         bytes = smb_bytes_push_str(bytes, cli_ucs2(cli), mask,
325                                    strlen(mask)+1, NULL);
326
327         bytes = smb_bytes_push_bytes(bytes, 5, (const uint8_t *)&zero, 2);
328         if (tevent_req_nomem(bytes, req)) {
329                 return tevent_req_post(req, ev);
330         }
331
332         subreq = cli_smb_send(state, state->ev, state->cli, SMBsearch,
333                               0, 2, state->vwv, talloc_get_size(bytes), bytes);
334         if (tevent_req_nomem(subreq, req)) {
335                 return tevent_req_post(req, ev);
336         }
337         tevent_req_set_callback(subreq, cli_list_old_done, req);
338         return req;
339 }
340
341 static void cli_list_old_done(struct tevent_req *subreq)
342 {
343         struct tevent_req *req = tevent_req_callback_data(
344                 subreq, struct tevent_req);
345         struct cli_list_old_state *state = tevent_req_data(
346                 req, struct cli_list_old_state);
347         NTSTATUS status;
348         uint8_t cmd;
349         uint8_t wct;
350         uint16_t *vwv;
351         uint32_t num_bytes;
352         uint8_t *bytes;
353         uint16_t received;
354         size_t dirlist_len;
355         uint8_t *tmp;
356
357         status = cli_smb_recv(subreq, state, NULL, 0, &wct, &vwv, &num_bytes,
358                               &bytes);
359         if (!NT_STATUS_IS_OK(status)
360             && !NT_STATUS_EQUAL(status, NT_STATUS_DOS(ERRDOS, ERRnofiles))
361             && !NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
362                 TALLOC_FREE(subreq);
363                 tevent_req_nterror(req, status);
364                 return;
365         }
366         if (NT_STATUS_EQUAL(status, NT_STATUS_DOS(ERRDOS, ERRnofiles))
367             || NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
368                 received = 0;
369         } else {
370                 if (wct < 1) {
371                         TALLOC_FREE(subreq);
372                         tevent_req_nterror(
373                                 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
374                         return;
375                 }
376                 received = SVAL(vwv + 0, 0);
377         }
378
379         if (received > 0) {
380                 /*
381                  * I don't think this can wrap. received is
382                  * initialized from a 16-bit value.
383                  */
384                 if (num_bytes < (received * DIR_STRUCT_SIZE + 3)) {
385                         TALLOC_FREE(subreq);
386                         tevent_req_nterror(
387                                 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
388                         return;
389                 }
390
391                 dirlist_len = talloc_get_size(state->dirlist);
392
393                 tmp = talloc_realloc(
394                         state, state->dirlist, uint8_t,
395                         dirlist_len + received * DIR_STRUCT_SIZE);
396                 if (tevent_req_nomem(tmp, req)) {
397                         return;
398                 }
399                 state->dirlist = tmp;
400                 memcpy(state->dirlist + dirlist_len, bytes + 3,
401                        received * DIR_STRUCT_SIZE);
402
403                 SSVAL(state->search_status, 0, 21);
404                 memcpy(state->search_status + 2,
405                        bytes + 3 + (received-1)*DIR_STRUCT_SIZE, 21);
406                 cmd = SMBsearch;
407         } else {
408                 if (state->first || state->done) {
409                         tevent_req_done(req);
410                         return;
411                 }
412                 state->done = true;
413                 state->num_asked = 0;
414                 cmd = SMBfclose;
415         }
416         TALLOC_FREE(subreq);
417
418         state->first = false;
419
420         SSVAL(state->vwv + 0, 0, state->num_asked);
421         SSVAL(state->vwv + 1, 0, state->attribute);
422
423         bytes = talloc_array(state, uint8_t, 1);
424         if (tevent_req_nomem(bytes, req)) {
425                 return;
426         }
427         bytes[0] = 4;
428         bytes = smb_bytes_push_str(bytes, cli_ucs2(state->cli), "",
429                                    1, NULL);
430         bytes = smb_bytes_push_bytes(bytes, 5, state->search_status,
431                                      sizeof(state->search_status));
432         if (tevent_req_nomem(bytes, req)) {
433                 return;
434         }
435         subreq = cli_smb_send(state, state->ev, state->cli, cmd, 0,
436                               2, state->vwv, talloc_get_size(bytes), bytes);
437         if (tevent_req_nomem(subreq, req)) {
438                 return;
439         }
440         tevent_req_set_callback(subreq, cli_list_old_done, req);
441 }
442
443 static NTSTATUS cli_list_old_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
444                                   struct file_info **pfinfo)
445 {
446         struct cli_list_old_state *state = tevent_req_data(
447                 req, struct cli_list_old_state);
448         NTSTATUS status;
449         size_t i, num_received;
450         struct file_info *finfo;
451
452         if (tevent_req_is_nterror(req, &status)) {
453                 return status;
454         }
455
456         num_received = talloc_array_length(state->dirlist) / DIR_STRUCT_SIZE;
457
458         finfo = talloc_array(mem_ctx, struct file_info, num_received);
459         if (finfo == NULL) {
460                 return NT_STATUS_NO_MEMORY;
461         }
462
463         for (i=0; i<num_received; i++) {
464                 if (!interpret_short_filename(
465                             finfo, state->cli,
466                             (char *)state->dirlist + i * DIR_STRUCT_SIZE,
467                             &finfo[i])) {
468                         TALLOC_FREE(finfo);
469                         return NT_STATUS_NO_MEMORY;
470                 }
471         }
472         *pfinfo = finfo;
473         return NT_STATUS_OK;
474 }
475
476 NTSTATUS cli_list_old(struct cli_state *cli, const char *mask,
477                       uint16 attribute,
478                       NTSTATUS (*fn)(const char *, struct file_info *,
479                                  const char *, void *), void *state)
480 {
481         TALLOC_CTX *frame = talloc_stackframe();
482         struct event_context *ev;
483         struct tevent_req *req;
484         NTSTATUS status = NT_STATUS_NO_MEMORY;
485         struct file_info *finfo;
486         size_t i, num_finfo;
487
488         if (cli_has_async_calls(cli)) {
489                 /*
490                  * Can't use sync call while an async call is in flight
491                  */
492                 status = NT_STATUS_INVALID_PARAMETER;
493                 goto fail;
494         }
495         ev = event_context_init(frame);
496         if (ev == NULL) {
497                 goto fail;
498         }
499         req = cli_list_old_send(frame, ev, cli, mask, attribute);
500         if (req == NULL) {
501                 goto fail;
502         }
503         if (!tevent_req_poll(req, ev)) {
504                 status = map_nt_error_from_unix(errno);
505                 goto fail;
506         }
507         status = cli_list_old_recv(req, frame, &finfo);
508         if (!NT_STATUS_IS_OK(status)) {
509                 goto fail;
510         }
511         num_finfo = talloc_array_length(finfo);
512         for (i=0; i<num_finfo; i++) {
513                 status = fn(cli->dfs_mountpoint, &finfo[i], mask, state);
514                 if (!NT_STATUS_IS_OK(status)) {
515                         goto fail;
516                 }
517         }
518  fail:
519         TALLOC_FREE(frame);
520         return status;
521 }
522
523 struct cli_list_trans_state {
524         struct tevent_context *ev;
525         struct cli_state *cli;
526         char *mask;
527         uint16_t attribute;
528         uint16_t info_level;
529
530         int loop_count;
531         int total_received;
532         uint16_t max_matches;
533         bool first;
534
535         int ff_eos;
536         int ff_dir_handle;
537
538         uint16_t setup[1];
539         uint8_t *param;
540
541         struct file_info *finfo;
542 };
543
544 static void cli_list_trans_done(struct tevent_req *subreq);
545
546 static struct tevent_req *cli_list_trans_send(TALLOC_CTX *mem_ctx,
547                                               struct tevent_context *ev,
548                                               struct cli_state *cli,
549                                               const char *mask,
550                                               uint16_t attribute,
551                                               uint16_t info_level)
552 {
553         struct tevent_req *req, *subreq;
554         struct cli_list_trans_state *state;
555         size_t param_len;
556
557         req = tevent_req_create(mem_ctx, &state,
558                                 struct cli_list_trans_state);
559         if (req == NULL) {
560                 return NULL;
561         }
562         state->ev = ev;
563         state->cli = cli;
564         state->mask = talloc_strdup(state, mask);
565         if (tevent_req_nomem(state->mask, req)) {
566                 return tevent_req_post(req, ev);
567         }
568         state->attribute = attribute;
569         state->info_level = info_level;
570         state->loop_count = 0;
571         state->first = true;
572
573         state->max_matches = 1366; /* Match W2k */
574
575         SSVAL(&state->setup[0], 0, TRANSACT2_FINDFIRST);
576
577         state->param = talloc_array(state, uint8_t, 12);
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         state->param = trans2_bytes_push_str(state->param, cli_ucs2(cli),
591                                              state->mask, strlen(state->mask)+1,
592                                              NULL);
593         if (tevent_req_nomem(state->param, req)) {
594                 return tevent_req_post(req, ev);
595         }
596         param_len = talloc_get_size(state->param);
597
598         subreq = cli_trans_send(state, state->ev, state->cli,
599                                 SMBtrans2, NULL, -1, 0, 0,
600                                 state->setup, 1, 0,
601                                 state->param, param_len, 10,
602                                 NULL, 0, CLI_BUFFER_SIZE);
603         if (tevent_req_nomem(subreq, req)) {
604                 return tevent_req_post(req, ev);
605         }
606         tevent_req_set_callback(subreq, cli_list_trans_done, req);
607         return req;
608 }
609
610 static void cli_list_trans_done(struct tevent_req *subreq)
611 {
612         struct tevent_req *req = tevent_req_callback_data(
613                 subreq, struct tevent_req);
614         struct cli_list_trans_state *state = tevent_req_data(
615                 req, struct cli_list_trans_state);
616         NTSTATUS status;
617         uint8_t *param;
618         uint32_t num_param;
619         uint8_t *data;
620         char *data_end;
621         uint32_t num_data;
622         uint32_t min_param;
623         struct file_info *tmp;
624         size_t old_num_finfo;
625         uint16_t recv_flags2;
626         int ff_searchcount;
627         bool ff_eos;
628         char *p, *p2;
629         uint32_t resume_key = 0;
630         int i;
631         DATA_BLOB last_name_raw;
632         struct file_info *finfo = NULL;
633         size_t param_len;
634
635         min_param = (state->first ? 6 : 4);
636
637         status = cli_trans_recv(subreq, talloc_tos(), &recv_flags2,
638                                 NULL, 0, NULL,
639                                 &param, min_param, &num_param,
640                                 &data, 0, &num_data);
641         TALLOC_FREE(subreq);
642         if (!NT_STATUS_IS_OK(status)) {
643                 /*
644                  * TODO: retry, OS/2 nofiles
645                  */
646                 tevent_req_nterror(req, status);
647                 return;
648         }
649
650         if (state->first) {
651                 state->ff_dir_handle = SVAL(param, 0);
652                 ff_searchcount = SVAL(param, 2);
653                 ff_eos = SVAL(param, 4) != 0;
654         } else {
655                 ff_searchcount = SVAL(param, 0);
656                 ff_eos = SVAL(param, 2) != 0;
657         }
658
659         old_num_finfo = talloc_array_length(state->finfo);
660
661         tmp = talloc_realloc(state, state->finfo, struct file_info,
662                                    old_num_finfo + ff_searchcount);
663         if (tevent_req_nomem(tmp, req)) {
664                 return;
665         }
666         state->finfo = tmp;
667
668         p2 = p = (char *)data;
669         data_end = (char *)data + num_data;
670         last_name_raw = data_blob_null;
671
672         for (i=0; i<ff_searchcount; i++) {
673                 if (p2 >= data_end) {
674                         ff_eos = true;
675                         break;
676                 }
677                 if ((state->info_level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO)
678                     && (i == ff_searchcount-1)) {
679                         /* Last entry - fixup the last offset length. */
680                         SIVAL(p2, 0, PTR_DIFF((data + num_data), p2));
681                 }
682
683                 data_blob_free(&last_name_raw);
684
685                 finfo = &state->finfo[old_num_finfo + i];
686
687                 p2 += interpret_long_filename(
688                         state->finfo, /* Stick fname to the array as such */
689                         state->cli, state->info_level,
690                         (char *)data, recv_flags2, p2,
691                         data_end, finfo, &resume_key, &last_name_raw);
692
693                 if (finfo->name == NULL) {
694                         DEBUG(1, ("cli_list: Error: unable to parse name from "
695                                   "info level %d\n", state->info_level));
696                         ff_eos = true;
697                         break;
698                 }
699                 if (!state->first && (state->mask[0] != '\0') &&
700                     strcsequal(finfo->name, state->mask)) {
701                         DEBUG(1, ("Error: Looping in FIND_NEXT as name %s has "
702                                   "already been seen?\n", finfo->name));
703                         ff_eos = true;
704                         break;
705                 }
706         }
707
708         if (ff_searchcount == 0) {
709                 ff_eos = true;
710         }
711
712         TALLOC_FREE(param);
713         TALLOC_FREE(data);
714
715         /*
716          * Shrink state->finfo to the real length we received
717          */
718         tmp = talloc_realloc(state, state->finfo, struct file_info,
719                                    old_num_finfo + i);
720         if (tevent_req_nomem(tmp, req)) {
721                 return;
722         }
723         state->finfo = tmp;
724
725         state->first = false;
726
727         if (ff_eos) {
728                 data_blob_free(&last_name_raw);
729                 tevent_req_done(req);
730                 return;
731         }
732
733         TALLOC_FREE(state->mask);
734         state->mask = talloc_strdup(state, finfo->name);
735         if (tevent_req_nomem(state->mask, req)) {
736                 return;
737         }
738
739         SSVAL(&state->setup[0], 0, TRANSACT2_FINDNEXT);
740
741         param = talloc_realloc(state, state->param, uint8_t, 12);
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         if (last_name_raw.length) {
763                 state->param = trans2_bytes_push_bytes(state->param,
764                                                        last_name_raw.data,
765                                                        last_name_raw.length);
766                 if (tevent_req_nomem(state->param, req)) {
767                         return;
768                 }
769                 data_blob_free(&last_name_raw);
770         } else {
771                 state->param = trans2_bytes_push_str(state->param,
772                                                      cli_ucs2(state->cli),
773                                                      state->mask,
774                                                      strlen(state->mask)+1,
775                                                      NULL);
776                 if (tevent_req_nomem(state->param, req)) {
777                         return;
778                 }
779         }
780         param_len = talloc_get_size(state->param);
781
782         subreq = cli_trans_send(state, state->ev, state->cli,
783                                 SMBtrans2, NULL, -1, 0, 0,
784                                 state->setup, 1, 0,
785                                 state->param, param_len, 10,
786                                 NULL, 0, CLI_BUFFER_SIZE);
787         if (tevent_req_nomem(subreq, req)) {
788                 return;
789         }
790         tevent_req_set_callback(subreq, cli_list_trans_done, req);
791 }
792
793 static NTSTATUS cli_list_trans_recv(struct tevent_req *req,
794                                     TALLOC_CTX *mem_ctx,
795                                     struct file_info **finfo)
796 {
797         struct cli_list_trans_state *state = tevent_req_data(
798                 req, struct cli_list_trans_state);
799         NTSTATUS status;
800
801         if (tevent_req_is_nterror(req, &status)) {
802                 return status;
803         }
804         *finfo = talloc_move(mem_ctx, &state->finfo);
805         return NT_STATUS_OK;
806 }
807
808 NTSTATUS cli_list_trans(struct cli_state *cli, const char *mask,
809                         uint16_t attribute, int info_level,
810                         NTSTATUS (*fn)(const char *mnt, struct file_info *finfo,
811                                    const char *mask, void *private_data),
812                         void *private_data)
813 {
814         TALLOC_CTX *frame = talloc_stackframe();
815         struct event_context *ev;
816         struct tevent_req *req;
817         int i, num_finfo;
818         struct file_info *finfo = NULL;
819         NTSTATUS status = NT_STATUS_NO_MEMORY;
820
821         if (cli_has_async_calls(cli)) {
822                 /*
823                  * Can't use sync call while an async call is in flight
824                  */
825                 status = NT_STATUS_INVALID_PARAMETER;
826                 goto fail;
827         }
828         ev = event_context_init(frame);
829         if (ev == NULL) {
830                 goto fail;
831         }
832         req = cli_list_trans_send(frame, ev, cli, mask, attribute, info_level);
833         if (req == NULL) {
834                 goto fail;
835         }
836         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
837                 goto fail;
838         }
839         status = cli_list_trans_recv(req, frame, &finfo);
840         if (!NT_STATUS_IS_OK(status)) {
841                 goto fail;
842         }
843         num_finfo = talloc_array_length(finfo);
844         for (i=0; i<num_finfo; i++) {
845                 status = fn(cli->dfs_mountpoint, &finfo[i], mask, private_data);
846                 if (!NT_STATUS_IS_OK(status)) {
847                         goto fail;
848                 }
849         }
850  fail:
851         TALLOC_FREE(frame);
852         return status;
853 }
854
855 struct cli_list_state {
856         NTSTATUS (*recv_fn)(struct tevent_req *req, TALLOC_CTX *mem_ctx,
857                             struct file_info **finfo);
858         struct file_info *finfo;
859 };
860
861 static void cli_list_done(struct tevent_req *subreq);
862
863 struct tevent_req *cli_list_send(TALLOC_CTX *mem_ctx,
864                                  struct tevent_context *ev,
865                                  struct cli_state *cli,
866                                  const char *mask,
867                                  uint16_t attribute,
868                                  uint16_t info_level)
869 {
870         struct tevent_req *req, *subreq;
871         struct cli_list_state *state;
872
873         req = tevent_req_create(mem_ctx, &state, struct cli_list_state);
874         if (req == NULL) {
875                 return NULL;
876         }
877
878         if (cli_state_protocol(cli) <= PROTOCOL_LANMAN1) {
879                 subreq = cli_list_old_send(state, ev, cli, mask, attribute);
880                 state->recv_fn = cli_list_old_recv;
881         } else {
882                 subreq = cli_list_trans_send(state, ev, cli, mask, attribute,
883                                              info_level);
884                 state->recv_fn = cli_list_trans_recv;
885         }
886         if (tevent_req_nomem(subreq, req)) {
887                 return tevent_req_post(req, ev);
888         }
889         tevent_req_set_callback(subreq, cli_list_done, req);
890         return req;
891 }
892
893 static void cli_list_done(struct tevent_req *subreq)
894 {
895         struct tevent_req *req = tevent_req_callback_data(
896                 subreq, struct tevent_req);
897         struct cli_list_state *state = tevent_req_data(
898                 req, struct cli_list_state);
899         NTSTATUS status;
900
901         status = state->recv_fn(subreq, state, &state->finfo);
902         TALLOC_FREE(subreq);
903         if (!NT_STATUS_IS_OK(status)) {
904                 tevent_req_nterror(req, status);
905                 return;
906         }
907         tevent_req_done(req);
908 }
909
910 NTSTATUS cli_list_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
911                        struct file_info **finfo, size_t *num_finfo)
912 {
913         struct cli_list_state *state = tevent_req_data(
914                 req, struct cli_list_state);
915         NTSTATUS status;
916
917         if (tevent_req_is_nterror(req, &status)) {
918                 return status;
919         }
920         *num_finfo = talloc_array_length(state->finfo);
921         *finfo = talloc_move(mem_ctx, &state->finfo);
922         return NT_STATUS_OK;
923 }
924
925 NTSTATUS cli_list(struct cli_state *cli, const char *mask, uint16 attribute,
926                   NTSTATUS (*fn)(const char *, struct file_info *, const char *,
927                              void *), void *state)
928 {
929         TALLOC_CTX *frame = talloc_stackframe();
930         struct event_context *ev;
931         struct tevent_req *req;
932         NTSTATUS status = NT_STATUS_NO_MEMORY;
933         struct file_info *finfo;
934         size_t i, num_finfo;
935         uint16_t info_level;
936
937         if (cli_has_async_calls(cli)) {
938                 /*
939                  * Can't use sync call while an async call is in flight
940                  */
941                 status = NT_STATUS_INVALID_PARAMETER;
942                 goto fail;
943         }
944         ev = event_context_init(frame);
945         if (ev == NULL) {
946                 goto fail;
947         }
948
949         info_level = (cli_state_capabilities(cli) & CAP_NT_SMBS)
950                 ? SMB_FIND_FILE_BOTH_DIRECTORY_INFO : SMB_FIND_INFO_STANDARD;
951
952         req = cli_list_send(frame, ev, cli, mask, attribute, info_level);
953         if (req == NULL) {
954                 goto fail;
955         }
956         if (!tevent_req_poll(req, ev)) {
957                 status = map_nt_error_from_unix(errno);
958                 goto fail;
959         }
960
961         status = cli_list_recv(req, frame, &finfo, &num_finfo);
962         if (!NT_STATUS_IS_OK(status)) {
963                 goto fail;
964         }
965
966         for (i=0; i<num_finfo; i++) {
967                 status = fn(cli->dfs_mountpoint, &finfo[i], mask, state);
968                 if (!NT_STATUS_IS_OK(status)) {
969                         goto fail;
970                 }
971         }
972  fail:
973         TALLOC_FREE(frame);
974         return status;
975 }