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.
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)
46 memcpy(finfo,&def_finfo,sizeof(*finfo));
50 case 1: /* OS/2 understands this */
51 /* these dates are converted to GMT by
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);
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
65 p += clistr_pull(cli, finfo->name, p,
69 return PTR_DIFF(p, base);
71 case 2: /* this is what OS/2 uses mostly */
72 /* these dates are converted to GMT by
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);
81 /* check for unisys! */
82 p += clistr_pull(cli, finfo->name, p,
86 return PTR_DIFF(p, base) + 1;
88 case 260: /* NT uses this, but also accepts 2 */
91 p += 4; /* next entry offset */
94 *p_resume_key = IVAL(p,0);
96 p += 4; /* fileindex */
98 /* Offset zero is "create time", not "change time". */
100 finfo->atime_ts = interpret_long_date(p);
102 finfo->mtime_ts = interpret_long_date(p);
104 finfo->ctime_ts = interpret_long_date(p);
106 finfo->size = IVAL2_TO_SMB_BIG_UINT(p,0);
108 p += 8; /* alloc size */
109 finfo->mode = CVAL(p,0);
113 p += 4; /* EA size */
117 /* stupid NT bugs. grr */
119 if (p[1] == 0 && namelen > 1) flags |= STR_UNICODE;
120 clistr_pull(cli, finfo->short_name, p,
121 sizeof(finfo->short_name),
124 p += 24; /* short name? */
125 clistr_pull(cli, finfo->name, p,
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
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;
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)
160 int max_matches = 1366; /* Match W2k - was 512. */
162 int max_matches = 512;
169 char *dirlist = NULL;
171 int total_received = -1;
173 int ff_searchcount=0;
177 char *rparam=NULL, *rdata=NULL;
178 unsigned int param_len, data_len;
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;
191 while (ff_eos == 0) {
193 if (loop_count > 200) {
194 DEBUG(0,("Error: Looping in FIND_NEXT??\n"));
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);
206 p += clistr_push(cli, param+12, mask, sizeof(param)-12,
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 */
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;
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,
232 -1, 0, /* fid, flags */
233 &setup, 1, 0, /* setup, length, max */
234 param, param_len, 10, /* param, length, max */
238 MIN(16384,cli->max_xmit) /* data, length, max. */
240 cli->max_xmit /* data, length, max. */
246 if (!cli_receive_trans(cli, SMBtrans2,
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 */
258 cli_dos_error(cli, &eclass, &ecode);
259 if (eclass != ERRSRV || ecode != ERRerror)
265 if (cli_is_error(cli) || !rdata || !rparam) {
271 if (total_received == -1)
274 /* parse out some important return info */
277 ff_dir_handle = SVAL(p,0);
278 ff_searchcount = SVAL(p,2);
281 ff_searchcount = SVAL(p,0);
285 if (ff_searchcount == 0) {
291 /* point to the data bytes */
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",
311 if (ff_searchcount > 0) {
312 pstrcpy(mask, finfo.name);
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);
322 DEBUG(0,("cli_list_new: Failed to expand dirlist\n"));
328 memcpy(dirlist+dirlist_len,p,data_len);
329 dirlist_len += data_len;
331 total_received += ff_searchcount;
336 DEBUG(3,("received %d entries (eos=%d)\n",
337 ff_searchcount,ff_eos));
339 if (ff_searchcount > 0)
345 mnt = cli_cm_get_mntpoint( cli );
347 /* see if the server disconnected or the connection otherwise failed */
348 if (cli_is_error(cli)) {
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 */
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)
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)
407 int num_asked = (cli->max_xmit - 100)/DIR_STRUCT_SIZE;
408 int num_received = 0;
410 char *dirlist = NULL;
418 memset(cli->outbuf,'\0',smb_size);
419 memset(cli->inbuf,'\0',smb_size);
421 set_message(NULL,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);
434 p += clistr_push(cli, p, first?mask:"", -1, STR_TERMINATE);
446 cli_setup_bcc(cli, p);
448 if (!cli_receive_smb(cli)) break;
450 received = SVAL(cli->inbuf,smb_vwv0);
451 if (received <= 0) break;
455 dirlist = (char *)SMB_REALLOC(
456 dirlist,(num_received + received)*DIR_STRUCT_SIZE);
458 DEBUG(0,("cli_list_old: failed to expand dirlist"));
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;
475 memset(cli->outbuf,'\0',smb_size);
476 memset(cli->inbuf,'\0',smb_size);
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);
483 SSVAL(cli->outbuf, smb_vwv0, 0); /* find count? */
484 SSVAL(cli->outbuf, smb_vwv1, attribute);
486 p = smb_buf(cli->outbuf);
496 cli_setup_bcc(cli, p);
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++) {
505 p += interpret_short_filename(cli, p,&finfo);
506 fn("\\", &finfo, Mask, state);
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);