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