ntlm_auth: Fix another typo in the test.
[Samba.git] / source / libsmb / clilist.c
blobd5c7db09e998c4dfd55962a82d3bd4c6bd03628c
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. 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,
47 int level,
48 const char *p,
49 const char *pdata_end,
50 file_info *finfo,
51 uint32 *p_resume_key,
52 DATA_BLOB *p_last_name_raw)
54 int len;
55 size_t ret;
56 const char *base = p;
58 data_blob_free(p_last_name_raw);
60 if (p_resume_key) {
61 *p_resume_key = 0;
63 ZERO_STRUCTP(finfo);
64 finfo->cli = cli;
66 switch (level) {
67 case 1: /* OS/2 understands this */
68 /* these dates are converted to GMT by
69 make_unix_date */
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);
78 len = CVAL(p, 26);
79 p += 27;
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
87 (tridge) */
88 ret = clistr_pull_talloc(ctx,
89 cli,
90 &finfo->name,
92 len+2,
93 STR_TERMINATE);
94 if (ret == (size_t)-1) {
95 return pdata_end - base;
97 p += ret;
98 return PTR_DIFF(p, base);
100 case 2: /* this is what OS/2 uses mostly */
101 /* these dates are converted to GMT by
102 make_unix_date */
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);
111 len = CVAL(p, 30);
112 p += 31;
113 /* check for unisys! */
114 if (p + len + 1 > pdata_end) {
115 return pdata_end - base;
117 ret = clistr_pull_talloc(ctx,
118 cli,
119 &finfo->name,
121 len,
122 STR_NOALIGN);
123 if (ret == (size_t)-1) {
124 return pdata_end - base;
126 p += ret;
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 */
139 if (p_resume_key) {
140 *p_resume_key = IVAL(p,0);
142 p += 4; /* fileindex */
144 /* Offset zero is "create time", not "change time". */
145 p += 8;
146 finfo->atime_ts = interpret_long_date(p);
147 p += 8;
148 finfo->mtime_ts = interpret_long_date(p);
149 p += 8;
150 finfo->ctime_ts = interpret_long_date(p);
151 p += 8;
152 finfo->size = IVAL2_TO_SMB_BIG_UINT(p,0);
153 p += 8;
154 p += 8; /* alloc size */
155 finfo->mode = CVAL(p,0);
156 p += 4;
157 namelen = IVAL(p,0);
158 p += 4;
159 p += 4; /* EA size */
160 slen = SVAL(p, 0);
161 if (slen > 24) {
162 /* Bad short name length. */
163 return pdata_end - base;
165 p += 2;
167 /* stupid NT bugs. grr */
168 int flags = 0;
169 if (p[1] == 0 && namelen > 1) flags |= STR_UNICODE;
170 clistr_pull(cli, finfo->short_name, p,
171 sizeof(finfo->short_name),
172 slen, flags);
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,
179 cli,
180 &finfo->name,
182 namelen,
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
191 copy it here. */
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)
213 #if 1
214 int max_matches = 1366; /* Match W2k - was 512. */
215 #else
216 int max_matches = 512;
217 #endif
218 int info_level;
219 char *p, *p2, *rdata_end;
220 char *mask = NULL;
221 file_info finfo;
222 int i;
223 char *dirlist = NULL;
224 int dirlist_len = 0;
225 int total_received = -1;
226 bool First = True;
227 int ff_searchcount=0;
228 int ff_eos=0;
229 int ff_dir_handle=0;
230 int loop_count = 0;
231 char *rparam=NULL, *rdata=NULL;
232 unsigned int param_len, data_len;
233 uint16 setup;
234 char *param;
235 const char *mnt;
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);
244 if (!mask) {
245 TALLOC_FREE(frame);
246 return -1;
249 while (ff_eos == 0) {
250 size_t nlen = 2*(strlen(mask)+1);
252 loop_count++;
253 if (loop_count > 200) {
254 DEBUG(0,("Error: Looping in FIND_NEXT??\n"));
255 break;
258 param = SMB_MALLOC_ARRAY(char, 12+nlen+last_name_raw.length+2);
259 if (!param) {
260 break;
263 if (First) {
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);
269 SIVAL(param,8,0);
270 p = param+12;
271 p += clistr_push(cli, param+12, mask,
272 nlen, STR_TERMINATE);
273 } else {
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 */
284 p = param+12;
285 if (last_name_raw.length) {
286 memcpy(p, last_name_raw.data, last_name_raw.length);
287 p += last_name_raw.length;
288 } else {
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,
297 NULL, /* Name */
298 -1, 0, /* fid, flags */
299 &setup, 1, 0, /* setup, length, max */
300 param, param_len, 10, /* param, length, max */
301 NULL, 0,
302 #if 0
303 /* w2k value. */
304 MIN(16384,cli->max_xmit) /* data, length, max. */
305 #else
306 cli->max_xmit /* data, length, max. */
307 #endif
308 )) {
309 SAFE_FREE(param);
310 TALLOC_FREE(frame);
311 break;
314 SAFE_FREE(param);
316 if (!cli_receive_trans(cli, SMBtrans2,
317 &rparam, &param_len,
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 */
322 uint8 eclass;
323 uint32 ecode;
325 SAFE_FREE(rdata);
326 SAFE_FREE(rparam);
328 cli_dos_error(cli, &eclass, &ecode);
329 if (eclass != ERRSRV || ecode != ERRerror)
330 break;
331 smb_msleep(100);
332 continue;
335 if (cli_is_error(cli) || !rdata || !rparam) {
336 SAFE_FREE(rdata);
337 SAFE_FREE(rparam);
338 break;
341 if (total_received == -1)
342 total_received = 0;
344 /* parse out some important return info */
345 p = rparam;
346 if (First) {
347 ff_dir_handle = SVAL(p,0);
348 ff_searchcount = SVAL(p,2);
349 ff_eos = SVAL(p,4);
350 } else {
351 ff_searchcount = SVAL(p,0);
352 ff_eos = SVAL(p,2);
355 if (ff_searchcount == 0) {
356 SAFE_FREE(rdata);
357 SAFE_FREE(rparam);
358 break;
361 /* point to the data bytes */
362 p = rdata;
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,
372 cli,
373 info_level,
375 rdata_end,
376 &finfo,
377 &resume_key,
378 &last_name_raw);
380 if (!First && *mask && strcsequal(finfo.name, mask)) {
381 DEBUG(0,("Error: Looping in FIND_NEXT as name %s has already been seen?\n",
382 finfo.name));
383 ff_eos = 1;
384 break;
388 SAFE_FREE(mask);
389 if (ff_searchcount > 0) {
390 mask = SMB_STRDUP(finfo.name);
391 } else {
392 mask = SMB_STRDUP("");
394 if (!mask) {
395 SAFE_FREE(rdata);
396 SAFE_FREE(rparam);
397 break;
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);
404 if (!dirlist) {
405 DEBUG(0,("cli_list_new: Failed to expand dirlist\n"));
406 SAFE_FREE(rdata);
407 SAFE_FREE(rparam);
408 break;
411 memcpy(dirlist+dirlist_len,p,data_len);
412 dirlist_len += data_len;
414 total_received += ff_searchcount;
416 SAFE_FREE(rdata);
417 SAFE_FREE(rparam);
419 DEBUG(3,("received %d entries (eos=%d)\n",
420 ff_searchcount,ff_eos));
422 if (ff_searchcount > 0)
423 loop_count = 0;
425 First = False;
428 mnt = cli_cm_get_mntpoint( cli );
430 /* see if the server disconnected or the connection otherwise failed */
431 if (cli_is_error(cli)) {
432 total_received = -1;
433 } else {
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,
438 cli,
439 info_level,
441 rdata_end,
442 &finfo,
443 NULL,
444 NULL);
445 fn(mnt,&finfo, Mask, state);
449 /* free up the dirlist buffer and last name raw blob */
450 SAFE_FREE(dirlist);
451 data_blob_free(&last_name_raw);
452 SAFE_FREE(mask);
453 TALLOC_FREE(frame);
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,
464 char *p,
465 file_info *finfo)
467 ZERO_STRUCTP(finfo);
469 finfo->cli = 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,
479 cli,
480 &finfo->name,
481 p+30,
483 STR_ASCII);
484 if (!finfo->name) {
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)
502 char *p;
503 int received = 0;
504 bool first = True;
505 char status[21];
506 int num_asked = (cli->max_xmit - 100)/DIR_STRUCT_SIZE;
507 int num_received = 0;
508 int i;
509 char *dirlist = NULL;
510 char *mask = NULL;
511 TALLOC_CTX *frame = NULL;
513 ZERO_ARRAY(status);
515 mask = SMB_STRDUP(Mask);
516 if (!mask) {
517 return -1;
520 while (1) {
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);
535 *p++ = 4;
537 p += clistr_push(cli, p, first?mask:"",
538 cli->bufsize - PTR_DIFF(p,cli->outbuf),
539 STR_TERMINATE);
540 *p++ = 5;
541 if (first) {
542 SSVAL(p,0,0);
543 p += 2;
544 } else {
545 SSVAL(p,0,21);
546 p += 2;
547 memcpy(p,status,21);
548 p += 21;
551 cli_setup_bcc(cli, p);
552 cli_send_smb(cli);
553 if (!cli_receive_smb(cli)) break;
555 received = SVAL(cli->inbuf,smb_vwv0);
556 if (received <= 0) break;
558 first = False;
560 dirlist = (char *)SMB_REALLOC(
561 dirlist,(num_received + received)*DIR_STRUCT_SIZE);
562 if (!dirlist) {
563 DEBUG(0,("cli_list_old: failed to expand dirlist"));
564 SAFE_FREE(mask);
565 return 0;
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;
580 if (!first) {
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);
593 *p++ = 4;
594 fstrcpy(p, "");
595 p += strlen(p) + 1;
596 *p++ = 5;
597 SSVAL(p, 0, 21);
598 p += 2;
599 memcpy(p,status,21);
600 p += 21;
602 cli_setup_bcc(cli, p);
603 cli_send_smb(cli);
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++) {
611 file_info finfo;
612 p += interpret_short_filename(frame,cli, p,&finfo);
613 fn("\\", &finfo, Mask, state);
615 TALLOC_FREE(frame);
617 SAFE_FREE(mask);
618 SAFE_FREE(dirlist);
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);