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 extern file_info def_finfo
;
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 ****************************************************************************/
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
)
45 memcpy(finfo
,&def_finfo
,sizeof(*finfo
));
49 case 1: /* OS/2 understands this */
50 /* these dates are converted to GMT by
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);
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
64 p
+= clistr_pull(cli
, finfo
->name
, p
,
68 return PTR_DIFF(p
, base
);
70 case 2: /* this is what OS/2 uses mostly */
71 /* these dates are converted to GMT by
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 /* check for unisys! */
81 p
+= clistr_pull(cli
, finfo
->name
, p
,
85 return PTR_DIFF(p
, base
) + 1;
87 case 260: /* NT uses this, but also accepts 2 */
90 p
+= 4; /* next entry offset */
93 *p_resume_key
= IVAL(p
,0);
95 p
+= 4; /* fileindex */
97 /* Offset zero is "create time", not "change time". */
99 finfo
->atime_ts
= interpret_long_date(p
);
101 finfo
->mtime_ts
= interpret_long_date(p
);
103 finfo
->ctime_ts
= interpret_long_date(p
);
105 finfo
->size
= IVAL2_TO_SMB_BIG_UINT(p
,0);
107 p
+= 8; /* alloc size */
108 finfo
->mode
= CVAL(p
,0);
112 p
+= 4; /* EA size */
116 /* stupid NT bugs. grr */
118 if (p
[1] == 0 && namelen
> 1) flags
|= STR_UNICODE
;
119 clistr_pull(cli
, finfo
->short_name
, p
,
120 sizeof(finfo
->short_name
),
123 p
+= 24; /* short name? */
124 clistr_pull(cli
, finfo
->name
, p
,
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
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;
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;
143 return (size_t)IVAL(base
, 0);
147 DEBUG(1,("Unknown long filename format %d\n",level
));
148 return (size_t)IVAL(base
,0);
151 /****************************************************************************
152 Do a directory listing, calling fn on each file found.
153 ****************************************************************************/
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
)
159 int max_matches
= 1366; /* Match W2k - was 512. */
161 int max_matches
= 512;
168 char *dirlist
= NULL
;
170 int total_received
= -1;
172 int ff_searchcount
=0;
176 char *rparam
=NULL
, *rdata
=NULL
;
177 unsigned int param_len
, data_len
;
181 uint32 resume_key
= 0;
182 uint32 last_name_raw_len
= 0;
183 DATA_BLOB last_name_raw
= data_blob(NULL
, 2*sizeof(pstring
));
185 /* NT uses 260, OS/2 uses 2. Both accept 1. */
186 info_level
= (cli
->capabilities
&CAP_NT_SMBS
)?260:1;
190 while (ff_eos
== 0) {
192 if (loop_count
> 200) {
193 DEBUG(0,("Error: Looping in FIND_NEXT??\n"));
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
);
205 p
+= clistr_push(cli
, param
+12, mask
, sizeof(param
)-12,
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 */
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
;
223 p
+= clistr_push(cli
, param
+12, mask
, sizeof(param
)-12, STR_TERMINATE
);
227 param_len
= PTR_DIFF(p
, param
);
229 if (!cli_send_trans(cli
, SMBtrans2
,
231 -1, 0, /* fid, flags */
232 &setup
, 1, 0, /* setup, length, max */
233 param
, param_len
, 10, /* param, length, max */
237 MIN(16384,cli
->max_xmit
) /* data, length, max. */
239 cli
->max_xmit
/* data, length, max. */
245 if (!cli_receive_trans(cli
, SMBtrans2
,
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 */
257 cli_dos_error(cli
, &eclass
, &ecode
);
258 if (eclass
!= ERRSRV
|| ecode
!= ERRerror
)
264 if (cli_is_error(cli
) || !rdata
|| !rparam
) {
270 if (total_received
== -1)
273 /* parse out some important return info */
276 ff_dir_handle
= SVAL(p
,0);
277 ff_searchcount
= SVAL(p
,2);
280 ff_searchcount
= SVAL(p
,0);
284 if (ff_searchcount
== 0) {
290 /* point to the data bytes */
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
));
299 p2
+= interpret_long_filename(cli
,info_level
,p2
,&finfo
,
300 &resume_key
,&last_name_raw
,&last_name_raw_len
);
302 if (!First
&& *mask
&& strcsequal(finfo
.name
, mask
)) {
303 DEBUG(0,("Error: Looping in FIND_NEXT as name %s has already been seen?\n",
310 if (ff_searchcount
> 0) {
311 pstrcpy(mask
, finfo
.name
);
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
);
321 DEBUG(0,("cli_list_new: Failed to expand dirlist\n"));
327 memcpy(dirlist
+dirlist_len
,p
,data_len
);
328 dirlist_len
+= data_len
;
330 total_received
+= ff_searchcount
;
335 DEBUG(3,("received %d entries (eos=%d)\n",
336 ff_searchcount
,ff_eos
));
338 if (ff_searchcount
> 0)
344 mnt
= cli_cm_get_mntpoint( cli
);
346 /* see if the server disconnected or the connection otherwise failed */
347 if (cli_is_error(cli
)) {
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
);
358 /* free up the dirlist buffer and last name raw blob */
360 data_blob_free(&last_name_raw
);
361 return(total_received
);
364 /****************************************************************************
365 Interpret a short filename structure.
366 The length of the structure is returned.
367 ****************************************************************************/
369 static int interpret_short_filename(struct cli_state
*cli
, char *p
,file_info
*finfo
)
375 finfo
->mode
= CVAL(p
,21);
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';
389 return(DIR_STRUCT_SIZE
);
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 ****************************************************************************/
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
)
406 int num_asked
= (cli
->max_xmit
- 100)/DIR_STRUCT_SIZE
;
407 int num_received
= 0;
409 char *dirlist
= NULL
;
417 memset(cli
->outbuf
,'\0',smb_size
);
418 memset(cli
->inbuf
,'\0',smb_size
);
420 set_message(NULL
,cli
->outbuf
,2,0,True
);
422 SCVAL(cli
->outbuf
,smb_com
,SMBsearch
);
424 SSVAL(cli
->outbuf
,smb_tid
,cli
->cnum
);
425 cli_setup_packet(cli
);
427 SSVAL(cli
->outbuf
,smb_vwv0
,num_asked
);
428 SSVAL(cli
->outbuf
,smb_vwv1
,attribute
);
430 p
= smb_buf(cli
->outbuf
);
433 p
+= clistr_push(cli
, p
, first
?mask
:"", -1, STR_TERMINATE
);
445 cli_setup_bcc(cli
, p
);
447 if (!cli_receive_smb(cli
)) break;
449 received
= SVAL(cli
->inbuf
,smb_vwv0
);
450 if (received
<= 0) break;
454 dirlist
= (char *)SMB_REALLOC(
455 dirlist
,(num_received
+ received
)*DIR_STRUCT_SIZE
);
457 DEBUG(0,("cli_list_old: failed to expand dirlist"));
461 p
= smb_buf(cli
->inbuf
) + 3;
463 memcpy(dirlist
+num_received
*DIR_STRUCT_SIZE
,
464 p
,received
*DIR_STRUCT_SIZE
);
466 memcpy(status
,p
+ ((received
-1)*DIR_STRUCT_SIZE
),21);
468 num_received
+= received
;
470 if (cli_is_error(cli
)) break;
474 memset(cli
->outbuf
,'\0',smb_size
);
475 memset(cli
->inbuf
,'\0',smb_size
);
477 set_message(NULL
,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
);
482 SSVAL(cli
->outbuf
, smb_vwv0
, 0); /* find count? */
483 SSVAL(cli
->outbuf
, smb_vwv1
, attribute
);
485 p
= smb_buf(cli
->outbuf
);
495 cli_setup_bcc(cli
, p
);
497 if (!cli_receive_smb(cli
)) {
498 DEBUG(0,("Error closing search: %s\n",cli_errstr(cli
)));
502 for (p
=dirlist
,i
=0;i
<num_received
;i
++) {
504 p
+= interpret_short_filename(cli
, p
,&finfo
);
505 fn("\\", &finfo
, Mask
, state
);
509 return(num_received
);
512 /****************************************************************************
513 Do a directory listing, calling fn on each file found.
514 This auto-switches between old and new style.
515 ****************************************************************************/
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
)
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
);