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);
82
/* We can safely use len here (which is required by OS/2)
83
* and the NAS-BASIC server instead of +2 or +1 as the
84
* STR_TERMINATE flag below is
85
* actually used as the length calculation.
86
* The len is merely an upper bound.
87
* Due to the explicit 2 byte null termination
88
* in cli_receive_trans/cli_receive_nt_trans
89
* we know this is safe. JRA + kukks
92
if (p + len > pdata_end) {
93
return pdata_end - base;
96
/* the len+2 below looks strange but it is
97
important to cope with the differences
98
between win2000 and win9x for this call
100
ret = clistr_pull_talloc(ctx,
106
if (ret == (size_t)-1) {
107
return pdata_end - base;
110
return PTR_DIFF(p, base);
112
case 2: /* this is what OS/2 uses mostly */
113
/* these dates are converted to GMT by
115
if (pdata_end - base < 31) {
116
return pdata_end - base;
118
finfo->ctime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+4));
119
finfo->atime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+8));
120
finfo->mtime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+12));
121
finfo->size = IVAL(p,16);
122
finfo->mode = CVAL(p,24);
125
/* check for unisys! */
126
if (p + len + 1 > pdata_end) {
127
return pdata_end - base;
129
ret = clistr_pull_talloc(ctx,
135
if (ret == (size_t)-1) {
136
return pdata_end - base;
139
return PTR_DIFF(p, base) + 1;
141
case 260: /* NT uses this, but also accepts 2 */
143
size_t namelen, slen;
145
if (pdata_end - base < 94) {
146
return pdata_end - base;
149
p += 4; /* next entry offset */
152
*p_resume_key = IVAL(p,0);
154
p += 4; /* fileindex */
156
/* Offset zero is "create time", not "change time". */
158
finfo->atime_ts = interpret_long_date(p);
160
finfo->mtime_ts = interpret_long_date(p);
162
finfo->ctime_ts = interpret_long_date(p);
164
finfo->size = IVAL2_TO_SMB_BIG_UINT(p,0);
166
p += 8; /* alloc size */
167
finfo->mode = CVAL(p,0);
171
p += 4; /* EA size */
174
/* Bad short name length. */
175
return pdata_end - base;
179
/* stupid NT bugs. grr */
181
if (p[1] == 0 && namelen > 1) flags |= STR_UNICODE;
182
clistr_pull(cli->inbuf, finfo->short_name, p,
183
sizeof(finfo->short_name),
186
p += 24; /* short name? */
187
if (p + namelen < p || p + namelen > pdata_end) {
188
return pdata_end - base;
190
ret = clistr_pull_talloc(ctx,
196
if (ret == (size_t)-1) {
197
return pdata_end - base;
200
/* To be robust in the face of unicode conversion failures
201
we need to copy the raw bytes of the last name seen here.
202
Namelen doesn't include the terminating unicode null, so
205
if (p_last_name_raw) {
206
*p_last_name_raw = data_blob(NULL, namelen+2);
207
memcpy(p_last_name_raw->data, p, namelen);
208
SSVAL(p_last_name_raw->data, namelen, 0);
210
return calc_next_entry_offset(base, pdata_end);
214
DEBUG(1,("Unknown long filename format %d\n",level));
215
return calc_next_entry_offset(base, pdata_end);
218
/****************************************************************************
219
Do a directory listing, calling fn on each file found.
220
****************************************************************************/
222
int cli_list_new(struct cli_state *cli,const char *Mask,uint16 attribute,
223
void (*fn)(const char *, file_info *, const char *, void *), void *state)
226
int max_matches = 1366; /* Match W2k - was 512. */
228
int max_matches = 512;
231
char *p, *p2, *rdata_end;
235
char *dirlist = NULL;
237
int total_received = -1;
239
int ff_searchcount=0;
243
char *rparam=NULL, *rdata=NULL;
244
unsigned int param_len, data_len;
247
uint32 resume_key = 0;
248
TALLOC_CTX *frame = talloc_stackframe();
249
DATA_BLOB last_name_raw = data_blob(NULL, 0);
251
/* NT uses 260, OS/2 uses 2. Both accept 1. */
252
info_level = (cli->capabilities&CAP_NT_SMBS)?260:1;
254
mask = SMB_STRDUP(Mask);
260
while (ff_eos == 0) {
261
size_t nlen = 2*(strlen(mask)+1);
264
if (loop_count > 200) {
265
DEBUG(0,("Error: Looping in FIND_NEXT??\n"));
269
param = SMB_MALLOC_ARRAY(char, 12+nlen+last_name_raw.length+2);
275
setup = TRANSACT2_FINDFIRST;
276
SSVAL(param,0,attribute); /* attribute */
277
SSVAL(param,2,max_matches); /* max count */
278
SSVAL(param,4,(FLAG_TRANS2_FIND_REQUIRE_RESUME|FLAG_TRANS2_FIND_CLOSE_IF_END)); /* resume required + close on end */
279
SSVAL(param,6,info_level);
282
p += clistr_push(cli, param+12, mask,
283
nlen, STR_TERMINATE);
285
setup = TRANSACT2_FINDNEXT;
286
SSVAL(param,0,ff_dir_handle);
287
SSVAL(param,2,max_matches); /* max count */
288
SSVAL(param,4,info_level);
289
/* For W2K servers serving out FAT filesystems we *must* set the
290
resume key. If it's not FAT then it's returned as zero. */
291
SIVAL(param,6,resume_key); /* ff_resume_key */
292
/* NB. *DON'T* use continue here. If you do it seems that W2K and bretheren
293
can miss filenames. Use last filename continue instead. JRA */
294
SSVAL(param,10,(FLAG_TRANS2_FIND_REQUIRE_RESUME|FLAG_TRANS2_FIND_CLOSE_IF_END)); /* resume required + close on end */
296
if (last_name_raw.length) {
297
memcpy(p, last_name_raw.data, last_name_raw.length);
298
p += last_name_raw.length;
300
p += clistr_push(cli, param+12, mask,
301
nlen, STR_TERMINATE);
305
param_len = PTR_DIFF(p, param);
307
if (!cli_send_trans(cli, SMBtrans2,
309
-1, 0, /* fid, flags */
310
&setup, 1, 0, /* setup, length, max */
311
param, param_len, 10, /* param, length, max */
315
MIN(16384,cli->max_xmit) /* data, length, max. */
317
cli->max_xmit /* data, length, max. */
327
if (!cli_receive_trans(cli, SMBtrans2,
329
&rdata, &data_len) &&
330
cli_is_dos_error(cli)) {
331
/* We need to work around a Win95 bug - sometimes
332
it gives ERRSRV/ERRerror temprarily */
339
cli_dos_error(cli, &eclass, &ecode);
342
* OS/2 might return "no more files",
343
* which just tells us, that searchcount is zero
345
* Guenter Kukkukk <linux@kukkukk.com>
348
if (eclass == ERRDOS && ecode == ERRnofiles) {
350
cli_reset_error(cli);
354
if (eclass != ERRSRV || ecode != ERRerror)
360
if (cli_is_error(cli) || !rdata || !rparam) {
366
if (total_received == -1)
369
/* parse out some important return info */
372
ff_dir_handle = SVAL(p,0);
373
ff_searchcount = SVAL(p,2);
376
ff_searchcount = SVAL(p,0);
380
if (ff_searchcount == 0) {
386
/* point to the data bytes */
388
rdata_end = rdata + data_len;
390
/* we might need the lastname for continuations */
391
for (p2=p,i=0;i<ff_searchcount && p2 < rdata_end;i++) {
392
if ((info_level == 260) && (i == ff_searchcount-1)) {
393
/* Last entry - fixup the last offset length. */
394
SIVAL(p2,0,PTR_DIFF((rdata + data_len),p2));
396
p2 += interpret_long_filename(frame,
406
DEBUG(0,("cli_list_new: Error: unable to parse name from info level %d\n",
411
if (!First && *mask && strcsequal(finfo.name, mask)) {
412
DEBUG(0,("Error: Looping in FIND_NEXT as name %s has already been seen?\n",
420
if (ff_searchcount > 0 && ff_eos == 0 && finfo.name) {
421
mask = SMB_STRDUP(finfo.name);
423
mask = SMB_STRDUP("");
431
/* grab the data for later use */
432
/* and add them to the dirlist pool */
433
dirlist = (char *)SMB_REALLOC(dirlist,dirlist_len + data_len);
436
DEBUG(0,("cli_list_new: Failed to expand dirlist\n"));
442
memcpy(dirlist+dirlist_len,p,data_len);
443
dirlist_len += data_len;
445
total_received += ff_searchcount;
450
DEBUG(3,("received %d entries (eos=%d)\n",
451
ff_searchcount,ff_eos));
453
if (ff_searchcount > 0)
459
/* see if the server disconnected or the connection otherwise failed */
460
if (cli_is_error(cli)) {
463
/* no connection problem. let user function add each entry */
464
rdata_end = dirlist + dirlist_len;
465
for (p=dirlist,i=0;i<total_received;i++) {
466
p += interpret_long_filename(frame,
475
DEBUG(0,("cli_list_new: unable to parse name from info level %d\n",
479
fn(cli->dfs_mountpoint, &finfo, Mask, state);
483
/* free up the dirlist buffer and last name raw blob */
485
data_blob_free(&last_name_raw);
488
return(total_received);
491
/****************************************************************************
492
Interpret a short filename structure.
493
The length of the structure is returned.
494
****************************************************************************/
496
static bool interpret_short_filename(TALLOC_CTX *ctx,
497
struct cli_state *cli,
505
finfo->mode = CVAL(p,21);
507
/* this date is converted to GMT by make_unix_date */
508
finfo->ctime_ts.tv_sec = cli_make_unix_date(cli, p+22);
509
finfo->ctime_ts.tv_nsec = 0;
510
finfo->mtime_ts.tv_sec = finfo->atime_ts.tv_sec = finfo->ctime_ts.tv_sec;
511
finfo->mtime_ts.tv_nsec = finfo->atime_ts.tv_nsec = 0;
512
finfo->size = IVAL(p,26);
513
ret = clistr_pull_talloc(ctx,
519
if (ret == (size_t)-1) {
524
strlcpy(finfo->short_name,
526
sizeof(finfo->short_name));
529
return(DIR_STRUCT_SIZE);
532
/****************************************************************************
533
Do a directory listing, calling fn on each file found.
534
this uses the old SMBsearch interface. It is needed for testing Samba,
535
but should otherwise not be used.
536
****************************************************************************/
538
int cli_list_old(struct cli_state *cli,const char *Mask,uint16 attribute,
539
void (*fn)(const char *, file_info *, const char *, void *), void *state)
545
int num_asked = (cli->max_xmit - 100)/DIR_STRUCT_SIZE;
546
int num_received = 0;
548
char *dirlist = NULL;
550
TALLOC_CTX *frame = NULL;
554
mask = SMB_STRDUP(Mask);
560
memset(cli->outbuf,'\0',smb_size);
561
memset(cli->inbuf,'\0',smb_size);
563
cli_set_message(cli->outbuf,2,0,True);
565
SCVAL(cli->outbuf,smb_com,SMBsearch);
567
SSVAL(cli->outbuf,smb_tid,cli->cnum);
568
cli_setup_packet(cli);
570
SSVAL(cli->outbuf,smb_vwv0,num_asked);
571
SSVAL(cli->outbuf,smb_vwv1,attribute);
573
p = smb_buf(cli->outbuf);
576
p += clistr_push(cli, p, first?mask:"",
577
cli->bufsize - PTR_DIFF(p,cli->outbuf),
590
cli_setup_bcc(cli, p);
592
if (!cli_receive_smb(cli)) break;
594
received = SVAL(cli->inbuf,smb_vwv0);
595
if (received <= 0) break;
597
/* Ensure we received enough data. */
598
if ((cli->inbuf+4+smb_len(cli->inbuf) - (smb_buf(cli->inbuf)+3)) <
599
received*DIR_STRUCT_SIZE) {
605
dirlist = (char *)SMB_REALLOC(
606
dirlist,(num_received + received)*DIR_STRUCT_SIZE);
608
DEBUG(0,("cli_list_old: failed to expand dirlist"));
613
p = smb_buf(cli->inbuf) + 3;
615
memcpy(dirlist+num_received*DIR_STRUCT_SIZE,
616
p,received*DIR_STRUCT_SIZE);
618
memcpy(status,p + ((received-1)*DIR_STRUCT_SIZE),21);
620
num_received += received;
622
if (cli_is_error(cli)) break;
626
memset(cli->outbuf,'\0',smb_size);
627
memset(cli->inbuf,'\0',smb_size);
629
cli_set_message(cli->outbuf,2,0,True);
630
SCVAL(cli->outbuf,smb_com,SMBfclose);
631
SSVAL(cli->outbuf,smb_tid,cli->cnum);
632
cli_setup_packet(cli);
634
SSVAL(cli->outbuf, smb_vwv0, 0); /* find count? */
635
SSVAL(cli->outbuf, smb_vwv1, attribute);
637
p = smb_buf(cli->outbuf);
647
cli_setup_bcc(cli, p);
649
if (!cli_receive_smb(cli)) {
650
DEBUG(0,("Error closing search: %s\n",cli_errstr(cli)));
654
frame = talloc_stackframe();
655
for (p=dirlist,i=0;i<num_received;i++) {
657
if (!interpret_short_filename(frame, cli, p, &finfo)) {
660
p += DIR_STRUCT_SIZE;
661
fn("\\", &finfo, Mask, state);
667
return(num_received);
670
/****************************************************************************
671
Do a directory listing, calling fn on each file found.
672
This auto-switches between old and new style.
673
****************************************************************************/
675
int cli_list(struct cli_state *cli,const char *Mask,uint16 attribute,
676
void (*fn)(const char *, file_info *, const char *, void *), void *state)
678
if (cli->protocol <= PROTOCOL_LANMAN1)
679
return cli_list_old(cli, Mask, attribute, fn, state);
680
return cli_list_new(cli, Mask, attribute, fn, state);