s3:libsmb: get rid of cli_state_server_time_zone
[mat/samba.git] / source3 / libsmb / clilist.c
1 /*
2    Unix SMB/CIFS implementation.
3    client directory list routines
4    Copyright (C) Andrew Tridgell 1994-1998
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "includes.h"
21 #include "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, smb1cli_conn_server_time_zone(cli->conn)));
81                         finfo->atime_ts = convert_time_t_to_timespec(
82                                 make_unix_date2(p+8, smb1cli_conn_server_time_zone(cli->conn)));
83                         finfo->mtime_ts = convert_time_t_to_timespec(
84                                 make_unix_date2(p+12, smb1cli_conn_server_time_zone(cli->conn)));
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, smb1cli_conn_server_time_zone(cli->conn)));
130                         finfo->atime_ts = convert_time_t_to_timespec(
131                                 make_unix_date2(p+8, smb1cli_conn_server_time_zone(cli->conn)));
132                         finfo->mtime_ts = convert_time_t_to_timespec(
133                                 make_unix_date2(p+12, smb1cli_conn_server_time_zone(cli->conn)));
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, smb1cli_conn_server_time_zone(cli->conn));
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         uint32_t usable_space;
302
303         req = tevent_req_create(mem_ctx, &state, struct cli_list_old_state);
304         if (req == NULL) {
305                 return NULL;
306         }
307         state->ev = ev;
308         state->cli = cli;
309         state->attribute = attribute;
310         state->first = true;
311         state->mask = talloc_strdup(state, mask);
312         if (tevent_req_nomem(state->mask, req)) {
313                 return tevent_req_post(req, ev);
314         }
315         usable_space = cli_state_available_size(cli, 100);
316         state->num_asked = usable_space / DIR_STRUCT_SIZE;
317
318         SSVAL(state->vwv + 0, 0, state->num_asked);
319         SSVAL(state->vwv + 1, 0, state->attribute);
320
321         bytes = talloc_array(state, uint8_t, 1);
322         if (tevent_req_nomem(bytes, req)) {
323                 return tevent_req_post(req, ev);
324         }
325         bytes[0] = 4;
326         bytes = smb_bytes_push_str(bytes, cli_ucs2(cli), mask,
327                                    strlen(mask)+1, NULL);
328
329         bytes = smb_bytes_push_bytes(bytes, 5, (const uint8_t *)&zero, 2);
330         if (tevent_req_nomem(bytes, req)) {
331                 return tevent_req_post(req, ev);
332         }
333
334         subreq = cli_smb_send(state, state->ev, state->cli, SMBsearch,
335                               0, 2, state->vwv, talloc_get_size(bytes), bytes);
336         if (tevent_req_nomem(subreq, req)) {
337                 return tevent_req_post(req, ev);
338         }
339         tevent_req_set_callback(subreq, cli_list_old_done, req);
340         return req;
341 }
342
343 static void cli_list_old_done(struct tevent_req *subreq)
344 {
345         struct tevent_req *req = tevent_req_callback_data(
346                 subreq, struct tevent_req);
347         struct cli_list_old_state *state = tevent_req_data(
348                 req, struct cli_list_old_state);
349         NTSTATUS status;
350         uint8_t cmd;
351         uint8_t wct;
352         uint16_t *vwv;
353         uint32_t num_bytes;
354         uint8_t *bytes;
355         uint16_t received;
356         size_t dirlist_len;
357         uint8_t *tmp;
358
359         status = cli_smb_recv(subreq, state, NULL, 0, &wct, &vwv, &num_bytes,
360                               &bytes);
361         if (!NT_STATUS_IS_OK(status)
362             && !NT_STATUS_EQUAL(status, NT_STATUS_DOS(ERRDOS, ERRnofiles))
363             && !NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
364                 TALLOC_FREE(subreq);
365                 tevent_req_nterror(req, status);
366                 return;
367         }
368         if (NT_STATUS_EQUAL(status, NT_STATUS_DOS(ERRDOS, ERRnofiles))
369             || NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
370                 received = 0;
371         } else {
372                 if (wct < 1) {
373                         TALLOC_FREE(subreq);
374                         tevent_req_nterror(
375                                 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
376                         return;
377                 }
378                 received = SVAL(vwv + 0, 0);
379         }
380
381         if (received > 0) {
382                 /*
383                  * I don't think this can wrap. received is
384                  * initialized from a 16-bit value.
385                  */
386                 if (num_bytes < (received * DIR_STRUCT_SIZE + 3)) {
387                         TALLOC_FREE(subreq);
388                         tevent_req_nterror(
389                                 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
390                         return;
391                 }
392
393                 dirlist_len = talloc_get_size(state->dirlist);
394
395                 tmp = talloc_realloc(
396                         state, state->dirlist, uint8_t,
397                         dirlist_len + received * DIR_STRUCT_SIZE);
398                 if (tevent_req_nomem(tmp, req)) {
399                         return;
400                 }
401                 state->dirlist = tmp;
402                 memcpy(state->dirlist + dirlist_len, bytes + 3,
403                        received * DIR_STRUCT_SIZE);
404
405                 SSVAL(state->search_status, 0, 21);
406                 memcpy(state->search_status + 2,
407                        bytes + 3 + (received-1)*DIR_STRUCT_SIZE, 21);
408                 cmd = SMBsearch;
409         } else {
410                 if (state->first || state->done) {
411                         tevent_req_done(req);
412                         return;
413                 }
414                 state->done = true;
415                 state->num_asked = 0;
416                 cmd = SMBfclose;
417         }
418         TALLOC_FREE(subreq);
419
420         state->first = false;
421
422         SSVAL(state->vwv + 0, 0, state->num_asked);
423         SSVAL(state->vwv + 1, 0, state->attribute);
424
425         bytes = talloc_array(state, uint8_t, 1);
426         if (tevent_req_nomem(bytes, req)) {
427                 return;
428         }
429         bytes[0] = 4;
430         bytes = smb_bytes_push_str(bytes, cli_ucs2(state->cli), "",
431                                    1, NULL);
432         bytes = smb_bytes_push_bytes(bytes, 5, state->search_status,
433                                      sizeof(state->search_status));
434         if (tevent_req_nomem(bytes, req)) {
435                 return;
436         }
437         subreq = cli_smb_send(state, state->ev, state->cli, cmd, 0,
438                               2, state->vwv, talloc_get_size(bytes), bytes);
439         if (tevent_req_nomem(subreq, req)) {
440                 return;
441         }
442         tevent_req_set_callback(subreq, cli_list_old_done, req);
443 }
444
445 static NTSTATUS cli_list_old_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
446                                   struct file_info **pfinfo)
447 {
448         struct cli_list_old_state *state = tevent_req_data(
449                 req, struct cli_list_old_state);
450         NTSTATUS status;
451         size_t i, num_received;
452         struct file_info *finfo;
453
454         if (tevent_req_is_nterror(req, &status)) {
455                 return status;
456         }
457
458         num_received = talloc_array_length(state->dirlist) / DIR_STRUCT_SIZE;
459
460         finfo = talloc_array(mem_ctx, struct file_info, num_received);
461         if (finfo == NULL) {
462                 return NT_STATUS_NO_MEMORY;
463         }
464
465         for (i=0; i<num_received; i++) {
466                 if (!interpret_short_filename(
467                             finfo, state->cli,
468                             (char *)state->dirlist + i * DIR_STRUCT_SIZE,
469                             &finfo[i])) {
470                         TALLOC_FREE(finfo);
471                         return NT_STATUS_NO_MEMORY;
472                 }
473         }
474         *pfinfo = finfo;
475         return NT_STATUS_OK;
476 }
477
478 NTSTATUS cli_list_old(struct cli_state *cli, const char *mask,
479                       uint16 attribute,
480                       NTSTATUS (*fn)(const char *, struct file_info *,
481                                  const char *, void *), void *state)
482 {
483         TALLOC_CTX *frame = talloc_stackframe();
484         struct event_context *ev;
485         struct tevent_req *req;
486         NTSTATUS status = NT_STATUS_NO_MEMORY;
487         struct file_info *finfo;
488         size_t i, num_finfo;
489
490         if (cli_has_async_calls(cli)) {
491                 /*
492                  * Can't use sync call while an async call is in flight
493                  */
494                 status = NT_STATUS_INVALID_PARAMETER;
495                 goto fail;
496         }
497         ev = event_context_init(frame);
498         if (ev == NULL) {
499                 goto fail;
500         }
501         req = cli_list_old_send(frame, ev, cli, mask, attribute);
502         if (req == NULL) {
503                 goto fail;
504         }
505         if (!tevent_req_poll(req, ev)) {
506                 status = map_nt_error_from_unix(errno);
507                 goto fail;
508         }
509         status = cli_list_old_recv(req, frame, &finfo);
510         if (!NT_STATUS_IS_OK(status)) {
511                 goto fail;
512         }
513         num_finfo = talloc_array_length(finfo);
514         for (i=0; i<num_finfo; i++) {
515                 status = fn(cli->dfs_mountpoint, &finfo[i], mask, state);
516                 if (!NT_STATUS_IS_OK(status)) {
517                         goto fail;
518                 }
519         }
520  fail:
521         TALLOC_FREE(frame);
522         return status;
523 }
524
525 struct cli_list_trans_state {
526         struct tevent_context *ev;
527         struct cli_state *cli;
528         char *mask;
529         uint16_t attribute;
530         uint16_t info_level;
531
532         int loop_count;
533         int total_received;
534         uint16_t max_matches;
535         bool first;
536
537         int ff_eos;
538         int ff_dir_handle;
539
540         uint16_t setup[1];
541         uint8_t *param;
542
543         struct file_info *finfo;
544 };
545
546 static void cli_list_trans_done(struct tevent_req *subreq);
547
548 static struct tevent_req *cli_list_trans_send(TALLOC_CTX *mem_ctx,
549                                               struct tevent_context *ev,
550                                               struct cli_state *cli,
551                                               const char *mask,
552                                               uint16_t attribute,
553                                               uint16_t info_level)
554 {
555         struct tevent_req *req, *subreq;
556         struct cli_list_trans_state *state;
557         size_t param_len;
558
559         req = tevent_req_create(mem_ctx, &state,
560                                 struct cli_list_trans_state);
561         if (req == NULL) {
562                 return NULL;
563         }
564         state->ev = ev;
565         state->cli = cli;
566         state->mask = talloc_strdup(state, mask);
567         if (tevent_req_nomem(state->mask, req)) {
568                 return tevent_req_post(req, ev);
569         }
570         state->attribute = attribute;
571         state->info_level = info_level;
572         state->loop_count = 0;
573         state->first = true;
574
575         state->max_matches = 1366; /* Match W2k */
576
577         SSVAL(&state->setup[0], 0, TRANSACT2_FINDFIRST);
578
579         state->param = talloc_array(state, uint8_t, 12);
580         if (tevent_req_nomem(state->param, req)) {
581                 return tevent_req_post(req, ev);
582         }
583
584         SSVAL(state->param, 0, state->attribute);
585         SSVAL(state->param, 2, state->max_matches);
586         SSVAL(state->param, 4,
587               FLAG_TRANS2_FIND_REQUIRE_RESUME
588               |FLAG_TRANS2_FIND_CLOSE_IF_END
589               |(cli->backup_intent ? FLAG_TRANS2_FIND_BACKUP_INTENT : 0));
590         SSVAL(state->param, 6, state->info_level);
591         SIVAL(state->param, 8, 0);
592
593         state->param = trans2_bytes_push_str(state->param, cli_ucs2(cli),
594                                              state->mask, strlen(state->mask)+1,
595                                              NULL);
596         if (tevent_req_nomem(state->param, req)) {
597                 return tevent_req_post(req, ev);
598         }
599         param_len = talloc_get_size(state->param);
600
601         subreq = cli_trans_send(state, state->ev, state->cli,
602                                 SMBtrans2, NULL, -1, 0, 0,
603                                 state->setup, 1, 0,
604                                 state->param, param_len, 10,
605                                 NULL, 0, CLI_BUFFER_SIZE);
606         if (tevent_req_nomem(subreq, req)) {
607                 return tevent_req_post(req, ev);
608         }
609         tevent_req_set_callback(subreq, cli_list_trans_done, req);
610         return req;
611 }
612
613 static void cli_list_trans_done(struct tevent_req *subreq)
614 {
615         struct tevent_req *req = tevent_req_callback_data(
616                 subreq, struct tevent_req);
617         struct cli_list_trans_state *state = tevent_req_data(
618                 req, struct cli_list_trans_state);
619         NTSTATUS status;
620         uint8_t *param;
621         uint32_t num_param;
622         uint8_t *data;
623         char *data_end;
624         uint32_t num_data;
625         uint32_t min_param;
626         struct file_info *tmp;
627         size_t old_num_finfo;
628         uint16_t recv_flags2;
629         int ff_searchcount;
630         bool ff_eos;
631         char *p, *p2;
632         uint32_t resume_key = 0;
633         int i;
634         DATA_BLOB last_name_raw;
635         struct file_info *finfo = NULL;
636         size_t param_len;
637
638         min_param = (state->first ? 6 : 4);
639
640         status = cli_trans_recv(subreq, talloc_tos(), &recv_flags2,
641                                 NULL, 0, NULL,
642                                 &param, min_param, &num_param,
643                                 &data, 0, &num_data);
644         TALLOC_FREE(subreq);
645         if (!NT_STATUS_IS_OK(status)) {
646                 /*
647                  * TODO: retry, OS/2 nofiles
648                  */
649                 tevent_req_nterror(req, status);
650                 return;
651         }
652
653         if (state->first) {
654                 state->ff_dir_handle = SVAL(param, 0);
655                 ff_searchcount = SVAL(param, 2);
656                 ff_eos = SVAL(param, 4) != 0;
657         } else {
658                 ff_searchcount = SVAL(param, 0);
659                 ff_eos = SVAL(param, 2) != 0;
660         }
661
662         old_num_finfo = talloc_array_length(state->finfo);
663
664         tmp = talloc_realloc(state, state->finfo, struct file_info,
665                                    old_num_finfo + ff_searchcount);
666         if (tevent_req_nomem(tmp, req)) {
667                 return;
668         }
669         state->finfo = tmp;
670
671         p2 = p = (char *)data;
672         data_end = (char *)data + num_data;
673         last_name_raw = data_blob_null;
674
675         for (i=0; i<ff_searchcount; i++) {
676                 if (p2 >= data_end) {
677                         ff_eos = true;
678                         break;
679                 }
680                 if ((state->info_level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO)
681                     && (i == ff_searchcount-1)) {
682                         /* Last entry - fixup the last offset length. */
683                         SIVAL(p2, 0, PTR_DIFF((data + num_data), p2));
684                 }
685
686                 data_blob_free(&last_name_raw);
687
688                 finfo = &state->finfo[old_num_finfo + i];
689
690                 p2 += interpret_long_filename(
691                         state->finfo, /* Stick fname to the array as such */
692                         state->cli, state->info_level,
693                         (char *)data, recv_flags2, p2,
694                         data_end, finfo, &resume_key, &last_name_raw);
695
696                 if (finfo->name == NULL) {
697                         DEBUG(1, ("cli_list: Error: unable to parse name from "
698                                   "info level %d\n", state->info_level));
699                         ff_eos = true;
700                         break;
701                 }
702                 if (!state->first && (state->mask[0] != '\0') &&
703                     strcsequal(finfo->name, state->mask)) {
704                         DEBUG(1, ("Error: Looping in FIND_NEXT as name %s has "
705                                   "already been seen?\n", finfo->name));
706                         ff_eos = true;
707                         break;
708                 }
709         }
710
711         if (ff_searchcount == 0) {
712                 ff_eos = true;
713         }
714
715         TALLOC_FREE(param);
716         TALLOC_FREE(data);
717
718         /*
719          * Shrink state->finfo to the real length we received
720          */
721         tmp = talloc_realloc(state, state->finfo, struct file_info,
722                                    old_num_finfo + i);
723         if (tevent_req_nomem(tmp, req)) {
724                 return;
725         }
726         state->finfo = tmp;
727
728         state->first = false;
729
730         if (ff_eos) {
731                 data_blob_free(&last_name_raw);
732                 tevent_req_done(req);
733                 return;
734         }
735
736         TALLOC_FREE(state->mask);
737         state->mask = talloc_strdup(state, finfo->name);
738         if (tevent_req_nomem(state->mask, req)) {
739                 return;
740         }
741
742         SSVAL(&state->setup[0], 0, TRANSACT2_FINDNEXT);
743
744         param = talloc_realloc(state, state->param, uint8_t, 12);
745         if (tevent_req_nomem(param, req)) {
746                 return;
747         }
748         state->param = param;
749
750         SSVAL(param, 0, state->ff_dir_handle);
751         SSVAL(param, 2, state->max_matches); /* max count */
752         SSVAL(param, 4, state->info_level);
753         /*
754          * For W2K servers serving out FAT filesystems we *must* set
755          * the resume key. If it's not FAT then it's returned as zero.
756          */
757         SIVAL(param, 6, resume_key); /* ff_resume_key */
758         /*
759          * NB. *DON'T* use continue here. If you do it seems that W2K
760          * and bretheren can miss filenames. Use last filename
761          * continue instead. JRA
762          */
763         SSVAL(param, 10, (FLAG_TRANS2_FIND_REQUIRE_RESUME
764                           |FLAG_TRANS2_FIND_CLOSE_IF_END
765                           |(state->cli->backup_intent ? FLAG_TRANS2_FIND_BACKUP_INTENT : 0)));
766         if (last_name_raw.length) {
767                 state->param = trans2_bytes_push_bytes(state->param,
768                                                        last_name_raw.data,
769                                                        last_name_raw.length);
770                 if (tevent_req_nomem(state->param, req)) {
771                         return;
772                 }
773                 data_blob_free(&last_name_raw);
774         } else {
775                 state->param = trans2_bytes_push_str(state->param,
776                                                      cli_ucs2(state->cli),
777                                                      state->mask,
778                                                      strlen(state->mask)+1,
779                                                      NULL);
780                 if (tevent_req_nomem(state->param, req)) {
781                         return;
782                 }
783         }
784         param_len = talloc_get_size(state->param);
785
786         subreq = cli_trans_send(state, state->ev, state->cli,
787                                 SMBtrans2, NULL, -1, 0, 0,
788                                 state->setup, 1, 0,
789                                 state->param, param_len, 10,
790                                 NULL, 0, CLI_BUFFER_SIZE);
791         if (tevent_req_nomem(subreq, req)) {
792                 return;
793         }
794         tevent_req_set_callback(subreq, cli_list_trans_done, req);
795 }
796
797 static NTSTATUS cli_list_trans_recv(struct tevent_req *req,
798                                     TALLOC_CTX *mem_ctx,
799                                     struct file_info **finfo)
800 {
801         struct cli_list_trans_state *state = tevent_req_data(
802                 req, struct cli_list_trans_state);
803         NTSTATUS status;
804
805         if (tevent_req_is_nterror(req, &status)) {
806                 return status;
807         }
808         *finfo = talloc_move(mem_ctx, &state->finfo);
809         return NT_STATUS_OK;
810 }
811
812 NTSTATUS cli_list_trans(struct cli_state *cli, const char *mask,
813                         uint16_t attribute, int info_level,
814                         NTSTATUS (*fn)(const char *mnt, struct file_info *finfo,
815                                    const char *mask, void *private_data),
816                         void *private_data)
817 {
818         TALLOC_CTX *frame = talloc_stackframe();
819         struct event_context *ev;
820         struct tevent_req *req;
821         int i, num_finfo;
822         struct file_info *finfo = NULL;
823         NTSTATUS status = NT_STATUS_NO_MEMORY;
824
825         if (cli_has_async_calls(cli)) {
826                 /*
827                  * Can't use sync call while an async call is in flight
828                  */
829                 status = NT_STATUS_INVALID_PARAMETER;
830                 goto fail;
831         }
832         ev = event_context_init(frame);
833         if (ev == NULL) {
834                 goto fail;
835         }
836         req = cli_list_trans_send(frame, ev, cli, mask, attribute, info_level);
837         if (req == NULL) {
838                 goto fail;
839         }
840         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
841                 goto fail;
842         }
843         status = cli_list_trans_recv(req, frame, &finfo);
844         if (!NT_STATUS_IS_OK(status)) {
845                 goto fail;
846         }
847         num_finfo = talloc_array_length(finfo);
848         for (i=0; i<num_finfo; i++) {
849                 status = fn(cli->dfs_mountpoint, &finfo[i], mask, private_data);
850                 if (!NT_STATUS_IS_OK(status)) {
851                         goto fail;
852                 }
853         }
854  fail:
855         TALLOC_FREE(frame);
856         return status;
857 }
858
859 struct cli_list_state {
860         NTSTATUS (*recv_fn)(struct tevent_req *req, TALLOC_CTX *mem_ctx,
861                             struct file_info **finfo);
862         struct file_info *finfo;
863 };
864
865 static void cli_list_done(struct tevent_req *subreq);
866
867 struct tevent_req *cli_list_send(TALLOC_CTX *mem_ctx,
868                                  struct tevent_context *ev,
869                                  struct cli_state *cli,
870                                  const char *mask,
871                                  uint16_t attribute,
872                                  uint16_t info_level)
873 {
874         struct tevent_req *req, *subreq;
875         struct cli_list_state *state;
876
877         req = tevent_req_create(mem_ctx, &state, struct cli_list_state);
878         if (req == NULL) {
879                 return NULL;
880         }
881
882         if (smbXcli_conn_protocol(cli->conn) <= PROTOCOL_LANMAN1) {
883                 subreq = cli_list_old_send(state, ev, cli, mask, attribute);
884                 state->recv_fn = cli_list_old_recv;
885         } else {
886                 subreq = cli_list_trans_send(state, ev, cli, mask, attribute,
887                                              info_level);
888                 state->recv_fn = cli_list_trans_recv;
889         }
890         if (tevent_req_nomem(subreq, req)) {
891                 return tevent_req_post(req, ev);
892         }
893         tevent_req_set_callback(subreq, cli_list_done, req);
894         return req;
895 }
896
897 static void cli_list_done(struct tevent_req *subreq)
898 {
899         struct tevent_req *req = tevent_req_callback_data(
900                 subreq, struct tevent_req);
901         struct cli_list_state *state = tevent_req_data(
902                 req, struct cli_list_state);
903         NTSTATUS status;
904
905         status = state->recv_fn(subreq, state, &state->finfo);
906         TALLOC_FREE(subreq);
907         if (!NT_STATUS_IS_OK(status)) {
908                 tevent_req_nterror(req, status);
909                 return;
910         }
911         tevent_req_done(req);
912 }
913
914 NTSTATUS cli_list_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
915                        struct file_info **finfo, size_t *num_finfo)
916 {
917         struct cli_list_state *state = tevent_req_data(
918                 req, struct cli_list_state);
919         NTSTATUS status;
920
921         if (tevent_req_is_nterror(req, &status)) {
922                 return status;
923         }
924         *num_finfo = talloc_array_length(state->finfo);
925         *finfo = talloc_move(mem_ctx, &state->finfo);
926         return NT_STATUS_OK;
927 }
928
929 NTSTATUS cli_list(struct cli_state *cli, const char *mask, uint16 attribute,
930                   NTSTATUS (*fn)(const char *, struct file_info *, const char *,
931                              void *), void *state)
932 {
933         TALLOC_CTX *frame = talloc_stackframe();
934         struct event_context *ev;
935         struct tevent_req *req;
936         NTSTATUS status = NT_STATUS_NO_MEMORY;
937         struct file_info *finfo;
938         size_t i, num_finfo;
939         uint16_t info_level;
940
941         if (cli_has_async_calls(cli)) {
942                 /*
943                  * Can't use sync call while an async call is in flight
944                  */
945                 status = NT_STATUS_INVALID_PARAMETER;
946                 goto fail;
947         }
948         ev = event_context_init(frame);
949         if (ev == NULL) {
950                 goto fail;
951         }
952
953         info_level = (smb1cli_conn_capabilities(cli->conn) & CAP_NT_SMBS)
954                 ? SMB_FIND_FILE_BOTH_DIRECTORY_INFO : SMB_FIND_INFO_STANDARD;
955
956         req = cli_list_send(frame, ev, cli, mask, attribute, info_level);
957         if (req == NULL) {
958                 goto fail;
959         }
960         if (!tevent_req_poll(req, ev)) {
961                 status = map_nt_error_from_unix(errno);
962                 goto fail;
963         }
964
965         status = cli_list_recv(req, frame, &finfo, &num_finfo);
966         if (!NT_STATUS_IS_OK(status)) {
967                 goto fail;
968         }
969
970         for (i=0; i<num_finfo; i++) {
971                 status = fn(cli->dfs_mountpoint, &finfo[i], mask, state);
972                 if (!NT_STATUS_IS_OK(status)) {
973                         goto fail;
974                 }
975         }
976  fail:
977         TALLOC_FREE(frame);
978         return status;
979 }