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 3 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, see <http://www.gnu.org/licenses/>.
22 /****************************************************************************
23 Calculate a safe next_entry_offset.
24 ****************************************************************************/
26 static size_t calc_next_entry_offset(const char *base
, const char *pdata_end
)
28 size_t next_entry_offset
= (size_t)IVAL(base
,0);
30 if (next_entry_offset
== 0 ||
31 base
+ next_entry_offset
< base
||
32 base
+ next_entry_offset
> pdata_end
) {
33 next_entry_offset
= pdata_end
- base
;
35 return next_entry_offset
;
38 /****************************************************************************
39 Interpret a long filename structure - this is mostly guesses at the moment.
40 The length of the structure is returned
41 The structure of a long filename depends on the info level. 260 is used
42 by NT and 2 is used by OS/2
43 ****************************************************************************/
45 static size_t interpret_long_filename(TALLOC_CTX
*ctx
,
46 struct cli_state
*cli
,
49 const char *pdata_end
,
52 DATA_BLOB
*p_last_name_raw
)
58 data_blob_free(p_last_name_raw
);
67 case 1: /* OS/2 understands this */
68 /* these dates are converted to GMT by
70 if (pdata_end
- base
< 27) {
71 return pdata_end
- base
;
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);
80 p
+= clistr_align_in(cli
, p
, 0);
81 if (p
+ len
+ 2 > pdata_end
) {
82 return pdata_end
- base
;
84 /* the len+2 below looks strange but it is
85 important to cope with the differences
86 between win2000 and win9x for this call
88 ret
= clistr_pull_talloc(ctx
,
94 if (ret
== (size_t)-1) {
95 return pdata_end
- base
;
98 return PTR_DIFF(p
, base
);
100 case 2: /* this is what OS/2 uses mostly */
101 /* these dates are converted to GMT by
103 if (pdata_end
- base
< 31) {
104 return pdata_end
- base
;
106 finfo
->ctime_ts
= convert_time_t_to_timespec(cli_make_unix_date2(cli
, p
+4));
107 finfo
->atime_ts
= convert_time_t_to_timespec(cli_make_unix_date2(cli
, p
+8));
108 finfo
->mtime_ts
= convert_time_t_to_timespec(cli_make_unix_date2(cli
, p
+12));
109 finfo
->size
= IVAL(p
,16);
110 finfo
->mode
= CVAL(p
,24);
113 /* check for unisys! */
114 if (p
+ len
+ 1 > pdata_end
) {
115 return pdata_end
- base
;
117 ret
= clistr_pull_talloc(ctx
,
123 if (ret
== (size_t)-1) {
124 return pdata_end
- base
;
127 return PTR_DIFF(p
, base
) + 1;
129 case 260: /* NT uses this, but also accepts 2 */
131 size_t namelen
, slen
;
133 if (pdata_end
- base
< 94) {
134 return pdata_end
- base
;
137 p
+= 4; /* next entry offset */
140 *p_resume_key
= IVAL(p
,0);
142 p
+= 4; /* fileindex */
144 /* Offset zero is "create time", not "change time". */
146 finfo
->atime_ts
= interpret_long_date(p
);
148 finfo
->mtime_ts
= interpret_long_date(p
);
150 finfo
->ctime_ts
= interpret_long_date(p
);
152 finfo
->size
= IVAL2_TO_SMB_BIG_UINT(p
,0);
154 p
+= 8; /* alloc size */
155 finfo
->mode
= CVAL(p
,0);
159 p
+= 4; /* EA size */
162 /* Bad short name length. */
163 return pdata_end
- base
;
167 /* stupid NT bugs. grr */
169 if (p
[1] == 0 && namelen
> 1) flags
|= STR_UNICODE
;
170 clistr_pull(cli
, finfo
->short_name
, p
,
171 sizeof(finfo
->short_name
),
174 p
+= 24; /* short name? */
175 if (p
+ namelen
< p
|| p
+ namelen
> pdata_end
) {
176 return pdata_end
- base
;
178 ret
= clistr_pull_talloc(ctx
,
184 if (ret
== (size_t)-1) {
185 return pdata_end
- base
;
188 /* To be robust in the face of unicode conversion failures
189 we need to copy the raw bytes of the last name seen here.
190 Namelen doesn't include the terminating unicode null, so
193 if (p_last_name_raw
) {
194 *p_last_name_raw
= data_blob(NULL
, namelen
+2);
195 memcpy(p_last_name_raw
->data
, p
, namelen
);
196 SSVAL(p_last_name_raw
->data
, namelen
, 0);
198 return calc_next_entry_offset(base
, pdata_end
);
202 DEBUG(1,("Unknown long filename format %d\n",level
));
203 return calc_next_entry_offset(base
, pdata_end
);
206 /****************************************************************************
207 Do a directory listing, calling fn on each file found.
208 ****************************************************************************/
210 int cli_list_new(struct cli_state
*cli
,const char *Mask
,uint16 attribute
,
211 void (*fn
)(const char *, file_info
*, const char *, void *), void *state
)
214 int max_matches
= 1366; /* Match W2k - was 512. */
216 int max_matches
= 512;
219 char *p
, *p2
, *rdata_end
;
223 char *dirlist
= NULL
;
225 int total_received
= -1;
227 int ff_searchcount
=0;
231 char *rparam
=NULL
, *rdata
=NULL
;
232 unsigned int param_len
, data_len
;
236 uint32 resume_key
= 0;
237 TALLOC_CTX
*frame
= talloc_stackframe();
238 DATA_BLOB last_name_raw
= data_blob(NULL
, 0);
240 /* NT uses 260, OS/2 uses 2. Both accept 1. */
241 info_level
= (cli
->capabilities
&CAP_NT_SMBS
)?260:1;
243 mask
= SMB_STRDUP(Mask
);
249 while (ff_eos
== 0) {
250 size_t nlen
= 2*(strlen(mask
)+1);
253 if (loop_count
> 200) {
254 DEBUG(0,("Error: Looping in FIND_NEXT??\n"));
258 param
= SMB_MALLOC_ARRAY(char, 12+nlen
+last_name_raw
.length
+2);
264 setup
= TRANSACT2_FINDFIRST
;
265 SSVAL(param
,0,attribute
); /* attribute */
266 SSVAL(param
,2,max_matches
); /* max count */
267 SSVAL(param
,4,(FLAG_TRANS2_FIND_REQUIRE_RESUME
|FLAG_TRANS2_FIND_CLOSE_IF_END
)); /* resume required + close on end */
268 SSVAL(param
,6,info_level
);
271 p
+= clistr_push(cli
, param
+12, mask
,
272 nlen
, STR_TERMINATE
);
274 setup
= TRANSACT2_FINDNEXT
;
275 SSVAL(param
,0,ff_dir_handle
);
276 SSVAL(param
,2,max_matches
); /* max count */
277 SSVAL(param
,4,info_level
);
278 /* For W2K servers serving out FAT filesystems we *must* set the
279 resume key. If it's not FAT then it's returned as zero. */
280 SIVAL(param
,6,resume_key
); /* ff_resume_key */
281 /* NB. *DON'T* use continue here. If you do it seems that W2K and bretheren
282 can miss filenames. Use last filename continue instead. JRA */
283 SSVAL(param
,10,(FLAG_TRANS2_FIND_REQUIRE_RESUME
|FLAG_TRANS2_FIND_CLOSE_IF_END
)); /* resume required + close on end */
285 if (last_name_raw
.length
) {
286 memcpy(p
, last_name_raw
.data
, last_name_raw
.length
);
287 p
+= last_name_raw
.length
;
289 p
+= clistr_push(cli
, param
+12, mask
,
290 nlen
, STR_TERMINATE
);
294 param_len
= PTR_DIFF(p
, param
);
296 if (!cli_send_trans(cli
, SMBtrans2
,
298 -1, 0, /* fid, flags */
299 &setup
, 1, 0, /* setup, length, max */
300 param
, param_len
, 10, /* param, length, max */
304 MIN(16384,cli
->max_xmit
) /* data, length, max. */
306 cli
->max_xmit
/* data, length, max. */
316 if (!cli_receive_trans(cli
, SMBtrans2
,
318 &rdata
, &data_len
) &&
319 cli_is_dos_error(cli
)) {
320 /* we need to work around a Win95 bug - sometimes
321 it gives ERRSRV/ERRerror temprarily */
328 cli_dos_error(cli
, &eclass
, &ecode
);
329 if (eclass
!= ERRSRV
|| ecode
!= ERRerror
)
335 if (cli_is_error(cli
) || !rdata
|| !rparam
) {
341 if (total_received
== -1)
344 /* parse out some important return info */
347 ff_dir_handle
= SVAL(p
,0);
348 ff_searchcount
= SVAL(p
,2);
351 ff_searchcount
= SVAL(p
,0);
355 if (ff_searchcount
== 0) {
361 /* point to the data bytes */
363 rdata_end
= rdata
+ data_len
;
365 /* we might need the lastname for continuations */
366 for (p2
=p
,i
=0;i
<ff_searchcount
&& p2
< rdata_end
;i
++) {
367 if ((info_level
== 260) && (i
== ff_searchcount
-1)) {
368 /* Last entry - fixup the last offset length. */
369 SIVAL(p2
,0,PTR_DIFF((rdata
+ data_len
),p2
));
371 p2
+= interpret_long_filename(frame
,
380 if (!First
&& *mask
&& strcsequal(finfo
.name
, mask
)) {
381 DEBUG(0,("Error: Looping in FIND_NEXT as name %s has already been seen?\n",
389 if (ff_searchcount
> 0) {
390 mask
= SMB_STRDUP(finfo
.name
);
392 mask
= SMB_STRDUP("");
400 /* grab the data for later use */
401 /* and add them to the dirlist pool */
402 dirlist
= (char *)SMB_REALLOC(dirlist
,dirlist_len
+ data_len
);
405 DEBUG(0,("cli_list_new: Failed to expand dirlist\n"));
411 memcpy(dirlist
+dirlist_len
,p
,data_len
);
412 dirlist_len
+= data_len
;
414 total_received
+= ff_searchcount
;
419 DEBUG(3,("received %d entries (eos=%d)\n",
420 ff_searchcount
,ff_eos
));
422 if (ff_searchcount
> 0)
428 mnt
= cli_cm_get_mntpoint( cli
);
430 /* see if the server disconnected or the connection otherwise failed */
431 if (cli_is_error(cli
)) {
434 /* no connection problem. let user function add each entry */
435 rdata_end
= dirlist
+ dirlist_len
;
436 for (p
=dirlist
,i
=0;i
<total_received
;i
++) {
437 p
+= interpret_long_filename(frame
,
445 fn(mnt
,&finfo
, Mask
, state
);
449 /* free up the dirlist buffer and last name raw blob */
451 data_blob_free(&last_name_raw
);
454 return(total_received
);
457 /****************************************************************************
458 Interpret a short filename structure.
459 The length of the structure is returned.
460 ****************************************************************************/
462 static int interpret_short_filename(TALLOC_CTX
*ctx
,
463 struct cli_state
*cli
,
470 finfo
->mode
= CVAL(p
,21);
472 /* this date is converted to GMT by make_unix_date */
473 finfo
->ctime_ts
.tv_sec
= cli_make_unix_date(cli
, p
+22);
474 finfo
->ctime_ts
.tv_nsec
= 0;
475 finfo
->mtime_ts
.tv_sec
= finfo
->atime_ts
.tv_sec
= finfo
->ctime_ts
.tv_sec
;
476 finfo
->mtime_ts
.tv_nsec
= finfo
->atime_ts
.tv_nsec
= 0;
477 finfo
->size
= IVAL(p
,26);
478 clistr_pull_talloc(ctx
,
485 finfo
->name
= talloc_strdup(ctx
, finfo
->short_name
);
486 } else if (strcmp(finfo
->name
, "..") && strcmp(finfo
->name
, ".")) {
487 finfo
->name
= talloc_strdup(ctx
, finfo
->short_name
);
490 return(DIR_STRUCT_SIZE
);
493 /****************************************************************************
494 Do a directory listing, calling fn on each file found.
495 this uses the old SMBsearch interface. It is needed for testing Samba,
496 but should otherwise not be used.
497 ****************************************************************************/
499 int cli_list_old(struct cli_state
*cli
,const char *Mask
,uint16 attribute
,
500 void (*fn
)(const char *, file_info
*, const char *, void *), void *state
)
506 int num_asked
= (cli
->max_xmit
- 100)/DIR_STRUCT_SIZE
;
507 int num_received
= 0;
509 char *dirlist
= NULL
;
511 TALLOC_CTX
*frame
= NULL
;
515 mask
= SMB_STRDUP(Mask
);
521 memset(cli
->outbuf
,'\0',smb_size
);
522 memset(cli
->inbuf
,'\0',smb_size
);
524 cli_set_message(cli
->outbuf
,2,0,True
);
526 SCVAL(cli
->outbuf
,smb_com
,SMBsearch
);
528 SSVAL(cli
->outbuf
,smb_tid
,cli
->cnum
);
529 cli_setup_packet(cli
);
531 SSVAL(cli
->outbuf
,smb_vwv0
,num_asked
);
532 SSVAL(cli
->outbuf
,smb_vwv1
,attribute
);
534 p
= smb_buf(cli
->outbuf
);
537 p
+= clistr_push(cli
, p
, first
?mask
:"",
538 cli
->bufsize
- PTR_DIFF(p
,cli
->outbuf
),
551 cli_setup_bcc(cli
, p
);
553 if (!cli_receive_smb(cli
)) break;
555 received
= SVAL(cli
->inbuf
,smb_vwv0
);
556 if (received
<= 0) break;
560 dirlist
= (char *)SMB_REALLOC(
561 dirlist
,(num_received
+ received
)*DIR_STRUCT_SIZE
);
563 DEBUG(0,("cli_list_old: failed to expand dirlist"));
568 p
= smb_buf(cli
->inbuf
) + 3;
570 memcpy(dirlist
+num_received
*DIR_STRUCT_SIZE
,
571 p
,received
*DIR_STRUCT_SIZE
);
573 memcpy(status
,p
+ ((received
-1)*DIR_STRUCT_SIZE
),21);
575 num_received
+= received
;
577 if (cli_is_error(cli
)) break;
581 memset(cli
->outbuf
,'\0',smb_size
);
582 memset(cli
->inbuf
,'\0',smb_size
);
584 cli_set_message(cli
->outbuf
,2,0,True
);
585 SCVAL(cli
->outbuf
,smb_com
,SMBfclose
);
586 SSVAL(cli
->outbuf
,smb_tid
,cli
->cnum
);
587 cli_setup_packet(cli
);
589 SSVAL(cli
->outbuf
, smb_vwv0
, 0); /* find count? */
590 SSVAL(cli
->outbuf
, smb_vwv1
, attribute
);
592 p
= smb_buf(cli
->outbuf
);
602 cli_setup_bcc(cli
, p
);
604 if (!cli_receive_smb(cli
)) {
605 DEBUG(0,("Error closing search: %s\n",cli_errstr(cli
)));
609 frame
= talloc_stackframe();
610 for (p
=dirlist
,i
=0;i
<num_received
;i
++) {
612 p
+= interpret_short_filename(frame
,cli
, p
,&finfo
);
613 fn("\\", &finfo
, Mask
, state
);
619 return(num_received
);
622 /****************************************************************************
623 Do a directory listing, calling fn on each file found.
624 This auto-switches between old and new style.
625 ****************************************************************************/
627 int cli_list(struct cli_state
*cli
,const char *Mask
,uint16 attribute
,
628 void (*fn
)(const char *, file_info
*, const char *, void *), void *state
)
630 if (cli
->protocol
<= PROTOCOL_LANMAN1
)
631 return cli_list_old(cli
, Mask
, attribute
, fn
, state
);
632 return cli_list_new(cli
, Mask
, attribute
, fn
, state
);