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