[GLUE] Rsync SAMBA_3_2_0 SVN r25598 in order to create the v3-2-test branch.
[ira/wip.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  Interpret a long filename structure - this is mostly guesses at the moment.
26  The length of the structure is returned
27  The structure of a long filename depends on the info level. 260 is used
28  by NT and 2 is used by OS/2
29 ****************************************************************************/
30
31 static size_t interpret_long_filename(struct cli_state *cli, int level,char *p,file_info *finfo,
32                                         uint32 *p_resume_key, DATA_BLOB *p_last_name_raw, uint32 *p_last_name_raw_len)
33 {
34         file_info finfo2;
35         int len;
36         char *base = p;
37
38         if (!finfo) {
39                 finfo = &finfo2;
40         }
41
42         if (p_resume_key) {
43                 *p_resume_key = 0;
44         }
45         memcpy(finfo,&def_finfo,sizeof(*finfo));
46         finfo->cli = cli;
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         pstrcpy(mask,Mask);
189         
190         while (ff_eos == 0) {
191                 loop_count++;
192                 if (loop_count > 200) {
193                         DEBUG(0,("Error: Looping in FIND_NEXT??\n"));
194                         break;
195                 }
196
197                 if (First) {
198                         setup = TRANSACT2_FINDFIRST;
199                         SSVAL(param,0,attribute); /* attribute */
200                         SSVAL(param,2,max_matches); /* max count */
201                         SSVAL(param,4,(FLAG_TRANS2_FIND_REQUIRE_RESUME|FLAG_TRANS2_FIND_CLOSE_IF_END)); /* resume required + close on end */
202                         SSVAL(param,6,info_level); 
203                         SIVAL(param,8,0);
204                         p = param+12;
205                         p += clistr_push(cli, param+12, mask, sizeof(param)-12, 
206                                          STR_TERMINATE);
207                 } else {
208                         setup = TRANSACT2_FINDNEXT;
209                         SSVAL(param,0,ff_dir_handle);
210                         SSVAL(param,2,max_matches); /* max count */
211                         SSVAL(param,4,info_level); 
212                         /* For W2K servers serving out FAT filesystems we *must* set the
213                            resume key. If it's not FAT then it's returned as zero. */
214                         SIVAL(param,6,resume_key); /* ff_resume_key */
215                         /* NB. *DON'T* use continue here. If you do it seems that W2K and bretheren
216                            can miss filenames. Use last filename continue instead. JRA */
217                         SSVAL(param,10,(FLAG_TRANS2_FIND_REQUIRE_RESUME|FLAG_TRANS2_FIND_CLOSE_IF_END));        /* resume required + close on end */
218                         p = param+12;
219                         if (last_name_raw_len && (last_name_raw_len < (sizeof(param)-12))) {
220                                 memcpy(p, last_name_raw.data, last_name_raw_len);
221                                 p += last_name_raw_len;
222                         } else {
223                                 p += clistr_push(cli, param+12, mask, sizeof(param)-12, STR_TERMINATE);
224                         }
225                 }
226
227                 param_len = PTR_DIFF(p, param);
228
229                 if (!cli_send_trans(cli, SMBtrans2, 
230                                     NULL,                   /* Name */
231                                     -1, 0,                  /* fid, flags */
232                                     &setup, 1, 0,           /* setup, length, max */
233                                     param, param_len, 10,   /* param, length, max */
234                                     NULL, 0, 
235 #if 0
236                                     /* w2k value. */
237                                     MIN(16384,cli->max_xmit) /* data, length, max. */
238 #else
239                                     cli->max_xmit           /* data, length, max. */
240 #endif
241                                     )) {
242                         break;
243                 }
244
245                 if (!cli_receive_trans(cli, SMBtrans2, 
246                                        &rparam, &param_len,
247                                        &rdata, &data_len) &&
248                     cli_is_dos_error(cli)) {
249                         /* we need to work around a Win95 bug - sometimes
250                            it gives ERRSRV/ERRerror temprarily */
251                         uint8 eclass;
252                         uint32 ecode;
253
254                         SAFE_FREE(rdata);
255                         SAFE_FREE(rparam);
256
257                         cli_dos_error(cli, &eclass, &ecode);
258                         if (eclass != ERRSRV || ecode != ERRerror)
259                                 break;
260                         smb_msleep(100);
261                         continue;
262                 }
263
264                 if (cli_is_error(cli) || !rdata || !rparam) {
265                         SAFE_FREE(rdata);
266                         SAFE_FREE(rparam);
267                         break;
268                 }
269
270                 if (total_received == -1)
271                         total_received = 0;
272
273                 /* parse out some important return info */
274                 p = rparam;
275                 if (First) {
276                         ff_dir_handle = SVAL(p,0);
277                         ff_searchcount = SVAL(p,2);
278                         ff_eos = SVAL(p,4);
279                 } else {
280                         ff_searchcount = SVAL(p,0);
281                         ff_eos = SVAL(p,2);
282                 }
283
284                 if (ff_searchcount == 0) {
285                         SAFE_FREE(rdata);
286                         SAFE_FREE(rparam);
287                         break;
288                 }
289
290                 /* point to the data bytes */
291                 p = rdata;
292
293                 /* we might need the lastname for continuations */
294                 for (p2=p,i=0;i<ff_searchcount;i++) {
295                         if ((info_level == 260) && (i == ff_searchcount-1)) {
296                                 /* Last entry - fixup the last offset length. */
297                                 SIVAL(p2,0,PTR_DIFF((rdata + data_len),p2));
298                         }
299                         p2 += interpret_long_filename(cli,info_level,p2,&finfo,
300                                                         &resume_key,&last_name_raw,&last_name_raw_len);
301
302                         if (!First && *mask && strcsequal(finfo.name, mask)) {
303                                 DEBUG(0,("Error: Looping in FIND_NEXT as name %s has already been seen?\n",
304                                         finfo.name));
305                                 ff_eos = 1;
306                                 break;
307                         }
308                 }
309
310                 if (ff_searchcount > 0) {
311                         pstrcpy(mask, finfo.name);
312                 } else {
313                         pstrcpy(mask,"");
314                 }
315
316                 /* grab the data for later use */
317                 /* and add them to the dirlist pool */
318                 dirlist = (char *)SMB_REALLOC(dirlist,dirlist_len + data_len);
319
320                 if (!dirlist) {
321                         DEBUG(0,("cli_list_new: Failed to expand dirlist\n"));
322                         SAFE_FREE(rdata);
323                         SAFE_FREE(rparam);
324                         break;
325                 }
326
327                 memcpy(dirlist+dirlist_len,p,data_len);
328                 dirlist_len += data_len;
329
330                 total_received += ff_searchcount;
331
332                 SAFE_FREE(rdata);
333                 SAFE_FREE(rparam);
334
335                 DEBUG(3,("received %d entries (eos=%d)\n",
336                          ff_searchcount,ff_eos));
337
338                 if (ff_searchcount > 0)
339                         loop_count = 0;
340
341                 First = False;
342         }
343
344         mnt = cli_cm_get_mntpoint( cli );
345
346         /* see if the server disconnected or the connection otherwise failed */
347         if (cli_is_error(cli)) {
348                 total_received = -1;
349         } else {
350                 /* no connection problem.  let user function add each entry */
351                 for (p=dirlist,i=0;i<total_received;i++) {
352                         p += interpret_long_filename(cli, info_level, p,
353                                                      &finfo,NULL,NULL,NULL);
354                         fn( mnt,&finfo, Mask, state );
355                 }
356         }
357
358         /* free up the dirlist buffer and last name raw blob */
359         SAFE_FREE(dirlist);
360         data_blob_free(&last_name_raw);
361         return(total_received);
362 }
363
364 /****************************************************************************
365  Interpret a short filename structure.
366  The length of the structure is returned.
367 ****************************************************************************/
368
369 static int interpret_short_filename(struct cli_state *cli, char *p,file_info *finfo)
370 {
371
372         *finfo = def_finfo;
373
374         finfo->cli = cli;
375         finfo->mode = CVAL(p,21);
376         
377         /* this date is converted to GMT by make_unix_date */
378         finfo->ctime_ts.tv_sec = cli_make_unix_date(cli, p+22);
379         finfo->ctime_ts.tv_nsec = 0;
380         finfo->mtime_ts.tv_sec = finfo->atime_ts.tv_sec = finfo->ctime_ts.tv_sec;
381         finfo->mtime_ts.tv_nsec = finfo->atime_ts.tv_nsec = 0;
382         finfo->size = IVAL(p,26);
383         clistr_pull(cli, finfo->name, p+30, sizeof(finfo->name), 12, STR_ASCII);
384         if (strcmp(finfo->name, "..") && strcmp(finfo->name, ".")) {
385                 strncpy(finfo->short_name,finfo->name, sizeof(finfo->short_name)-1);
386                 finfo->short_name[sizeof(finfo->short_name)-1] = '\0';
387         }
388
389         return(DIR_STRUCT_SIZE);
390 }
391
392
393 /****************************************************************************
394  Do a directory listing, calling fn on each file found.
395  this uses the old SMBsearch interface. It is needed for testing Samba,
396  but should otherwise not be used.
397 ****************************************************************************/
398
399 int cli_list_old(struct cli_state *cli,const char *Mask,uint16 attribute, 
400                  void (*fn)(const char *, file_info *, const char *, void *), void *state)
401 {
402         char *p;
403         int received = 0;
404         BOOL first = True;
405         char status[21];
406         int num_asked = (cli->max_xmit - 100)/DIR_STRUCT_SIZE;
407         int num_received = 0;
408         int i;
409         char *dirlist = NULL;
410         pstring mask;
411         
412         ZERO_ARRAY(status);
413
414         pstrcpy(mask,Mask);
415   
416         while (1) {
417                 memset(cli->outbuf,'\0',smb_size);
418                 memset(cli->inbuf,'\0',smb_size);
419
420                 set_message(cli->outbuf,2,0,True);
421
422                 SCVAL(cli->outbuf,smb_com,SMBsearch);
423
424                 SSVAL(cli->outbuf,smb_tid,cli->cnum);
425                 cli_setup_packet(cli);
426
427                 SSVAL(cli->outbuf,smb_vwv0,num_asked);
428                 SSVAL(cli->outbuf,smb_vwv1,attribute);
429   
430                 p = smb_buf(cli->outbuf);
431                 *p++ = 4;
432       
433                 p += clistr_push(cli, p, first?mask:"", -1, STR_TERMINATE);
434                 *p++ = 5;
435                 if (first) {
436                         SSVAL(p,0,0);
437                         p += 2;
438                 } else {
439                         SSVAL(p,0,21);
440                         p += 2;
441                         memcpy(p,status,21);
442                         p += 21;
443                 }
444
445                 cli_setup_bcc(cli, p);
446                 cli_send_smb(cli);
447                 if (!cli_receive_smb(cli)) break;
448
449                 received = SVAL(cli->inbuf,smb_vwv0);
450                 if (received <= 0) break;
451
452                 first = False;
453
454                 dirlist = (char *)SMB_REALLOC(
455                         dirlist,(num_received + received)*DIR_STRUCT_SIZE);
456                 if (!dirlist) {
457                         DEBUG(0,("cli_list_old: failed to expand dirlist"));
458                         return 0;
459                 }
460
461                 p = smb_buf(cli->inbuf) + 3;
462
463                 memcpy(dirlist+num_received*DIR_STRUCT_SIZE,
464                        p,received*DIR_STRUCT_SIZE);
465                 
466                 memcpy(status,p + ((received-1)*DIR_STRUCT_SIZE),21);
467                 
468                 num_received += received;
469                 
470                 if (cli_is_error(cli)) break;
471         }
472
473         if (!first) {
474                 memset(cli->outbuf,'\0',smb_size);
475                 memset(cli->inbuf,'\0',smb_size);
476
477                 set_message(cli->outbuf,2,0,True);
478                 SCVAL(cli->outbuf,smb_com,SMBfclose);
479                 SSVAL(cli->outbuf,smb_tid,cli->cnum);
480                 cli_setup_packet(cli);
481
482                 SSVAL(cli->outbuf, smb_vwv0, 0); /* find count? */
483                 SSVAL(cli->outbuf, smb_vwv1, attribute);
484
485                 p = smb_buf(cli->outbuf);
486                 *p++ = 4;
487                 fstrcpy(p, "");
488                 p += strlen(p) + 1;
489                 *p++ = 5;
490                 SSVAL(p, 0, 21);
491                 p += 2;
492                 memcpy(p,status,21);
493                 p += 21;
494                 
495                 cli_setup_bcc(cli, p);
496                 cli_send_smb(cli);
497                 if (!cli_receive_smb(cli)) {
498                         DEBUG(0,("Error closing search: %s\n",cli_errstr(cli)));
499                 }
500         }
501
502         for (p=dirlist,i=0;i<num_received;i++) {
503                 file_info finfo;
504                 p += interpret_short_filename(cli, p,&finfo);
505                 fn("\\", &finfo, Mask, state);
506         }
507
508         SAFE_FREE(dirlist);
509         return(num_received);
510 }
511
512 /****************************************************************************
513  Do a directory listing, calling fn on each file found.
514  This auto-switches between old and new style.
515 ****************************************************************************/
516
517 int cli_list(struct cli_state *cli,const char *Mask,uint16 attribute, 
518              void (*fn)(const char *, file_info *, const char *, void *), void *state)
519 {
520         if (cli->protocol <= PROTOCOL_LANMAN1)
521                 return cli_list_old(cli, Mask, attribute, fn, state);
522         return cli_list_new(cli, Mask, attribute, fn, state);
523 }