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