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