s3: Remove a pointless wrapper function
[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
22 /****************************************************************************
23  Calculate a safe next_entry_offset.
24 ****************************************************************************/
25
26 static size_t calc_next_entry_offset(const char *base, const char *pdata_end)
27 {
28         size_t next_entry_offset = (size_t)IVAL(base,0);
29
30         if (next_entry_offset == 0 ||
31                         base + next_entry_offset < base ||
32                         base + next_entry_offset > pdata_end) {
33                 next_entry_offset = pdata_end - base;
34         }
35         return next_entry_offset;
36 }
37
38 /****************************************************************************
39  Interpret a long filename structure - this is mostly guesses at the moment.
40  The length of the structure is returned
41  The structure of a long filename depends on the info level.
42  SMB_FIND_FILE_BOTH_DIRECTORY_INFO is used
43  by NT and SMB_FIND_EA_SIZE is used by OS/2
44 ****************************************************************************/
45
46 static size_t interpret_long_filename(TALLOC_CTX *ctx,
47                                         struct cli_state *cli,
48                                         int level,
49                                         const char *base_ptr,
50                                         uint16_t recv_flags2,
51                                         const char *p,
52                                         const char *pdata_end,
53                                         struct file_info *finfo,
54                                         uint32 *p_resume_key,
55                                         DATA_BLOB *p_last_name_raw)
56 {
57         int len;
58         size_t ret;
59         const char *base = p;
60
61         data_blob_free(p_last_name_raw);
62
63         if (p_resume_key) {
64                 *p_resume_key = 0;
65         }
66         ZERO_STRUCTP(finfo);
67
68         switch (level) {
69                 case SMB_FIND_INFO_STANDARD: /* OS/2 understands this */
70                         /* these dates are converted to GMT by
71                            make_unix_date */
72                         if (pdata_end - base < 27) {
73                                 return pdata_end - base;
74                         }
75                         finfo->ctime_ts = convert_time_t_to_timespec(
76                                 make_unix_date2(p+4, cli->serverzone));
77                         finfo->atime_ts = convert_time_t_to_timespec(
78                                 make_unix_date2(p+8, cli->serverzone));
79                         finfo->mtime_ts = convert_time_t_to_timespec(
80                                 make_unix_date2(p+12, cli->serverzone));
81                         finfo->size = IVAL(p,16);
82                         finfo->mode = CVAL(p,24);
83                         len = CVAL(p, 26);
84                         p += 27;
85                         p += align_string(cli->inbuf, p, 0);
86
87                         /* We can safely use len here (which is required by OS/2)
88                          * and the NAS-BASIC server instead of +2 or +1 as the
89                          * STR_TERMINATE flag below is
90                          * actually used as the length calculation.
91                          * The len is merely an upper bound.
92                          * Due to the explicit 2 byte null termination
93                          * in cli_receive_trans/cli_receive_nt_trans
94                          * we know this is safe. JRA + kukks
95                          */
96
97                         if (p + len > pdata_end) {
98                                 return pdata_end - base;
99                         }
100
101                         /* the len+2 below looks strange but it is
102                            important to cope with the differences
103                            between win2000 and win9x for this call
104                            (tridge) */
105                         ret = clistr_pull_talloc(ctx,
106                                                 base_ptr,
107                                                 recv_flags2,
108                                                 &finfo->name,
109                                                 p,
110                                                 len+2,
111                                                 STR_TERMINATE);
112                         if (ret == (size_t)-1) {
113                                 return pdata_end - base;
114                         }
115                         p += ret;
116                         return PTR_DIFF(p, base);
117
118                 case SMB_FIND_EA_SIZE: /* this is what OS/2 uses mostly */
119                         /* these dates are converted to GMT by
120                            make_unix_date */
121                         if (pdata_end - base < 31) {
122                                 return pdata_end - base;
123                         }
124                         finfo->ctime_ts = convert_time_t_to_timespec(
125                                 make_unix_date2(p+4, cli->serverzone));
126                         finfo->atime_ts = convert_time_t_to_timespec(
127                                 make_unix_date2(p+8, cli->serverzone));
128                         finfo->mtime_ts = convert_time_t_to_timespec(
129                                 make_unix_date2(p+12, cli->serverzone));
130                         finfo->size = IVAL(p,16);
131                         finfo->mode = CVAL(p,24);
132                         len = CVAL(p, 30);
133                         p += 31;
134                         /* check for unisys! */
135                         if (p + len + 1 > pdata_end) {
136                                 return pdata_end - base;
137                         }
138                         ret = clistr_pull_talloc(ctx,
139                                                 base_ptr,
140                                                 recv_flags2,
141                                                 &finfo->name,
142                                                 p,
143                                                 len,
144                                                 STR_NOALIGN);
145                         if (ret == (size_t)-1) {
146                                 return pdata_end - base;
147                         }
148                         p += ret;
149                         return PTR_DIFF(p, base) + 1;
150
151                 case SMB_FIND_FILE_BOTH_DIRECTORY_INFO: /* NT uses this, but also accepts 2 */
152                 {
153                         size_t namelen, slen;
154
155                         if (pdata_end - base < 94) {
156                                 return pdata_end - base;
157                         }
158
159                         p += 4; /* next entry offset */
160
161                         if (p_resume_key) {
162                                 *p_resume_key = IVAL(p,0);
163                         }
164                         p += 4; /* fileindex */
165
166                         /* Offset zero is "create time", not "change time". */
167                         p += 8;
168                         finfo->atime_ts = interpret_long_date(p);
169                         p += 8;
170                         finfo->mtime_ts = interpret_long_date(p);
171                         p += 8;
172                         finfo->ctime_ts = interpret_long_date(p);
173                         p += 8;
174                         finfo->size = IVAL2_TO_SMB_BIG_UINT(p,0);
175                         p += 8;
176                         p += 8; /* alloc size */
177                         finfo->mode = CVAL(p,0);
178                         p += 4;
179                         namelen = IVAL(p,0);
180                         p += 4;
181                         p += 4; /* EA size */
182                         slen = SVAL(p, 0);
183                         if (slen > 24) {
184                                 /* Bad short name length. */
185                                 return pdata_end - base;
186                         }
187                         p += 2;
188                         {
189                                 /* stupid NT bugs. grr */
190                                 int flags = 0;
191                                 if (p[1] == 0 && namelen > 1) flags |= STR_UNICODE;
192                                 clistr_pull(cli->inbuf, finfo->short_name, p,
193                                             sizeof(finfo->short_name),
194                                             slen, flags);
195                         }
196                         p += 24; /* short name? */
197                         if (p + namelen < p || p + namelen > pdata_end) {
198                                 return pdata_end - base;
199                         }
200                         ret = clistr_pull_talloc(ctx,
201                                                 base_ptr,
202                                                 recv_flags2,
203                                                 &finfo->name,
204                                                 p,
205                                                 namelen,
206                                                 0);
207                         if (ret == (size_t)-1) {
208                                 return pdata_end - base;
209                         }
210
211                         /* To be robust in the face of unicode conversion failures
212                            we need to copy the raw bytes of the last name seen here.
213                            Namelen doesn't include the terminating unicode null, so
214                            copy it here. */
215
216                         if (p_last_name_raw) {
217                                 *p_last_name_raw = data_blob(NULL, namelen+2);
218                                 memcpy(p_last_name_raw->data, p, namelen);
219                                 SSVAL(p_last_name_raw->data, namelen, 0);
220                         }
221                         return calc_next_entry_offset(base, pdata_end);
222                 }
223         }
224
225         DEBUG(1,("Unknown long filename format %d\n",level));
226         return calc_next_entry_offset(base, pdata_end);
227 }
228
229 /****************************************************************************
230  Do a directory listing, calling fn on each file found.
231 ****************************************************************************/
232
233 int cli_list_new(struct cli_state *cli,const char *Mask,uint16 attribute,
234                  void (*fn)(const char *, struct file_info *, const char *,
235                             void *), void *state)
236 {
237 #if 1
238         int max_matches = 1366; /* Match W2k - was 512. */
239 #else
240         int max_matches = 512;
241 #endif
242         int info_level;
243         char *p, *p2, *rdata_end;
244         char *mask = NULL;
245         struct file_info finfo;
246         int i;
247         char *dirlist = NULL;
248         int dirlist_len = 0;
249         int total_received = -1;
250         bool First = True;
251         int ff_searchcount=0;
252         int ff_eos=0;
253         int ff_dir_handle=0;
254         int loop_count = 0;
255         char *rparam=NULL, *rdata=NULL;
256         unsigned int param_len, data_len;
257         uint16 setup;
258         char *param;
259         uint32 resume_key = 0;
260         TALLOC_CTX *frame = talloc_stackframe();
261         DATA_BLOB last_name_raw = data_blob_null;
262
263         /* NT uses SMB_FIND_FILE_BOTH_DIRECTORY_INFO,
264            OS/2 uses SMB_FIND_EA_SIZE. Both accept SMB_FIND_INFO_STANDARD. */
265         info_level = (cli->capabilities&CAP_NT_SMBS)?
266                 SMB_FIND_FILE_BOTH_DIRECTORY_INFO : SMB_FIND_INFO_STANDARD;
267
268         mask = SMB_STRDUP(Mask);
269         if (!mask) {
270                 TALLOC_FREE(frame);
271                 return -1;
272         }
273
274         ZERO_STRUCT(finfo);
275
276         while (ff_eos == 0) {
277                 size_t nlen = 2*(strlen(mask)+1);
278
279                 loop_count++;
280                 if (loop_count > 200) {
281                         DEBUG(0,("Error: Looping in FIND_NEXT??\n"));
282                         break;
283                 }
284
285                 param = SMB_MALLOC_ARRAY(char, 12+nlen+last_name_raw.length+2);
286                 if (!param) {
287                         break;
288                 }
289
290                 if (First) {
291                         setup = TRANSACT2_FINDFIRST;
292                         SSVAL(param,0,attribute); /* attribute */
293                         SSVAL(param,2,max_matches); /* max count */
294                         SSVAL(param,4,(FLAG_TRANS2_FIND_REQUIRE_RESUME|FLAG_TRANS2_FIND_CLOSE_IF_END)); /* resume required + close on end */
295                         SSVAL(param,6,info_level);
296                         SIVAL(param,8,0);
297                         p = param+12;
298                         p += clistr_push(cli, param+12, mask,
299                                          nlen, STR_TERMINATE);
300                 } else {
301                         setup = TRANSACT2_FINDNEXT;
302                         SSVAL(param,0,ff_dir_handle);
303                         SSVAL(param,2,max_matches); /* max count */
304                         SSVAL(param,4,info_level);
305                         /* For W2K servers serving out FAT filesystems we *must* set the
306                            resume key. If it's not FAT then it's returned as zero. */
307                         SIVAL(param,6,resume_key); /* ff_resume_key */
308                         /* NB. *DON'T* use continue here. If you do it seems that W2K and bretheren
309                            can miss filenames. Use last filename continue instead. JRA */
310                         SSVAL(param,10,(FLAG_TRANS2_FIND_REQUIRE_RESUME|FLAG_TRANS2_FIND_CLOSE_IF_END));        /* resume required + close on end */
311                         p = param+12;
312                         if (last_name_raw.length) {
313                                 memcpy(p, last_name_raw.data, last_name_raw.length);
314                                 p += last_name_raw.length;
315                         } else {
316                                 p += clistr_push(cli, param+12, mask,
317                                                 nlen, STR_TERMINATE);
318                         }
319                 }
320
321                 param_len = PTR_DIFF(p, param);
322
323                 if (!cli_send_trans(cli, SMBtrans2,
324                                     NULL,                   /* Name */
325                                     -1, 0,                  /* fid, flags */
326                                     &setup, 1, 0,           /* setup, length, max */
327                                     param, param_len, 10,   /* param, length, max */
328                                     NULL, 0,
329 #if 0
330                                     /* w2k value. */
331                                     MIN(16384,cli->max_xmit) /* data, length, max. */
332 #else
333                                     cli->max_xmit           /* data, length, max. */
334 #endif
335                                     )) {
336                         SAFE_FREE(param);
337                         TALLOC_FREE(frame);
338                         break;
339                 }
340
341                 SAFE_FREE(param);
342
343                 if (!cli_receive_trans(cli, SMBtrans2,
344                                        &rparam, &param_len,
345                                        &rdata, &data_len) &&
346                     cli_is_dos_error(cli)) {
347                         /* We need to work around a Win95 bug - sometimes
348                            it gives ERRSRV/ERRerror temprarily */
349                         uint8 eclass;
350                         uint32 ecode;
351
352                         SAFE_FREE(rdata);
353                         SAFE_FREE(rparam);
354
355                         cli_dos_error(cli, &eclass, &ecode);
356
357                         /*
358                          * OS/2 might return "no more files",
359                          * which just tells us, that searchcount is zero
360                          * in this search.
361                          * Guenter Kukkukk <linux@kukkukk.com>
362                          */
363
364                         if (eclass == ERRDOS && ecode == ERRnofiles) {
365                                 ff_searchcount = 0;
366                                 cli_reset_error(cli);
367                                 break;
368                         }
369
370                         if (eclass != ERRSRV || ecode != ERRerror)
371                                 break;
372                         smb_msleep(100);
373                         continue;
374                 }
375
376                 if (cli_is_error(cli) || !rdata || !rparam) {
377                         SAFE_FREE(rdata);
378                         SAFE_FREE(rparam);
379                         break;
380                 }
381
382                 if (total_received == -1)
383                         total_received = 0;
384
385                 /* parse out some important return info */
386                 p = rparam;
387                 if (First) {
388                         ff_dir_handle = SVAL(p,0);
389                         ff_searchcount = SVAL(p,2);
390                         ff_eos = SVAL(p,4);
391                 } else {
392                         ff_searchcount = SVAL(p,0);
393                         ff_eos = SVAL(p,2);
394                 }
395
396                 if (ff_searchcount == 0) {
397                         SAFE_FREE(rdata);
398                         SAFE_FREE(rparam);
399                         break;
400                 }
401
402                 /* point to the data bytes */
403                 p = rdata;
404                 rdata_end = rdata + data_len;
405
406                 /* we might need the lastname for continuations */
407                 for (p2=p,i=0;i<ff_searchcount && p2 < rdata_end;i++) {
408                         if ((info_level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO) &&
409                                         (i == ff_searchcount-1)) {
410                                 /* Last entry - fixup the last offset length. */
411                                 SIVAL(p2,0,PTR_DIFF((rdata + data_len),p2));
412                         }
413                         p2 += interpret_long_filename(frame,
414                                                         cli,
415                                                         info_level,
416                                                         cli->inbuf,
417                                                         SVAL(cli->inbuf, smb_flg2),
418                                                         p2,
419                                                         rdata_end,
420                                                         &finfo,
421                                                         &resume_key,
422                                                         &last_name_raw);
423
424                         if (!finfo.name) {
425                                 DEBUG(0,("cli_list_new: Error: unable to parse name from info level %d\n",
426                                         info_level));
427                                 ff_eos = 1;
428                                 break;
429                         }
430                         if (!First && *mask && strcsequal(finfo.name, mask)) {
431                                 DEBUG(0,("Error: Looping in FIND_NEXT as name %s has already been seen?\n",
432                                         finfo.name));
433                                 ff_eos = 1;
434                                 break;
435                         }
436                 }
437
438                 SAFE_FREE(mask);
439                 if (ff_searchcount > 0 && ff_eos == 0 && finfo.name) {
440                         mask = SMB_STRDUP(finfo.name);
441                 } else {
442                         mask = SMB_STRDUP("");
443                 }
444                 if (!mask) {
445                         SAFE_FREE(rdata);
446                         SAFE_FREE(rparam);
447                         break;
448                 }
449
450                 /* grab the data for later use */
451                 /* and add them to the dirlist pool */
452                 dirlist = (char *)SMB_REALLOC(dirlist,dirlist_len + data_len);
453
454                 if (!dirlist) {
455                         DEBUG(0,("cli_list_new: Failed to expand dirlist\n"));
456                         SAFE_FREE(rdata);
457                         SAFE_FREE(rparam);
458                         break;
459                 }
460
461                 memcpy(dirlist+dirlist_len,p,data_len);
462                 dirlist_len += data_len;
463
464                 total_received += ff_searchcount;
465
466                 SAFE_FREE(rdata);
467                 SAFE_FREE(rparam);
468
469                 DEBUG(3,("received %d entries (eos=%d)\n",
470                          ff_searchcount,ff_eos));
471
472                 if (ff_searchcount > 0)
473                         loop_count = 0;
474
475                 First = False;
476         }
477
478         /* see if the server disconnected or the connection otherwise failed */
479         if (cli_is_error(cli)) {
480                 total_received = -1;
481         } else {
482                 /* no connection problem.  let user function add each entry */
483                 rdata_end = dirlist + dirlist_len;
484                 for (p=dirlist,i=0;i<total_received;i++) {
485                         p += interpret_long_filename(frame,
486                                                         cli,
487                                                         info_level,
488                                                         cli->inbuf,
489                                                         SVAL(cli->inbuf, smb_flg2),
490                                                         p,
491                                                         rdata_end,
492                                                         &finfo,
493                                                         NULL,
494                                                         NULL);
495                         if (!finfo.name) {
496                                 DEBUG(0,("cli_list_new: unable to parse name from info level %d\n",
497                                         info_level));
498                                 break;
499                         }
500                         fn(cli->dfs_mountpoint, &finfo, Mask, state);
501                 }
502         }
503
504         /* free up the dirlist buffer and last name raw blob */
505         SAFE_FREE(dirlist);
506         data_blob_free(&last_name_raw);
507         SAFE_FREE(mask);
508         TALLOC_FREE(frame);
509         return(total_received);
510 }
511
512 /****************************************************************************
513  Interpret a short filename structure.
514  The length of the structure is returned.
515 ****************************************************************************/
516
517 static bool interpret_short_filename(TALLOC_CTX *ctx,
518                                 struct cli_state *cli,
519                                 char *p,
520                                 struct file_info *finfo)
521 {
522         size_t ret;
523         ZERO_STRUCTP(finfo);
524
525         finfo->mode = CVAL(p,21);
526
527         /* this date is converted to GMT by make_unix_date */
528         finfo->ctime_ts.tv_sec = make_unix_date(p+22, cli->serverzone);
529         finfo->ctime_ts.tv_nsec = 0;
530         finfo->mtime_ts.tv_sec = finfo->atime_ts.tv_sec = finfo->ctime_ts.tv_sec;
531         finfo->mtime_ts.tv_nsec = finfo->atime_ts.tv_nsec = 0;
532         finfo->size = IVAL(p,26);
533         ret = clistr_pull_talloc(ctx,
534                         cli->inbuf,
535                         SVAL(cli->inbuf, smb_flg2),
536                         &finfo->name,
537                         p+30,
538                         12,
539                         STR_ASCII);
540         if (ret == (size_t)-1) {
541                 return false;
542         }
543
544         if (finfo->name) {
545                 strlcpy(finfo->short_name,
546                         finfo->name,
547                         sizeof(finfo->short_name));
548         }
549         return true;
550 }
551
552 /****************************************************************************
553  Do a directory listing, calling fn on each file found.
554  this uses the old SMBsearch interface. It is needed for testing Samba,
555  but should otherwise not be used.
556 ****************************************************************************/
557
558 int cli_list_old(struct cli_state *cli,const char *Mask,uint16 attribute,
559                  void (*fn)(const char *, struct file_info *, const char *,
560                             void *), void *state)
561 {
562         char *p;
563         int received = 0;
564         bool first = True;
565         char status[21];
566         int num_asked = (cli->max_xmit - 100)/DIR_STRUCT_SIZE;
567         int num_received = 0;
568         int i;
569         char *dirlist = NULL;
570         char *mask = NULL;
571         TALLOC_CTX *frame = NULL;
572
573         ZERO_ARRAY(status);
574
575         mask = SMB_STRDUP(Mask);
576         if (!mask) {
577                 return -1;
578         }
579
580         while (1) {
581                 memset(cli->outbuf,'\0',smb_size);
582                 memset(cli->inbuf,'\0',smb_size);
583
584                 cli_set_message(cli->outbuf,2,0,True);
585
586                 SCVAL(cli->outbuf,smb_com,SMBsearch);
587
588                 SSVAL(cli->outbuf,smb_tid,cli->cnum);
589                 cli_setup_packet(cli);
590
591                 SSVAL(cli->outbuf,smb_vwv0,num_asked);
592                 SSVAL(cli->outbuf,smb_vwv1,attribute);
593
594                 p = smb_buf(cli->outbuf);
595                 *p++ = 4;
596
597                 p += clistr_push(cli, p, first?mask:"",
598                                 cli->bufsize - PTR_DIFF(p,cli->outbuf),
599                                 STR_TERMINATE);
600                 *p++ = 5;
601                 if (first) {
602                         SSVAL(p,0,0);
603                         p += 2;
604                 } else {
605                         SSVAL(p,0,21);
606                         p += 2;
607                         memcpy(p,status,21);
608                         p += 21;
609                 }
610
611                 cli_setup_bcc(cli, p);
612                 cli_send_smb(cli);
613                 if (!cli_receive_smb(cli)) break;
614
615                 received = SVAL(cli->inbuf,smb_vwv0);
616                 if (received <= 0) break;
617
618                 /* Ensure we received enough data. */
619                 if ((cli->inbuf+4+smb_len(cli->inbuf) - (smb_buf(cli->inbuf)+3)) <
620                                 received*DIR_STRUCT_SIZE) {
621                         break;
622                 }
623
624                 first = False;
625
626                 dirlist = (char *)SMB_REALLOC(
627                         dirlist,(num_received + received)*DIR_STRUCT_SIZE);
628                 if (!dirlist) {
629                         DEBUG(0,("cli_list_old: failed to expand dirlist"));
630                         SAFE_FREE(mask);
631                         return 0;
632                 }
633
634                 p = smb_buf(cli->inbuf) + 3;
635
636                 memcpy(dirlist+num_received*DIR_STRUCT_SIZE,
637                        p,received*DIR_STRUCT_SIZE);
638
639                 memcpy(status,p + ((received-1)*DIR_STRUCT_SIZE),21);
640
641                 num_received += received;
642
643                 if (cli_is_error(cli)) break;
644         }
645
646         if (!first) {
647                 memset(cli->outbuf,'\0',smb_size);
648                 memset(cli->inbuf,'\0',smb_size);
649
650                 cli_set_message(cli->outbuf,2,0,True);
651                 SCVAL(cli->outbuf,smb_com,SMBfclose);
652                 SSVAL(cli->outbuf,smb_tid,cli->cnum);
653                 cli_setup_packet(cli);
654
655                 SSVAL(cli->outbuf, smb_vwv0, 0); /* find count? */
656                 SSVAL(cli->outbuf, smb_vwv1, attribute);
657
658                 p = smb_buf(cli->outbuf);
659                 *p++ = 4;
660                 fstrcpy(p, "");
661                 p += strlen(p) + 1;
662                 *p++ = 5;
663                 SSVAL(p, 0, 21);
664                 p += 2;
665                 memcpy(p,status,21);
666                 p += 21;
667
668                 cli_setup_bcc(cli, p);
669                 cli_send_smb(cli);
670                 if (!cli_receive_smb(cli)) {
671                         DEBUG(0,("Error closing search: %s\n",cli_errstr(cli)));
672                 }
673         }
674
675         frame = talloc_stackframe();
676         for (p=dirlist,i=0;i<num_received;i++) {
677                 struct file_info finfo;
678                 if (!interpret_short_filename(frame, cli, p, &finfo)) {
679                         break;
680                 }
681                 p += DIR_STRUCT_SIZE;
682                 fn("\\", &finfo, Mask, state);
683         }
684         TALLOC_FREE(frame);
685
686         SAFE_FREE(mask);
687         SAFE_FREE(dirlist);
688         return(num_received);
689 }
690
691 /****************************************************************************
692  Do a directory listing, calling fn on each file found.
693  This auto-switches between old and new style.
694 ****************************************************************************/
695
696 NTSTATUS cli_list(struct cli_state *cli,const char *Mask,uint16 attribute,
697                   void (*fn)(const char *, struct file_info *, const char *,
698                              void *), void *state)
699 {
700         int rec;
701
702         if (cli->protocol <= PROTOCOL_LANMAN1) {
703                 rec = cli_list_old(cli, Mask, attribute, fn, state);
704         } else {
705                 rec = cli_list_new(cli, Mask, attribute, fn, state);
706         }
707         if (rec == -1) {
708                 return cli_nt_error(cli);
709         }
710         return NT_STATUS_OK;
711 }