WHATSNEW: Update changes.
[Samba.git] / source3 / libsmb / clilist.c
blob3af1b0dcc06379f85e05a034c638b001aec80474
1 /*
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/>.
20 #include "includes.h"
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,
48 int level,
49 const char *p,
50 const char *pdata_end,
51 struct file_info *finfo,
52 uint32 *p_resume_key,
53 DATA_BLOB *p_last_name_raw)
55 int len;
56 size_t ret;
57 const char *base = p;
59 data_blob_free(p_last_name_raw);
61 if (p_resume_key) {
62 *p_resume_key = 0;
64 ZERO_STRUCTP(finfo);
65 finfo->cli = cli;
67 switch (level) {
68 case SMB_FIND_INFO_STANDARD: /* OS/2 understands this */
69 /* these dates are converted to GMT by
70 make_unix_date */
71 if (pdata_end - base < 27) {
72 return pdata_end - base;
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);
79 len = CVAL(p, 26);
80 p += 27;
81 p += clistr_align_in(cli, p, 0);
83 /* We can safely use len here (which is required by OS/2)
84 * and the NAS-BASIC server instead of +2 or +1 as the
85 * STR_TERMINATE flag below is
86 * actually used as the length calculation.
87 * The len is merely an upper bound.
88 * Due to the explicit 2 byte null termination
89 * in cli_receive_trans/cli_receive_nt_trans
90 * we know this is safe. JRA + kukks
93 if (p + len > pdata_end) {
94 return pdata_end - base;
97 /* the len+2 below looks strange but it is
98 important to cope with the differences
99 between win2000 and win9x for this call
100 (tridge) */
101 ret = clistr_pull_talloc(ctx,
102 cli->inbuf,
103 &finfo->name,
105 len+2,
106 STR_TERMINATE);
107 if (ret == (size_t)-1) {
108 return pdata_end - base;
110 p += ret;
111 return PTR_DIFF(p, base);
113 case SMB_FIND_EA_SIZE: /* this is what OS/2 uses mostly */
114 /* these dates are converted to GMT by
115 make_unix_date */
116 if (pdata_end - base < 31) {
117 return pdata_end - base;
119 finfo->ctime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+4));
120 finfo->atime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+8));
121 finfo->mtime_ts = convert_time_t_to_timespec(cli_make_unix_date2(cli, p+12));
122 finfo->size = IVAL(p,16);
123 finfo->mode = CVAL(p,24);
124 len = CVAL(p, 30);
125 p += 31;
126 /* check for unisys! */
127 if (p + len + 1 > pdata_end) {
128 return pdata_end - base;
130 ret = clistr_pull_talloc(ctx,
131 cli->inbuf,
132 &finfo->name,
134 len,
135 STR_NOALIGN);
136 if (ret == (size_t)-1) {
137 return pdata_end - base;
139 p += ret;
140 return PTR_DIFF(p, base) + 1;
142 case SMB_FIND_FILE_BOTH_DIRECTORY_INFO: /* NT uses this, but also accepts 2 */
144 size_t namelen, slen;
146 if (pdata_end - base < 94) {
147 return pdata_end - base;
150 p += 4; /* next entry offset */
152 if (p_resume_key) {
153 *p_resume_key = IVAL(p,0);
155 p += 4; /* fileindex */
157 /* Offset zero is "create time", not "change time". */
158 p += 8;
159 finfo->atime_ts = interpret_long_date(p);
160 p += 8;
161 finfo->mtime_ts = interpret_long_date(p);
162 p += 8;
163 finfo->ctime_ts = interpret_long_date(p);
164 p += 8;
165 finfo->size = IVAL2_TO_SMB_BIG_UINT(p,0);
166 p += 8;
167 p += 8; /* alloc size */
168 finfo->mode = CVAL(p,0);
169 p += 4;
170 namelen = IVAL(p,0);
171 p += 4;
172 p += 4; /* EA size */
173 slen = SVAL(p, 0);
174 if (slen > 24) {
175 /* Bad short name length. */
176 return pdata_end - base;
178 p += 2;
180 /* stupid NT bugs. grr */
181 int flags = 0;
182 if (p[1] == 0 && namelen > 1) flags |= STR_UNICODE;
183 clistr_pull(cli->inbuf, finfo->short_name, p,
184 sizeof(finfo->short_name),
185 slen, flags);
187 p += 24; /* short name? */
188 if (p + namelen < p || p + namelen > pdata_end) {
189 return pdata_end - base;
191 ret = clistr_pull_talloc(ctx,
192 cli->inbuf,
193 &finfo->name,
195 namelen,
197 if (ret == (size_t)-1) {
198 return pdata_end - base;
201 /* To be robust in the face of unicode conversion failures
202 we need to copy the raw bytes of the last name seen here.
203 Namelen doesn't include the terminating unicode null, so
204 copy it here. */
206 if (p_last_name_raw) {
207 *p_last_name_raw = data_blob(NULL, namelen+2);
208 memcpy(p_last_name_raw->data, p, namelen);
209 SSVAL(p_last_name_raw->data, namelen, 0);
211 return calc_next_entry_offset(base, pdata_end);
215 DEBUG(1,("Unknown long filename format %d\n",level));
216 return calc_next_entry_offset(base, pdata_end);
219 /****************************************************************************
220 Do a directory listing, calling fn on each file found.
221 ****************************************************************************/
223 int cli_list_new(struct cli_state *cli,const char *Mask,uint16 attribute,
224 void (*fn)(const char *, struct file_info *, const char *,
225 void *), void *state)
227 #if 1
228 int max_matches = 1366; /* Match W2k - was 512. */
229 #else
230 int max_matches = 512;
231 #endif
232 int info_level;
233 char *p, *p2, *rdata_end;
234 char *mask = NULL;
235 struct file_info finfo;
236 int i;
237 char *dirlist = NULL;
238 int dirlist_len = 0;
239 int total_received = -1;
240 bool First = True;
241 int ff_searchcount=0;
242 int ff_eos=0;
243 int ff_dir_handle=0;
244 int loop_count = 0;
245 char *rparam=NULL, *rdata=NULL;
246 unsigned int param_len, data_len;
247 uint16 setup;
248 char *param;
249 uint32 resume_key = 0;
250 TALLOC_CTX *frame = talloc_stackframe();
251 DATA_BLOB last_name_raw = data_blob(NULL, 0);
253 /* NT uses SMB_FIND_FILE_BOTH_DIRECTORY_INFO,
254 OS/2 uses SMB_FIND_EA_SIZE. Both accept SMB_FIND_INFO_STANDARD. */
255 info_level = (cli->capabilities&CAP_NT_SMBS)?
256 SMB_FIND_FILE_BOTH_DIRECTORY_INFO : SMB_FIND_INFO_STANDARD;
258 mask = SMB_STRDUP(Mask);
259 if (!mask) {
260 TALLOC_FREE(frame);
261 return -1;
264 ZERO_STRUCT(finfo);
266 while (ff_eos == 0) {
267 size_t nlen = 2*(strlen(mask)+1);
269 loop_count++;
270 if (loop_count > 200) {
271 DEBUG(0,("Error: Looping in FIND_NEXT??\n"));
272 break;
275 param = SMB_MALLOC_ARRAY(char, 12+nlen+last_name_raw.length+2);
276 if (!param) {
277 break;
280 if (First) {
281 setup = TRANSACT2_FINDFIRST;
282 SSVAL(param,0,attribute); /* attribute */
283 SSVAL(param,2,max_matches); /* max count */
284 SSVAL(param,4,(FLAG_TRANS2_FIND_REQUIRE_RESUME|FLAG_TRANS2_FIND_CLOSE_IF_END)); /* resume required + close on end */
285 SSVAL(param,6,info_level);
286 SIVAL(param,8,0);
287 p = param+12;
288 p += clistr_push(cli, param+12, mask,
289 nlen, STR_TERMINATE);
290 } else {
291 setup = TRANSACT2_FINDNEXT;
292 SSVAL(param,0,ff_dir_handle);
293 SSVAL(param,2,max_matches); /* max count */
294 SSVAL(param,4,info_level);
295 /* For W2K servers serving out FAT filesystems we *must* set the
296 resume key. If it's not FAT then it's returned as zero. */
297 SIVAL(param,6,resume_key); /* ff_resume_key */
298 /* NB. *DON'T* use continue here. If you do it seems that W2K and bretheren
299 can miss filenames. Use last filename continue instead. JRA */
300 SSVAL(param,10,(FLAG_TRANS2_FIND_REQUIRE_RESUME|FLAG_TRANS2_FIND_CLOSE_IF_END)); /* resume required + close on end */
301 p = param+12;
302 if (last_name_raw.length) {
303 memcpy(p, last_name_raw.data, last_name_raw.length);
304 p += last_name_raw.length;
305 } else {
306 p += clistr_push(cli, param+12, mask,
307 nlen, STR_TERMINATE);
311 param_len = PTR_DIFF(p, param);
313 if (!cli_send_trans(cli, SMBtrans2,
314 NULL, /* Name */
315 -1, 0, /* fid, flags */
316 &setup, 1, 0, /* setup, length, max */
317 param, param_len, 10, /* param, length, max */
318 NULL, 0,
319 #if 0
320 /* w2k value. */
321 MIN(16384,cli->max_xmit) /* data, length, max. */
322 #else
323 cli->max_xmit /* data, length, max. */
324 #endif
325 )) {
326 SAFE_FREE(param);
327 TALLOC_FREE(frame);
328 break;
331 SAFE_FREE(param);
333 if (!cli_receive_trans(cli, SMBtrans2,
334 &rparam, &param_len,
335 &rdata, &data_len) &&
336 cli_is_dos_error(cli)) {
337 /* We need to work around a Win95 bug - sometimes
338 it gives ERRSRV/ERRerror temprarily */
339 uint8 eclass;
340 uint32 ecode;
342 SAFE_FREE(rdata);
343 SAFE_FREE(rparam);
345 cli_dos_error(cli, &eclass, &ecode);
348 * OS/2 might return "no more files",
349 * which just tells us, that searchcount is zero
350 * in this search.
351 * Guenter Kukkukk <linux@kukkukk.com>
354 if (eclass == ERRDOS && ecode == ERRnofiles) {
355 ff_searchcount = 0;
356 cli_reset_error(cli);
357 break;
360 if (eclass != ERRSRV || ecode != ERRerror)
361 break;
362 smb_msleep(100);
363 continue;
366 if (cli_is_error(cli) || !rdata || !rparam) {
367 SAFE_FREE(rdata);
368 SAFE_FREE(rparam);
369 break;
372 if (total_received == -1)
373 total_received = 0;
375 /* parse out some important return info */
376 p = rparam;
377 if (First) {
378 ff_dir_handle = SVAL(p,0);
379 ff_searchcount = SVAL(p,2);
380 ff_eos = SVAL(p,4);
381 } else {
382 ff_searchcount = SVAL(p,0);
383 ff_eos = SVAL(p,2);
386 if (ff_searchcount == 0) {
387 SAFE_FREE(rdata);
388 SAFE_FREE(rparam);
389 break;
392 /* point to the data bytes */
393 p = rdata;
394 rdata_end = rdata + data_len;
396 /* we might need the lastname for continuations */
397 for (p2=p,i=0;i<ff_searchcount && p2 < rdata_end;i++) {
398 if ((info_level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO) &&
399 (i == ff_searchcount-1)) {
400 /* Last entry - fixup the last offset length. */
401 SIVAL(p2,0,PTR_DIFF((rdata + data_len),p2));
403 p2 += interpret_long_filename(frame,
404 cli,
405 info_level,
407 rdata_end,
408 &finfo,
409 &resume_key,
410 &last_name_raw);
412 if (!finfo.name) {
413 DEBUG(0,("cli_list_new: Error: unable to parse name from info level %d\n",
414 info_level));
415 ff_eos = 1;
416 break;
418 if (!First && *mask && strcsequal(finfo.name, mask)) {
419 DEBUG(0,("Error: Looping in FIND_NEXT as name %s has already been seen?\n",
420 finfo.name));
421 ff_eos = 1;
422 break;
426 SAFE_FREE(mask);
427 if (ff_searchcount > 0 && ff_eos == 0 && finfo.name) {
428 mask = SMB_STRDUP(finfo.name);
429 } else {
430 mask = SMB_STRDUP("");
432 if (!mask) {
433 SAFE_FREE(rdata);
434 SAFE_FREE(rparam);
435 break;
438 /* grab the data for later use */
439 /* and add them to the dirlist pool */
440 dirlist = (char *)SMB_REALLOC(dirlist,dirlist_len + data_len);
442 if (!dirlist) {
443 DEBUG(0,("cli_list_new: Failed to expand dirlist\n"));
444 SAFE_FREE(rdata);
445 SAFE_FREE(rparam);
446 break;
449 memcpy(dirlist+dirlist_len,p,data_len);
450 dirlist_len += data_len;
452 total_received += ff_searchcount;
454 SAFE_FREE(rdata);
455 SAFE_FREE(rparam);
457 DEBUG(3,("received %d entries (eos=%d)\n",
458 ff_searchcount,ff_eos));
460 if (ff_searchcount > 0)
461 loop_count = 0;
463 First = False;
466 /* see if the server disconnected or the connection otherwise failed */
467 if (cli_is_error(cli)) {
468 total_received = -1;
469 } else {
470 /* no connection problem. let user function add each entry */
471 rdata_end = dirlist + dirlist_len;
472 for (p=dirlist,i=0;i<total_received;i++) {
473 p += interpret_long_filename(frame,
474 cli,
475 info_level,
477 rdata_end,
478 &finfo,
479 NULL,
480 NULL);
481 if (!finfo.name) {
482 DEBUG(0,("cli_list_new: unable to parse name from info level %d\n",
483 info_level));
484 break;
486 fn(cli->dfs_mountpoint, &finfo, Mask, state);
490 /* free up the dirlist buffer and last name raw blob */
491 SAFE_FREE(dirlist);
492 data_blob_free(&last_name_raw);
493 SAFE_FREE(mask);
494 TALLOC_FREE(frame);
495 return(total_received);
498 /****************************************************************************
499 Interpret a short filename structure.
500 The length of the structure is returned.
501 ****************************************************************************/
503 static bool interpret_short_filename(TALLOC_CTX *ctx,
504 struct cli_state *cli,
505 char *p,
506 struct file_info *finfo)
508 size_t ret;
509 ZERO_STRUCTP(finfo);
511 finfo->cli = cli;
512 finfo->mode = CVAL(p,21);
514 /* this date is converted to GMT by make_unix_date */
515 finfo->ctime_ts.tv_sec = cli_make_unix_date(cli, p+22);
516 finfo->ctime_ts.tv_nsec = 0;
517 finfo->mtime_ts.tv_sec = finfo->atime_ts.tv_sec = finfo->ctime_ts.tv_sec;
518 finfo->mtime_ts.tv_nsec = finfo->atime_ts.tv_nsec = 0;
519 finfo->size = IVAL(p,26);
520 ret = clistr_pull_talloc(ctx,
521 cli->inbuf,
522 &finfo->name,
523 p+30,
525 STR_ASCII);
526 if (ret == (size_t)-1) {
527 return false;
530 if (finfo->name) {
531 strlcpy(finfo->short_name,
532 finfo->name,
533 sizeof(finfo->short_name));
535 return true;
538 /****************************************************************************
539 Do a directory listing, calling fn on each file found.
540 this uses the old SMBsearch interface. It is needed for testing Samba,
541 but should otherwise not be used.
542 ****************************************************************************/
544 int cli_list_old(struct cli_state *cli,const char *Mask,uint16 attribute,
545 void (*fn)(const char *, struct file_info *, const char *,
546 void *), void *state)
548 char *p;
549 int received = 0;
550 bool first = True;
551 char status[21];
552 int num_asked = (cli->max_xmit - 100)/DIR_STRUCT_SIZE;
553 int num_received = 0;
554 int i;
555 char *dirlist = NULL;
556 char *mask = NULL;
557 TALLOC_CTX *frame = NULL;
559 ZERO_ARRAY(status);
561 mask = SMB_STRDUP(Mask);
562 if (!mask) {
563 return -1;
566 while (1) {
567 memset(cli->outbuf,'\0',smb_size);
568 memset(cli->inbuf,'\0',smb_size);
570 cli_set_message(cli->outbuf,2,0,True);
572 SCVAL(cli->outbuf,smb_com,SMBsearch);
574 SSVAL(cli->outbuf,smb_tid,cli->cnum);
575 cli_setup_packet(cli);
577 SSVAL(cli->outbuf,smb_vwv0,num_asked);
578 SSVAL(cli->outbuf,smb_vwv1,attribute);
580 p = smb_buf(cli->outbuf);
581 *p++ = 4;
583 p += clistr_push(cli, p, first?mask:"",
584 cli->bufsize - PTR_DIFF(p,cli->outbuf),
585 STR_TERMINATE);
586 *p++ = 5;
587 if (first) {
588 SSVAL(p,0,0);
589 p += 2;
590 } else {
591 SSVAL(p,0,21);
592 p += 2;
593 memcpy(p,status,21);
594 p += 21;
597 cli_setup_bcc(cli, p);
598 cli_send_smb(cli);
599 if (!cli_receive_smb(cli)) break;
601 received = SVAL(cli->inbuf,smb_vwv0);
602 if (received <= 0) break;
604 /* Ensure we received enough data. */
605 if ((cli->inbuf+4+smb_len(cli->inbuf) - (smb_buf(cli->inbuf)+3)) <
606 received*DIR_STRUCT_SIZE) {
607 break;
610 first = False;
612 dirlist = (char *)SMB_REALLOC(
613 dirlist,(num_received + received)*DIR_STRUCT_SIZE);
614 if (!dirlist) {
615 DEBUG(0,("cli_list_old: failed to expand dirlist"));
616 SAFE_FREE(mask);
617 return 0;
620 p = smb_buf(cli->inbuf) + 3;
622 memcpy(dirlist+num_received*DIR_STRUCT_SIZE,
623 p,received*DIR_STRUCT_SIZE);
625 memcpy(status,p + ((received-1)*DIR_STRUCT_SIZE),21);
627 num_received += received;
629 if (cli_is_error(cli)) break;
632 if (!first) {
633 memset(cli->outbuf,'\0',smb_size);
634 memset(cli->inbuf,'\0',smb_size);
636 cli_set_message(cli->outbuf,2,0,True);
637 SCVAL(cli->outbuf,smb_com,SMBfclose);
638 SSVAL(cli->outbuf,smb_tid,cli->cnum);
639 cli_setup_packet(cli);
641 SSVAL(cli->outbuf, smb_vwv0, 0); /* find count? */
642 SSVAL(cli->outbuf, smb_vwv1, attribute);
644 p = smb_buf(cli->outbuf);
645 *p++ = 4;
646 fstrcpy(p, "");
647 p += strlen(p) + 1;
648 *p++ = 5;
649 SSVAL(p, 0, 21);
650 p += 2;
651 memcpy(p,status,21);
652 p += 21;
654 cli_setup_bcc(cli, p);
655 cli_send_smb(cli);
656 if (!cli_receive_smb(cli)) {
657 DEBUG(0,("Error closing search: %s\n",cli_errstr(cli)));
661 frame = talloc_stackframe();
662 for (p=dirlist,i=0;i<num_received;i++) {
663 struct file_info finfo;
664 if (!interpret_short_filename(frame, cli, p, &finfo)) {
665 break;
667 p += DIR_STRUCT_SIZE;
668 fn("\\", &finfo, Mask, state);
670 TALLOC_FREE(frame);
672 SAFE_FREE(mask);
673 SAFE_FREE(dirlist);
674 return(num_received);
677 /****************************************************************************
678 Do a directory listing, calling fn on each file found.
679 This auto-switches between old and new style.
680 ****************************************************************************/
682 int cli_list(struct cli_state *cli,const char *Mask,uint16 attribute,
683 void (*fn)(const char *, struct file_info *, const char *,
684 void *), void *state)
686 if (cli->protocol <= PROTOCOL_LANMAN1)
687 return cli_list_old(cli, Mask, attribute, fn, state);
688 return cli_list_new(cli, Mask, attribute, fn, state);