s3: Lift the server_messaging_context from notify_job_status
[Samba/gbeck.git] / source3 / libsmb / clilist.c
blob1a8b79be340b0f5a4054da13916c494b2c7f8195
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 *base_ptr,
50 uint16_t recv_flags2,
51 const char *p,
52 const char *pdata_end,
53 struct file_info *finfo,
54 uint32 *p_resume_key,
55 DATA_BLOB *p_last_name_raw)
57 int len;
58 size_t ret;
59 const char *base = p;
61 data_blob_free(p_last_name_raw);
63 if (p_resume_key) {
64 *p_resume_key = 0;
66 ZERO_STRUCTP(finfo);
68 switch (level) {
69 case SMB_FIND_INFO_STANDARD: /* OS/2 understands this */
70 /* these dates are converted to GMT by
71 make_unix_date */
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);
83 len = CVAL(p, 26);
84 p += 27;
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
104 (tridge) */
105 ret = clistr_pull_talloc(ctx,
106 base_ptr,
107 recv_flags2,
108 &finfo->name,
110 len+2,
111 STR_TERMINATE);
112 if (ret == (size_t)-1) {
113 return pdata_end - base;
115 p += ret;
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
120 make_unix_date */
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);
132 len = CVAL(p, 30);
133 p += 31;
134 /* check for unisys! */
135 if (p + len + 1 > pdata_end) {
136 return pdata_end - base;
138 ret = clistr_pull_talloc(ctx,
139 base_ptr,
140 recv_flags2,
141 &finfo->name,
143 len,
144 STR_NOALIGN);
145 if (ret == (size_t)-1) {
146 return pdata_end - base;
148 p += ret;
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 */
161 if (p_resume_key) {
162 *p_resume_key = IVAL(p,0);
164 p += 4; /* fileindex */
166 /* Offset zero is "create time", not "change time". */
167 p += 8;
168 finfo->atime_ts = interpret_long_date(p);
169 p += 8;
170 finfo->mtime_ts = interpret_long_date(p);
171 p += 8;
172 finfo->ctime_ts = interpret_long_date(p);
173 p += 8;
174 finfo->size = IVAL2_TO_SMB_BIG_UINT(p,0);
175 p += 8;
176 p += 8; /* alloc size */
177 finfo->mode = CVAL(p,0);
178 p += 4;
179 namelen = IVAL(p,0);
180 p += 4;
181 p += 4; /* EA size */
182 slen = SVAL(p, 0);
183 if (slen > 24) {
184 /* Bad short name length. */
185 return pdata_end - base;
187 p += 2;
189 /* stupid NT bugs. grr */
190 int flags = 0;
191 if (p[1] == 0 && namelen > 1) flags |= STR_UNICODE;
192 clistr_pull(base_ptr, finfo->short_name, p,
193 sizeof(finfo->short_name),
194 slen, flags);
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,
201 base_ptr,
202 recv_flags2,
203 &finfo->name,
205 namelen,
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
214 copy it here. */
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)
237 #if 1
238 int max_matches = 1366; /* Match W2k - was 512. */
239 #else
240 int max_matches = 512;
241 #endif
242 int info_level;
243 char *p, *p2, *rdata_end;
244 char *mask = NULL;
245 struct file_info finfo;
246 int i;
247 char *dirlist = NULL;
248 int dirlist_len = 0;
249 int total_received = -1;
250 bool First = True;
251 int ff_searchcount=0;
252 int ff_eos=0;
253 int ff_dir_handle=0;
254 int loop_count = 0;
255 char *rparam=NULL, *rdata=NULL;
256 unsigned int param_len, data_len;
257 uint16 setup;
258 char *param;
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);
269 if (!mask) {
270 TALLOC_FREE(frame);
271 return -1;
274 ZERO_STRUCT(finfo);
276 while (ff_eos == 0) {
277 size_t nlen = 2*(strlen(mask)+1);
279 loop_count++;
280 if (loop_count > 200) {
281 DEBUG(0,("Error: Looping in FIND_NEXT??\n"));
282 break;
285 param = SMB_MALLOC_ARRAY(char, 12+nlen+last_name_raw.length+2);
286 if (!param) {
287 break;
290 if (First) {
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);
296 SIVAL(param,8,0);
297 p = param+12;
298 p += clistr_push(cli, param+12, mask,
299 nlen, STR_TERMINATE);
300 } else {
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 */
311 p = param+12;
312 if (last_name_raw.length) {
313 memcpy(p, last_name_raw.data, last_name_raw.length);
314 p += last_name_raw.length;
315 } else {
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,
324 NULL, /* Name */
325 -1, 0, /* fid, flags */
326 &setup, 1, 0, /* setup, length, max */
327 param, param_len, 10, /* param, length, max */
328 NULL, 0,
329 #if 0
330 /* w2k value. */
331 MIN(16384,cli->max_xmit) /* data, length, max. */
332 #else
333 cli->max_xmit /* data, length, max. */
334 #endif
335 )) {
336 SAFE_FREE(param);
337 TALLOC_FREE(frame);
338 break;
341 SAFE_FREE(param);
343 if (!cli_receive_trans(cli, SMBtrans2,
344 &rparam, &param_len,
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 */
349 uint8 eclass;
350 uint32 ecode;
352 SAFE_FREE(rdata);
353 SAFE_FREE(rparam);
355 cli_dos_error(cli, &eclass, &ecode);
358 * OS/2 might return "no more files",
359 * which just tells us, that searchcount is zero
360 * in this search.
361 * Guenter Kukkukk <linux@kukkukk.com>
364 if (eclass == ERRDOS && ecode == ERRnofiles) {
365 ff_searchcount = 0;
366 cli_reset_error(cli);
367 break;
370 if (eclass != ERRSRV || ecode != ERRerror)
371 break;
372 smb_msleep(100);
373 continue;
376 if (cli_is_error(cli) || !rdata || !rparam) {
377 SAFE_FREE(rdata);
378 SAFE_FREE(rparam);
379 break;
382 if (total_received == -1)
383 total_received = 0;
385 /* parse out some important return info */
386 p = rparam;
387 if (First) {
388 ff_dir_handle = SVAL(p,0);
389 ff_searchcount = SVAL(p,2);
390 ff_eos = SVAL(p,4);
391 } else {
392 ff_searchcount = SVAL(p,0);
393 ff_eos = SVAL(p,2);
396 if (ff_searchcount == 0) {
397 SAFE_FREE(rdata);
398 SAFE_FREE(rparam);
399 break;
402 /* point to the data bytes */
403 p = rdata;
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,
414 cli,
415 info_level,
416 cli->inbuf,
417 SVAL(cli->inbuf, smb_flg2),
419 rdata_end,
420 &finfo,
421 &resume_key,
422 &last_name_raw);
424 if (!finfo.name) {
425 DEBUG(0,("cli_list_new: Error: unable to parse name from info level %d\n",
426 info_level));
427 ff_eos = 1;
428 break;
430 if (!First && *mask && strcsequal(finfo.name, mask)) {
431 DEBUG(0,("Error: Looping in FIND_NEXT as name %s has already been seen?\n",
432 finfo.name));
433 ff_eos = 1;
434 break;
438 SAFE_FREE(mask);
439 if (ff_searchcount > 0 && ff_eos == 0 && finfo.name) {
440 mask = SMB_STRDUP(finfo.name);
441 } else {
442 mask = SMB_STRDUP("");
444 if (!mask) {
445 SAFE_FREE(rdata);
446 SAFE_FREE(rparam);
447 break;
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);
454 if (!dirlist) {
455 DEBUG(0,("cli_list_new: Failed to expand dirlist\n"));
456 SAFE_FREE(rdata);
457 SAFE_FREE(rparam);
458 break;
461 memcpy(dirlist+dirlist_len,p,data_len);
462 dirlist_len += data_len;
464 total_received += ff_searchcount;
466 SAFE_FREE(rdata);
467 SAFE_FREE(rparam);
469 DEBUG(3,("received %d entries (eos=%d)\n",
470 ff_searchcount,ff_eos));
472 if (ff_searchcount > 0)
473 loop_count = 0;
475 First = False;
478 /* see if the server disconnected or the connection otherwise failed */
479 if (cli_is_error(cli)) {
480 total_received = -1;
481 } else {
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,
486 cli,
487 info_level,
488 cli->inbuf,
489 SVAL(cli->inbuf, smb_flg2),
491 rdata_end,
492 &finfo,
493 NULL,
494 NULL);
495 if (!finfo.name) {
496 DEBUG(0,("cli_list_new: unable to parse name from info level %d\n",
497 info_level));
498 break;
500 fn(cli->dfs_mountpoint, &finfo, Mask, state);
504 /* free up the dirlist buffer and last name raw blob */
505 SAFE_FREE(dirlist);
506 data_blob_free(&last_name_raw);
507 SAFE_FREE(mask);
508 TALLOC_FREE(frame);
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,
519 char *p,
520 struct file_info *finfo)
522 size_t ret;
523 ZERO_STRUCTP(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,
534 cli->inbuf,
535 SVAL(cli->inbuf, smb_flg2),
536 &finfo->name,
537 p+30,
539 STR_ASCII);
540 if (ret == (size_t)-1) {
541 return false;
544 if (finfo->name) {
545 strlcpy(finfo->short_name,
546 finfo->name,
547 sizeof(finfo->short_name));
549 return true;
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)
562 char *p;
563 int received = 0;
564 bool first = True;
565 char status[21];
566 int num_asked = (cli->max_xmit - 100)/DIR_STRUCT_SIZE;
567 int num_received = 0;
568 int i;
569 char *dirlist = NULL;
570 char *mask = NULL;
571 TALLOC_CTX *frame = NULL;
573 ZERO_ARRAY(status);
575 mask = SMB_STRDUP(Mask);
576 if (!mask) {
577 return -1;
580 while (1) {
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);
595 *p++ = 4;
597 p += clistr_push(cli, p, first?mask:"",
598 cli->bufsize - PTR_DIFF(p,cli->outbuf),
599 STR_TERMINATE);
600 *p++ = 5;
601 if (first) {
602 SSVAL(p,0,0);
603 p += 2;
604 } else {
605 SSVAL(p,0,21);
606 p += 2;
607 memcpy(p,status,21);
608 p += 21;
611 cli_setup_bcc(cli, p);
612 cli_send_smb(cli);
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) {
621 break;
624 first = False;
626 dirlist = (char *)SMB_REALLOC(
627 dirlist,(num_received + received)*DIR_STRUCT_SIZE);
628 if (!dirlist) {
629 DEBUG(0,("cli_list_old: failed to expand dirlist"));
630 SAFE_FREE(mask);
631 return 0;
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;
646 if (!first) {
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);
659 *p++ = 4;
660 fstrcpy(p, "");
661 p += strlen(p) + 1;
662 *p++ = 5;
663 SSVAL(p, 0, 21);
664 p += 2;
665 memcpy(p,status,21);
666 p += 21;
668 cli_setup_bcc(cli, p);
669 cli_send_smb(cli);
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)) {
679 break;
681 p += DIR_STRUCT_SIZE;
682 fn("\\", &finfo, Mask, state);
684 TALLOC_FREE(frame);
686 SAFE_FREE(mask);
687 SAFE_FREE(dirlist);
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)
700 int rec;
702 if (cli->protocol <= PROTOCOL_LANMAN1) {
703 rec = cli_list_old(cli, Mask, attribute, fn, state);
704 } else {
705 rec = cli_list_new(cli, Mask, attribute, fn, state);
707 if (rec == -1) {
708 return cli_nt_error(cli);
710 return NT_STATUS_OK;