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