Merge branch 'v4-0-stable' into newmaster
[obnox/samba/samba-obnox.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                       void (*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                 fn(cli->dfs_mountpoint, &finfo[i], mask, state);
508         }
509  fail:
510         TALLOC_FREE(frame);
511         if (!NT_STATUS_IS_OK(status)) {
512                 cli_set_error(cli, status);
513         }
514         return status;
515 }
516
517 struct cli_list_trans_state {
518         struct tevent_context *ev;
519         struct cli_state *cli;
520         char *mask;
521         uint16_t attribute;
522         uint16_t info_level;
523
524         int loop_count;
525         int total_received;
526         uint16_t max_matches;
527         bool first;
528
529         int ff_eos;
530         int ff_dir_handle;
531
532         uint16_t setup[1];
533         uint8_t *param;
534
535         struct file_info *finfo;
536 };
537
538 static void cli_list_trans_done(struct tevent_req *subreq);
539
540 static struct tevent_req *cli_list_trans_send(TALLOC_CTX *mem_ctx,
541                                               struct tevent_context *ev,
542                                               struct cli_state *cli,
543                                               const char *mask,
544                                               uint16_t attribute,
545                                               uint16_t info_level)
546 {
547         struct tevent_req *req, *subreq;
548         struct cli_list_trans_state *state;
549         size_t nlen, param_len;
550         char *p;
551
552         req = tevent_req_create(mem_ctx, &state,
553                                 struct cli_list_trans_state);
554         if (req == NULL) {
555                 return NULL;
556         }
557         state->ev = ev;
558         state->cli = cli;
559         state->mask = talloc_strdup(state, mask);
560         if (tevent_req_nomem(state->mask, req)) {
561                 return tevent_req_post(req, ev);
562         }
563         state->attribute = attribute;
564         state->info_level = info_level;
565         state->loop_count = 0;
566         state->first = true;
567
568         state->max_matches = 1366; /* Match W2k */
569
570         state->setup[0] = TRANSACT2_FINDFIRST;
571
572         nlen = 2*(strlen(mask)+1);
573         state->param = TALLOC_ARRAY(state, uint8_t, 12+nlen+2);
574         if (tevent_req_nomem(state->param, req)) {
575                 return tevent_req_post(req, ev);
576         }
577
578         SSVAL(state->param, 0, state->attribute);
579         SSVAL(state->param, 2, state->max_matches);
580         SSVAL(state->param, 4,
581               FLAG_TRANS2_FIND_REQUIRE_RESUME
582               |FLAG_TRANS2_FIND_CLOSE_IF_END);
583         SSVAL(state->param, 6, state->info_level);
584         SIVAL(state->param, 8, 0);
585
586         p = ((char *)state->param)+12;
587         p += clistr_push(state->cli, p, state->mask, nlen,
588                          STR_TERMINATE);
589         param_len = PTR_DIFF(p, state->param);
590
591         subreq = cli_trans_send(state, state->ev, state->cli,
592                                 SMBtrans2, NULL, -1, 0, 0,
593                                 state->setup, 1, 0,
594                                 state->param, param_len, 10,
595                                 NULL, 0, cli->max_xmit);
596         if (tevent_req_nomem(subreq, req)) {
597                 return tevent_req_post(req, ev);
598         }
599         tevent_req_set_callback(subreq, cli_list_trans_done, req);
600         return req;
601 }
602
603 static void cli_list_trans_done(struct tevent_req *subreq)
604 {
605         struct tevent_req *req = tevent_req_callback_data(
606                 subreq, struct tevent_req);
607         struct cli_list_trans_state *state = tevent_req_data(
608                 req, struct cli_list_trans_state);
609         NTSTATUS status;
610         uint8_t *param;
611         uint32_t num_param;
612         uint8_t *data;
613         char *data_end;
614         uint32_t num_data;
615         uint32_t min_param;
616         struct file_info *tmp;
617         size_t old_num_finfo;
618         uint16_t recv_flags2;
619         int ff_searchcount;
620         bool ff_eos;
621         char *p, *p2;
622         uint32_t resume_key = 0;
623         int i;
624         DATA_BLOB last_name_raw;
625         struct file_info *finfo = NULL;
626         size_t nlen, param_len;
627
628         min_param = (state->first ? 6 : 4);
629
630         status = cli_trans_recv(subreq, talloc_tos(), &recv_flags2,
631                                 NULL, 0, NULL,
632                                 &param, min_param, &num_param,
633                                 &data, 0, &num_data);
634         TALLOC_FREE(subreq);
635         if (!NT_STATUS_IS_OK(status)) {
636                 /*
637                  * TODO: retry, OS/2 nofiles
638                  */
639                 tevent_req_nterror(req, status);
640                 return;
641         }
642
643         if (state->first) {
644                 state->ff_dir_handle = SVAL(param, 0);
645                 ff_searchcount = SVAL(param, 2);
646                 ff_eos = SVAL(param, 4) != 0;
647         } else {
648                 ff_searchcount = SVAL(param, 0);
649                 ff_eos = SVAL(param, 2) != 0;
650         }
651
652         old_num_finfo = talloc_array_length(state->finfo);
653
654         tmp = TALLOC_REALLOC_ARRAY(state, state->finfo, struct file_info,
655                                    old_num_finfo + ff_searchcount);
656         if (tevent_req_nomem(tmp, req)) {
657                 return;
658         }
659         state->finfo = tmp;
660
661         p2 = p = (char *)data;
662         data_end = (char *)data + num_data;
663         last_name_raw = data_blob_null;
664
665         for (i=0; i<ff_searchcount; i++) {
666                 if (p2 >= data_end) {
667                         ff_eos = true;
668                         break;
669                 }
670                 if ((state->info_level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO)
671                     && (i == ff_searchcount-1)) {
672                         /* Last entry - fixup the last offset length. */
673                         SIVAL(p2, 0, PTR_DIFF((data + num_data), p2));
674                 }
675
676                 data_blob_free(&last_name_raw);
677
678                 finfo = &state->finfo[old_num_finfo + i];
679
680                 p2 += interpret_long_filename(
681                         state->finfo, /* Stick fname to the array as such */
682                         state->cli, state->info_level,
683                         (char *)data, recv_flags2, p2,
684                         data_end, finfo, &resume_key, &last_name_raw);
685
686                 if (finfo->name == NULL) {
687                         DEBUG(1, ("cli_list: Error: unable to parse name from "
688                                   "info level %d\n", state->info_level));
689                         ff_eos = true;
690                         break;
691                 }
692                 if (!state->first && (state->mask[0] != '\0') &&
693                     strcsequal(finfo->name, state->mask)) {
694                         DEBUG(1, ("Error: Looping in FIND_NEXT as name %s has "
695                                   "already been seen?\n", finfo->name));
696                         ff_eos = true;
697                         break;
698                 }
699         }
700
701         if (ff_searchcount == 0) {
702                 ff_eos = true;
703         }
704
705         TALLOC_FREE(param);
706         TALLOC_FREE(data);
707
708         /*
709          * Shrink state->finfo to the real length we received
710          */
711         tmp = TALLOC_REALLOC_ARRAY(state, state->finfo, struct file_info,
712                                    old_num_finfo + i);
713         if (tevent_req_nomem(tmp, req)) {
714                 return;
715         }
716         state->finfo = tmp;
717
718         state->first = false;
719
720         if (ff_eos) {
721                 data_blob_free(&last_name_raw);
722                 tevent_req_done(req);
723                 return;
724         }
725
726         TALLOC_FREE(state->mask);
727         state->mask = talloc_strdup(state, finfo->name);
728         if (tevent_req_nomem(state->mask, req)) {
729                 return;
730         }
731
732         state->setup[0] = TRANSACT2_FINDNEXT;
733
734         nlen = 2*(strlen(state->mask) + 1);
735
736         param = TALLOC_REALLOC_ARRAY(state, state->param, uint8_t,
737                                      12 + nlen + last_name_raw.length + 2);
738         if (tevent_req_nomem(param, req)) {
739                 return;
740         }
741         state->param = param;
742
743         SSVAL(param, 0, state->ff_dir_handle);
744         SSVAL(param, 2, state->max_matches); /* max count */
745         SSVAL(param, 4, state->info_level);
746         /*
747          * For W2K servers serving out FAT filesystems we *must* set
748          * the resume key. If it's not FAT then it's returned as zero.
749          */
750         SIVAL(param, 6, resume_key); /* ff_resume_key */
751         /*
752          * NB. *DON'T* use continue here. If you do it seems that W2K
753          * and bretheren can miss filenames. Use last filename
754          * continue instead. JRA
755          */
756         SSVAL(param, 10, (FLAG_TRANS2_FIND_REQUIRE_RESUME
757                           |FLAG_TRANS2_FIND_CLOSE_IF_END));
758         p = ((char *)param)+12;
759         if (last_name_raw.length) {
760                 memcpy(p, last_name_raw.data, last_name_raw.length);
761                 p += last_name_raw.length;
762                 data_blob_free(&last_name_raw);
763         } else {
764                 p += clistr_push(state->cli, p, state->mask, nlen,
765                                  STR_TERMINATE);
766         }
767
768         param_len = PTR_DIFF(p, param);
769
770         subreq = cli_trans_send(state, state->ev, state->cli,
771                                 SMBtrans2, NULL, -1, 0, 0,
772                                 state->setup, 1, 0,
773                                 state->param, param_len, 10,
774                                 NULL, 0, state->cli->max_xmit);
775         if (tevent_req_nomem(subreq, req)) {
776                 return;
777         }
778         tevent_req_set_callback(subreq, cli_list_trans_done, req);
779 }
780
781 static NTSTATUS cli_list_trans_recv(struct tevent_req *req,
782                                     TALLOC_CTX *mem_ctx,
783                                     struct file_info **finfo)
784 {
785         struct cli_list_trans_state *state = tevent_req_data(
786                 req, struct cli_list_trans_state);
787         NTSTATUS status;
788
789         if (tevent_req_is_nterror(req, &status)) {
790                 return status;
791         }
792         *finfo = talloc_move(mem_ctx, &state->finfo);
793         return NT_STATUS_OK;
794 }
795
796 NTSTATUS cli_list_trans(struct cli_state *cli, const char *mask,
797                         uint16_t attribute, int info_level,
798                         void (*fn)(const char *mnt, struct file_info *finfo,
799                                    const char *mask, void *private_data),
800                         void *private_data)
801 {
802         TALLOC_CTX *frame = talloc_stackframe();
803         struct event_context *ev;
804         struct tevent_req *req;
805         int i, num_finfo;
806         struct file_info *finfo = NULL;
807         NTSTATUS status = NT_STATUS_NO_MEMORY;
808
809         if (cli_has_async_calls(cli)) {
810                 /*
811                  * Can't use sync call while an async call is in flight
812                  */
813                 status = NT_STATUS_INVALID_PARAMETER;
814                 goto fail;
815         }
816         ev = event_context_init(frame);
817         if (ev == NULL) {
818                 goto fail;
819         }
820         req = cli_list_trans_send(frame, ev, cli, mask, attribute, info_level);
821         if (req == NULL) {
822                 goto fail;
823         }
824         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
825                 goto fail;
826         }
827         status = cli_list_trans_recv(req, frame, &finfo);
828         if (!NT_STATUS_IS_OK(status)) {
829                 goto fail;
830         }
831         num_finfo = talloc_array_length(finfo);
832         for (i=0; i<num_finfo; i++) {
833                 fn(cli->dfs_mountpoint, &finfo[i], mask, private_data);
834         }
835  fail:
836         TALLOC_FREE(frame);
837         if (!NT_STATUS_IS_OK(status)) {
838                 cli_set_error(cli, status);
839         }
840         return status;
841 }
842
843 struct cli_list_state {
844         NTSTATUS (*recv_fn)(struct tevent_req *req, TALLOC_CTX *mem_ctx,
845                             struct file_info **finfo);
846         struct file_info *finfo;
847 };
848
849 static void cli_list_done(struct tevent_req *subreq);
850
851 struct tevent_req *cli_list_send(TALLOC_CTX *mem_ctx,
852                                  struct tevent_context *ev,
853                                  struct cli_state *cli,
854                                  const char *mask,
855                                  uint16_t attribute,
856                                  uint16_t info_level)
857 {
858         struct tevent_req *req, *subreq;
859         struct cli_list_state *state;
860
861         req = tevent_req_create(mem_ctx, &state, struct cli_list_state);
862         if (req == NULL) {
863                 return NULL;
864         }
865
866         if (cli->protocol <= PROTOCOL_LANMAN1) {
867                 subreq = cli_list_old_send(state, ev, cli, mask, attribute);
868                 state->recv_fn = cli_list_old_recv;
869         } else {
870                 subreq = cli_list_trans_send(state, ev, cli, mask, attribute,
871                                              info_level);
872                 state->recv_fn = cli_list_trans_recv;
873         }
874         if (tevent_req_nomem(subreq, req)) {
875                 return tevent_req_post(req, ev);
876         }
877         tevent_req_set_callback(subreq, cli_list_done, req);
878         return req;
879 }
880
881 static void cli_list_done(struct tevent_req *subreq)
882 {
883         struct tevent_req *req = tevent_req_callback_data(
884                 subreq, struct tevent_req);
885         struct cli_list_state *state = tevent_req_data(
886                 req, struct cli_list_state);
887         NTSTATUS status;
888
889         status = state->recv_fn(subreq, state, &state->finfo);
890         TALLOC_FREE(subreq);
891         if (!NT_STATUS_IS_OK(status)) {
892                 tevent_req_nterror(req, status);
893                 return;
894         }
895         tevent_req_done(req);
896 }
897
898 NTSTATUS cli_list_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
899                        struct file_info **finfo, size_t *num_finfo)
900 {
901         struct cli_list_state *state = tevent_req_data(
902                 req, struct cli_list_state);
903         NTSTATUS status;
904
905         if (tevent_req_is_nterror(req, &status)) {
906                 return status;
907         }
908         *num_finfo = talloc_array_length(state->finfo);
909         *finfo = talloc_move(mem_ctx, &state->finfo);
910         return NT_STATUS_OK;
911 }
912
913 NTSTATUS cli_list(struct cli_state *cli, const char *mask, uint16 attribute,
914                   void (*fn)(const char *, struct file_info *, const char *,
915                              void *), void *state)
916 {
917         TALLOC_CTX *frame = talloc_stackframe();
918         struct event_context *ev;
919         struct tevent_req *req;
920         NTSTATUS status = NT_STATUS_NO_MEMORY;
921         struct file_info *finfo;
922         size_t i, num_finfo;
923         uint16_t info_level;
924
925         if (cli_has_async_calls(cli)) {
926                 /*
927                  * Can't use sync call while an async call is in flight
928                  */
929                 status = NT_STATUS_INVALID_PARAMETER;
930                 goto fail;
931         }
932         ev = event_context_init(frame);
933         if (ev == NULL) {
934                 goto fail;
935         }
936
937         info_level = (cli->capabilities & CAP_NT_SMBS)
938                 ? SMB_FIND_FILE_BOTH_DIRECTORY_INFO : SMB_FIND_INFO_STANDARD;
939
940         req = cli_list_send(frame, ev, cli, mask, attribute, info_level);
941         if (req == NULL) {
942                 goto fail;
943         }
944         if (!tevent_req_poll(req, ev)) {
945                 status = map_nt_error_from_unix(errno);
946                 goto fail;
947         }
948
949         status = cli_list_recv(req, frame, &finfo, &num_finfo);
950         if (!NT_STATUS_IS_OK(status)) {
951                 goto fail;
952         }
953
954         for (i=0; i<num_finfo; i++) {
955                 fn(cli->dfs_mountpoint, &finfo[i], mask, state);
956         }
957  fail:
958         TALLOC_FREE(frame);
959         if (!NT_STATUS_IS_OK(status)) {
960                 cli_set_error(cli, status);
961         }
962         return status;
963 }