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