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