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.
42 SMB_FIND_FILE_BOTH_DIRECTORY_INFO is used
43 by NT and SMB_FIND_EA_SIZE is used by OS/2
44 ****************************************************************************/
46 static size_t interpret_long_filename(TALLOC_CTX
*ctx
,
47 struct cli_state
*cli
,
52 const char *pdata_end
,
53 struct file_info
*finfo
,
55 DATA_BLOB
*p_last_name_raw
)
61 data_blob_free(p_last_name_raw
);
69 case SMB_FIND_INFO_STANDARD
: /* OS/2 understands this */
70 /* these dates are converted to GMT by
72 if (pdata_end
- base
< 27) {
73 return pdata_end
- base
;
75 finfo
->ctime_ts
= convert_time_t_to_timespec(
76 make_unix_date2(p
+4, cli
->serverzone
));
77 finfo
->atime_ts
= convert_time_t_to_timespec(
78 make_unix_date2(p
+8, cli
->serverzone
));
79 finfo
->mtime_ts
= convert_time_t_to_timespec(
80 make_unix_date2(p
+12, cli
->serverzone
));
81 finfo
->size
= IVAL(p
,16);
82 finfo
->mode
= CVAL(p
,24);
85 p
+= align_string(base_ptr
, p
, 0);
87 /* We can safely use len here (which is required by OS/2)
88 * and the NAS-BASIC server instead of +2 or +1 as the
89 * STR_TERMINATE flag below is
90 * actually used as the length calculation.
91 * The len is merely an upper bound.
92 * Due to the explicit 2 byte null termination
93 * in cli_receive_trans/cli_receive_nt_trans
94 * we know this is safe. JRA + kukks
97 if (p
+ len
> pdata_end
) {
98 return pdata_end
- base
;
101 /* the len+2 below looks strange but it is
102 important to cope with the differences
103 between win2000 and win9x for this call
105 ret
= clistr_pull_talloc(ctx
,
112 if (ret
== (size_t)-1) {
113 return pdata_end
- base
;
116 return PTR_DIFF(p
, base
);
118 case SMB_FIND_EA_SIZE
: /* this is what OS/2 uses mostly */
119 /* these dates are converted to GMT by
121 if (pdata_end
- base
< 31) {
122 return pdata_end
- base
;
124 finfo
->ctime_ts
= convert_time_t_to_timespec(
125 make_unix_date2(p
+4, cli
->serverzone
));
126 finfo
->atime_ts
= convert_time_t_to_timespec(
127 make_unix_date2(p
+8, cli
->serverzone
));
128 finfo
->mtime_ts
= convert_time_t_to_timespec(
129 make_unix_date2(p
+12, cli
->serverzone
));
130 finfo
->size
= IVAL(p
,16);
131 finfo
->mode
= CVAL(p
,24);
134 /* check for unisys! */
135 if (p
+ len
+ 1 > pdata_end
) {
136 return pdata_end
- base
;
138 ret
= clistr_pull_talloc(ctx
,
145 if (ret
== (size_t)-1) {
146 return pdata_end
- base
;
149 return PTR_DIFF(p
, base
) + 1;
151 case SMB_FIND_FILE_BOTH_DIRECTORY_INFO
: /* NT uses this, but also accepts 2 */
153 size_t namelen
, slen
;
155 if (pdata_end
- base
< 94) {
156 return pdata_end
- base
;
159 p
+= 4; /* next entry offset */
162 *p_resume_key
= IVAL(p
,0);
164 p
+= 4; /* fileindex */
166 /* Offset zero is "create time", not "change time". */
168 finfo
->atime_ts
= interpret_long_date(p
);
170 finfo
->mtime_ts
= interpret_long_date(p
);
172 finfo
->ctime_ts
= interpret_long_date(p
);
174 finfo
->size
= IVAL2_TO_SMB_BIG_UINT(p
,0);
176 p
+= 8; /* alloc size */
177 finfo
->mode
= CVAL(p
,0);
181 p
+= 4; /* EA size */
184 /* Bad short name length. */
185 return pdata_end
- base
;
189 /* stupid NT bugs. grr */
191 if (p
[1] == 0 && namelen
> 1) flags
|= STR_UNICODE
;
192 clistr_pull(base_ptr
, finfo
->short_name
, p
,
193 sizeof(finfo
->short_name
),
196 p
+= 24; /* short name? */
197 if (p
+ namelen
< p
|| p
+ namelen
> pdata_end
) {
198 return pdata_end
- base
;
200 ret
= clistr_pull_talloc(ctx
,
207 if (ret
== (size_t)-1) {
208 return pdata_end
- base
;
211 /* To be robust in the face of unicode conversion failures
212 we need to copy the raw bytes of the last name seen here.
213 Namelen doesn't include the terminating unicode null, so
216 if (p_last_name_raw
) {
217 *p_last_name_raw
= data_blob(NULL
, namelen
+2);
218 memcpy(p_last_name_raw
->data
, p
, namelen
);
219 SSVAL(p_last_name_raw
->data
, namelen
, 0);
221 return calc_next_entry_offset(base
, pdata_end
);
225 DEBUG(1,("Unknown long filename format %d\n",level
));
226 return calc_next_entry_offset(base
, pdata_end
);
229 /****************************************************************************
230 Do a directory listing, calling fn on each file found.
231 ****************************************************************************/
233 int cli_list_new(struct cli_state
*cli
,const char *Mask
,uint16 attribute
,
234 void (*fn
)(const char *, struct file_info
*, const char *,
235 void *), void *state
)
238 int max_matches
= 1366; /* Match W2k - was 512. */
240 int max_matches
= 512;
243 char *p
, *p2
, *rdata_end
;
245 struct file_info finfo
;
247 char *dirlist
= NULL
;
249 int total_received
= -1;
251 int ff_searchcount
=0;
255 char *rparam
=NULL
, *rdata
=NULL
;
256 unsigned int param_len
, data_len
;
259 uint32 resume_key
= 0;
260 TALLOC_CTX
*frame
= talloc_stackframe();
261 DATA_BLOB last_name_raw
= data_blob_null
;
263 /* NT uses SMB_FIND_FILE_BOTH_DIRECTORY_INFO,
264 OS/2 uses SMB_FIND_EA_SIZE. Both accept SMB_FIND_INFO_STANDARD. */
265 info_level
= (cli
->capabilities
&CAP_NT_SMBS
)?
266 SMB_FIND_FILE_BOTH_DIRECTORY_INFO
: SMB_FIND_INFO_STANDARD
;
268 mask
= SMB_STRDUP(Mask
);
276 while (ff_eos
== 0) {
277 size_t nlen
= 2*(strlen(mask
)+1);
280 if (loop_count
> 200) {
281 DEBUG(0,("Error: Looping in FIND_NEXT??\n"));
285 param
= SMB_MALLOC_ARRAY(char, 12+nlen
+last_name_raw
.length
+2);
291 setup
= TRANSACT2_FINDFIRST
;
292 SSVAL(param
,0,attribute
); /* attribute */
293 SSVAL(param
,2,max_matches
); /* max count */
294 SSVAL(param
,4,(FLAG_TRANS2_FIND_REQUIRE_RESUME
|FLAG_TRANS2_FIND_CLOSE_IF_END
)); /* resume required + close on end */
295 SSVAL(param
,6,info_level
);
298 p
+= clistr_push(cli
, param
+12, mask
,
299 nlen
, STR_TERMINATE
);
301 setup
= TRANSACT2_FINDNEXT
;
302 SSVAL(param
,0,ff_dir_handle
);
303 SSVAL(param
,2,max_matches
); /* max count */
304 SSVAL(param
,4,info_level
);
305 /* For W2K servers serving out FAT filesystems we *must* set the
306 resume key. If it's not FAT then it's returned as zero. */
307 SIVAL(param
,6,resume_key
); /* ff_resume_key */
308 /* NB. *DON'T* use continue here. If you do it seems that W2K and bretheren
309 can miss filenames. Use last filename continue instead. JRA */
310 SSVAL(param
,10,(FLAG_TRANS2_FIND_REQUIRE_RESUME
|FLAG_TRANS2_FIND_CLOSE_IF_END
)); /* resume required + close on end */
312 if (last_name_raw
.length
) {
313 memcpy(p
, last_name_raw
.data
, last_name_raw
.length
);
314 p
+= last_name_raw
.length
;
316 p
+= clistr_push(cli
, param
+12, mask
,
317 nlen
, STR_TERMINATE
);
321 param_len
= PTR_DIFF(p
, param
);
323 if (!cli_send_trans(cli
, SMBtrans2
,
325 -1, 0, /* fid, flags */
326 &setup
, 1, 0, /* setup, length, max */
327 param
, param_len
, 10, /* param, length, max */
331 MIN(16384,cli
->max_xmit
) /* data, length, max. */
333 cli
->max_xmit
/* data, length, max. */
343 if (!cli_receive_trans(cli
, SMBtrans2
,
345 &rdata
, &data_len
) &&
346 cli_is_dos_error(cli
)) {
347 /* We need to work around a Win95 bug - sometimes
348 it gives ERRSRV/ERRerror temprarily */
355 cli_dos_error(cli
, &eclass
, &ecode
);
358 * OS/2 might return "no more files",
359 * which just tells us, that searchcount is zero
361 * Guenter Kukkukk <linux@kukkukk.com>
364 if (eclass
== ERRDOS
&& ecode
== ERRnofiles
) {
366 cli_reset_error(cli
);
370 if (eclass
!= ERRSRV
|| ecode
!= ERRerror
)
376 if (cli_is_error(cli
) || !rdata
|| !rparam
) {
382 if (total_received
== -1)
385 /* parse out some important return info */
388 ff_dir_handle
= SVAL(p
,0);
389 ff_searchcount
= SVAL(p
,2);
392 ff_searchcount
= SVAL(p
,0);
396 if (ff_searchcount
== 0) {
402 /* point to the data bytes */
404 rdata_end
= rdata
+ data_len
;
406 /* we might need the lastname for continuations */
407 for (p2
=p
,i
=0;i
<ff_searchcount
&& p2
< rdata_end
;i
++) {
408 if ((info_level
== SMB_FIND_FILE_BOTH_DIRECTORY_INFO
) &&
409 (i
== ff_searchcount
-1)) {
410 /* Last entry - fixup the last offset length. */
411 SIVAL(p2
,0,PTR_DIFF((rdata
+ data_len
),p2
));
413 p2
+= interpret_long_filename(frame
,
417 SVAL(cli
->inbuf
, smb_flg2
),
425 DEBUG(0,("cli_list_new: Error: unable to parse name from info level %d\n",
430 if (!First
&& *mask
&& strcsequal(finfo
.name
, mask
)) {
431 DEBUG(0,("Error: Looping in FIND_NEXT as name %s has already been seen?\n",
439 if (ff_searchcount
> 0 && ff_eos
== 0 && finfo
.name
) {
440 mask
= SMB_STRDUP(finfo
.name
);
442 mask
= SMB_STRDUP("");
450 /* grab the data for later use */
451 /* and add them to the dirlist pool */
452 dirlist
= (char *)SMB_REALLOC(dirlist
,dirlist_len
+ data_len
);
455 DEBUG(0,("cli_list_new: Failed to expand dirlist\n"));
461 memcpy(dirlist
+dirlist_len
,p
,data_len
);
462 dirlist_len
+= data_len
;
464 total_received
+= ff_searchcount
;
469 DEBUG(3,("received %d entries (eos=%d)\n",
470 ff_searchcount
,ff_eos
));
472 if (ff_searchcount
> 0)
478 /* see if the server disconnected or the connection otherwise failed */
479 if (cli_is_error(cli
)) {
482 /* no connection problem. let user function add each entry */
483 rdata_end
= dirlist
+ dirlist_len
;
484 for (p
=dirlist
,i
=0;i
<total_received
;i
++) {
485 p
+= interpret_long_filename(frame
,
489 SVAL(cli
->inbuf
, smb_flg2
),
496 DEBUG(0,("cli_list_new: unable to parse name from info level %d\n",
500 fn(cli
->dfs_mountpoint
, &finfo
, Mask
, state
);
504 /* free up the dirlist buffer and last name raw blob */
506 data_blob_free(&last_name_raw
);
509 return(total_received
);
512 /****************************************************************************
513 Interpret a short filename structure.
514 The length of the structure is returned.
515 ****************************************************************************/
517 static bool interpret_short_filename(TALLOC_CTX
*ctx
,
518 struct cli_state
*cli
,
520 struct file_info
*finfo
)
525 finfo
->mode
= CVAL(p
,21);
527 /* this date is converted to GMT by make_unix_date */
528 finfo
->ctime_ts
.tv_sec
= make_unix_date(p
+22, cli
->serverzone
);
529 finfo
->ctime_ts
.tv_nsec
= 0;
530 finfo
->mtime_ts
.tv_sec
= finfo
->atime_ts
.tv_sec
= finfo
->ctime_ts
.tv_sec
;
531 finfo
->mtime_ts
.tv_nsec
= finfo
->atime_ts
.tv_nsec
= 0;
532 finfo
->size
= IVAL(p
,26);
533 ret
= clistr_pull_talloc(ctx
,
535 SVAL(cli
->inbuf
, smb_flg2
),
540 if (ret
== (size_t)-1) {
545 strlcpy(finfo
->short_name
,
547 sizeof(finfo
->short_name
));
552 /****************************************************************************
553 Do a directory listing, calling fn on each file found.
554 this uses the old SMBsearch interface. It is needed for testing Samba,
555 but should otherwise not be used.
556 ****************************************************************************/
558 int cli_list_old(struct cli_state
*cli
,const char *Mask
,uint16 attribute
,
559 void (*fn
)(const char *, struct file_info
*, const char *,
560 void *), void *state
)
566 int num_asked
= (cli
->max_xmit
- 100)/DIR_STRUCT_SIZE
;
567 int num_received
= 0;
569 char *dirlist
= NULL
;
571 TALLOC_CTX
*frame
= NULL
;
575 mask
= SMB_STRDUP(Mask
);
581 memset(cli
->outbuf
,'\0',smb_size
);
582 memset(cli
->inbuf
,'\0',smb_size
);
584 cli_set_message(cli
->outbuf
,2,0,True
);
586 SCVAL(cli
->outbuf
,smb_com
,SMBsearch
);
588 SSVAL(cli
->outbuf
,smb_tid
,cli
->cnum
);
589 cli_setup_packet(cli
);
591 SSVAL(cli
->outbuf
,smb_vwv0
,num_asked
);
592 SSVAL(cli
->outbuf
,smb_vwv1
,attribute
);
594 p
= smb_buf(cli
->outbuf
);
597 p
+= clistr_push(cli
, p
, first
?mask
:"",
598 cli
->bufsize
- PTR_DIFF(p
,cli
->outbuf
),
611 cli_setup_bcc(cli
, p
);
613 if (!cli_receive_smb(cli
)) break;
615 received
= SVAL(cli
->inbuf
,smb_vwv0
);
616 if (received
<= 0) break;
618 /* Ensure we received enough data. */
619 if ((cli
->inbuf
+4+smb_len(cli
->inbuf
) - (smb_buf(cli
->inbuf
)+3)) <
620 received
*DIR_STRUCT_SIZE
) {
626 dirlist
= (char *)SMB_REALLOC(
627 dirlist
,(num_received
+ received
)*DIR_STRUCT_SIZE
);
629 DEBUG(0,("cli_list_old: failed to expand dirlist"));
634 p
= smb_buf(cli
->inbuf
) + 3;
636 memcpy(dirlist
+num_received
*DIR_STRUCT_SIZE
,
637 p
,received
*DIR_STRUCT_SIZE
);
639 memcpy(status
,p
+ ((received
-1)*DIR_STRUCT_SIZE
),21);
641 num_received
+= received
;
643 if (cli_is_error(cli
)) break;
647 memset(cli
->outbuf
,'\0',smb_size
);
648 memset(cli
->inbuf
,'\0',smb_size
);
650 cli_set_message(cli
->outbuf
,2,0,True
);
651 SCVAL(cli
->outbuf
,smb_com
,SMBfclose
);
652 SSVAL(cli
->outbuf
,smb_tid
,cli
->cnum
);
653 cli_setup_packet(cli
);
655 SSVAL(cli
->outbuf
, smb_vwv0
, 0); /* find count? */
656 SSVAL(cli
->outbuf
, smb_vwv1
, attribute
);
658 p
= smb_buf(cli
->outbuf
);
668 cli_setup_bcc(cli
, p
);
670 if (!cli_receive_smb(cli
)) {
671 DEBUG(0,("Error closing search: %s\n",cli_errstr(cli
)));
675 frame
= talloc_stackframe();
676 for (p
=dirlist
,i
=0;i
<num_received
;i
++) {
677 struct file_info finfo
;
678 if (!interpret_short_filename(frame
, cli
, p
, &finfo
)) {
681 p
+= DIR_STRUCT_SIZE
;
682 fn("\\", &finfo
, Mask
, state
);
688 return(num_received
);
691 /****************************************************************************
692 Do a directory listing, calling fn on each file found.
693 This auto-switches between old and new style.
694 ****************************************************************************/
696 NTSTATUS
cli_list(struct cli_state
*cli
,const char *Mask
,uint16 attribute
,
697 void (*fn
)(const char *, struct file_info
*, const char *,
698 void *), void *state
)
702 if (cli
->protocol
<= PROTOCOL_LANMAN1
) {
703 rec
= cli_list_old(cli
, Mask
, attribute
, fn
, state
);
705 rec
= cli_list_new(cli
, Mask
, attribute
, fn
, state
);
708 return cli_nt_error(cli
);