[GLUE] Rsync SAMBA_3_0 SVN r25598 in order to create the v3-0-test branch.
[Samba.git] / source / libsmb / clilist.c
blob3e76cd47754db9815bff5ac4320131dcfed19ebc
1 /*
2 Unix SMB/CIFS implementation.
3 client directory list routines
4 Copyright (C) Andrew Tridgell 1994-1998
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.
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.
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.
21 #include "includes.h"
23 extern file_info def_finfo;
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 ****************************************************************************/
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)
35 file_info finfo2;
36 int len;
37 char *base = p;
39 if (!finfo) {
40 finfo = &finfo2;
43 if (p_resume_key) {
44 *p_resume_key = 0;
46 memcpy(finfo,&def_finfo,sizeof(*finfo));
47 finfo->cli = cli;
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);
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;
88 case 260: /* NT uses this, but also accepts 2 */
90 size_t namelen, slen;
91 p += 4; /* next entry offset */
93 if (p_resume_key) {
94 *p_resume_key = IVAL(p,0);
96 p += 4; /* fileindex */
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;
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);
124 p += 24; /* short name? */
125 clistr_pull(cli, finfo->name, p,
126 sizeof(finfo->name),
127 namelen, 0);
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. */
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;
144 return (size_t)IVAL(base, 0);
148 DEBUG(1,("Unknown long filename format %d\n",level));
149 return (size_t)IVAL(base,0);
152 /****************************************************************************
153 Do a directory listing, calling fn on each file found.
154 ****************************************************************************/
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)
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));
186 /* NT uses 260, OS/2 uses 2. Both accept 1. */
187 info_level = (cli->capabilities&CAP_NT_SMBS)?260:1;
189 pstrcpy(mask,Mask);
191 while (ff_eos == 0) {
192 loop_count++;
193 if (loop_count > 200) {
194 DEBUG(0,("Error: Looping in FIND_NEXT??\n"));
195 break;
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);
228 param_len = PTR_DIFF(p, param);
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;
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;
255 SAFE_FREE(rdata);
256 SAFE_FREE(rparam);
258 cli_dos_error(cli, &eclass, &ecode);
259 if (eclass != ERRSRV || ecode != ERRerror)
260 break;
261 smb_msleep(100);
262 continue;
265 if (cli_is_error(cli) || !rdata || !rparam) {
266 SAFE_FREE(rdata);
267 SAFE_FREE(rparam);
268 break;
271 if (total_received == -1)
272 total_received = 0;
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);
285 if (ff_searchcount == 0) {
286 SAFE_FREE(rdata);
287 SAFE_FREE(rparam);
288 break;
291 /* point to the data bytes */
292 p = rdata;
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));
300 p2 += interpret_long_filename(cli,info_level,p2,&finfo,
301 &resume_key,&last_name_raw,&last_name_raw_len);
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;
311 if (ff_searchcount > 0) {
312 pstrcpy(mask, finfo.name);
313 } else {
314 pstrcpy(mask,"");
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);
321 if (!dirlist) {
322 DEBUG(0,("cli_list_new: Failed to expand dirlist\n"));
323 SAFE_FREE(rdata);
324 SAFE_FREE(rparam);
325 break;
328 memcpy(dirlist+dirlist_len,p,data_len);
329 dirlist_len += data_len;
331 total_received += ff_searchcount;
333 SAFE_FREE(rdata);
334 SAFE_FREE(rparam);
336 DEBUG(3,("received %d entries (eos=%d)\n",
337 ff_searchcount,ff_eos));
339 if (ff_searchcount > 0)
340 loop_count = 0;
342 First = False;
345 mnt = cli_cm_get_mntpoint( cli );
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 );
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);
365 /****************************************************************************
366 Interpret a short filename structure.
367 The length of the structure is returned.
368 ****************************************************************************/
370 static int interpret_short_filename(struct cli_state *cli, char *p,file_info *finfo)
373 *finfo = def_finfo;
375 finfo->cli = cli;
376 finfo->mode = CVAL(p,21);
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';
390 return(DIR_STRUCT_SIZE);
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 ****************************************************************************/
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)
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;
413 ZERO_ARRAY(status);
415 pstrcpy(mask,Mask);
417 while (1) {
418 memset(cli->outbuf,'\0',smb_size);
419 memset(cli->inbuf,'\0',smb_size);
421 set_message(cli->outbuf,2,0,True);
423 SCVAL(cli->outbuf,smb_com,SMBsearch);
425 SSVAL(cli->outbuf,smb_tid,cli->cnum);
426 cli_setup_packet(cli);
428 SSVAL(cli->outbuf,smb_vwv0,num_asked);
429 SSVAL(cli->outbuf,smb_vwv1,attribute);
431 p = smb_buf(cli->outbuf);
432 *p++ = 4;
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;
446 cli_setup_bcc(cli, p);
447 cli_send_smb(cli);
448 if (!cli_receive_smb(cli)) break;
450 received = SVAL(cli->inbuf,smb_vwv0);
451 if (received <= 0) break;
453 first = False;
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;
462 p = smb_buf(cli->inbuf) + 3;
464 memcpy(dirlist+num_received*DIR_STRUCT_SIZE,
465 p,received*DIR_STRUCT_SIZE);
467 memcpy(status,p + ((received-1)*DIR_STRUCT_SIZE),21);
469 num_received += received;
471 if (cli_is_error(cli)) break;
474 if (!first) {
475 memset(cli->outbuf,'\0',smb_size);
476 memset(cli->inbuf,'\0',smb_size);
478 set_message(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);
483 SSVAL(cli->outbuf, smb_vwv0, 0); /* find count? */
484 SSVAL(cli->outbuf, smb_vwv1, attribute);
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;
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)));
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);
509 SAFE_FREE(dirlist);
510 return(num_received);
513 /****************************************************************************
514 Do a directory listing, calling fn on each file found.
515 This auto-switches between old and new style.
516 ****************************************************************************/
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)
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);