2 Unix SMB/CIFS implementation.
3 client directory search routines
4 Copyright (C) James Myers 2003 <myersjj@samba.org>
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 2 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, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 #include "libcli/raw/libcliraw.h"
24 /****************************************************************************
25 Old style search backend - process output.
26 ****************************************************************************/
27 static void smb_raw_search_backend(struct smbcli_request *req,
31 BOOL (*callback)(void *private, union smb_search_data *file))
34 union smb_search_data search_data;
38 if (req->in.data_size < 3 + count*43) {
39 req->status = NT_STATUS_INVALID_PARAMETER;
45 for (i=0; i < count; i++) {
48 search_data.search.id.reserved = CVAL(p, 0);
49 memcpy(search_data.search.id.name, p+1, 11);
50 search_data.search.id.handle = CVAL(p, 12);
51 search_data.search.id.server_cookie = IVAL(p, 13);
52 search_data.search.id.client_cookie = IVAL(p, 17);
53 search_data.search.attrib = CVAL(p, 21);
54 search_data.search.write_time = raw_pull_dos_date(req->transport,
56 search_data.search.size = IVAL(p, 26);
57 smbcli_req_pull_ascii(req, mem_ctx, &name, p+30, 13, STR_ASCII);
58 search_data.search.name = name;
59 if (!callback(private, &search_data)) {
66 /****************************************************************************
67 Old style search first.
68 ****************************************************************************/
69 static NTSTATUS smb_raw_search_first_old(struct smbcli_tree *tree,
71 union smb_search_first *io, void *private,
72 BOOL (*callback)(void *private, union smb_search_data *file))
75 struct smbcli_request *req;
76 uint8_t op = SMBsearch;
78 if (io->generic.level == RAW_SEARCH_FFIRST) {
80 } else if (io->generic.level == RAW_SEARCH_FUNIQUE) {
84 req = smbcli_request_setup(tree, op, 2, 0);
86 return NT_STATUS_NO_MEMORY;
89 SSVAL(req->out.vwv, VWV(0), io->search_first.in.max_count);
90 SSVAL(req->out.vwv, VWV(1), io->search_first.in.search_attrib);
91 smbcli_req_append_ascii4(req, io->search_first.in.pattern, STR_TERMINATE);
92 smbcli_req_append_var_block(req, NULL, 0);
94 if (!smbcli_request_send(req) ||
95 !smbcli_request_receive(req)) {
96 return smbcli_request_destroy(req);
99 if (NT_STATUS_IS_OK(req->status)) {
100 io->search_first.out.count = SVAL(req->in.vwv, VWV(0));
101 smb_raw_search_backend(req, mem_ctx, io->search_first.out.count, private, callback);
104 return smbcli_request_destroy(req);
107 /****************************************************************************
108 Old style search next.
109 ****************************************************************************/
110 static NTSTATUS smb_raw_search_next_old(struct smbcli_tree *tree,
112 union smb_search_next *io, void *private,
113 BOOL (*callback)(void *private, union smb_search_data *file))
116 struct smbcli_request *req;
117 uint8_t var_block[21];
118 uint8_t op = SMBsearch;
120 if (io->generic.level == RAW_SEARCH_FFIRST) {
124 req = smbcli_request_setup(tree, op, 2, 0);
126 return NT_STATUS_NO_MEMORY;
129 SSVAL(req->out.vwv, VWV(0), io->search_next.in.max_count);
130 SSVAL(req->out.vwv, VWV(1), io->search_next.in.search_attrib);
131 smbcli_req_append_ascii4(req, "", STR_TERMINATE);
133 SCVAL(var_block, 0, io->search_next.in.id.reserved);
134 memcpy(&var_block[1], io->search_next.in.id.name, 11);
135 SCVAL(var_block, 12, io->search_next.in.id.handle);
136 SIVAL(var_block, 13, io->search_next.in.id.server_cookie);
137 SIVAL(var_block, 17, io->search_next.in.id.client_cookie);
139 smbcli_req_append_var_block(req, var_block, 21);
141 if (!smbcli_request_send(req) ||
142 !smbcli_request_receive(req)) {
143 return smbcli_request_destroy(req);
146 if (NT_STATUS_IS_OK(req->status)) {
147 io->search_next.out.count = SVAL(req->in.vwv, VWV(0));
148 smb_raw_search_backend(req, mem_ctx, io->search_next.out.count, private, callback);
151 return smbcli_request_destroy(req);
155 /****************************************************************************
156 Old style search next.
157 ****************************************************************************/
158 static NTSTATUS smb_raw_search_close_old(struct smbcli_tree *tree,
159 union smb_search_close *io)
161 struct smbcli_request *req;
162 uint8_t var_block[21];
164 req = smbcli_request_setup(tree, SMBfclose, 2, 0);
166 return NT_STATUS_NO_MEMORY;
169 SSVAL(req->out.vwv, VWV(0), io->fclose.in.max_count);
170 SSVAL(req->out.vwv, VWV(1), io->fclose.in.search_attrib);
171 smbcli_req_append_ascii4(req, "", STR_TERMINATE);
173 SCVAL(var_block, 0, io->fclose.in.id.reserved);
174 memcpy(&var_block[1], io->fclose.in.id.name, 11);
175 SCVAL(var_block, 12, io->fclose.in.id.handle);
176 SIVAL(var_block, 13, io->fclose.in.id.server_cookie);
177 SIVAL(var_block, 17, io->fclose.in.id.client_cookie);
179 smbcli_req_append_var_block(req, var_block, 21);
181 if (!smbcli_request_send(req) ||
182 !smbcli_request_receive(req)) {
183 return smbcli_request_destroy(req);
186 return smbcli_request_destroy(req);
191 /****************************************************************************
192 Very raw search first - returns param/data blobs.
193 ****************************************************************************/
194 static NTSTATUS smb_raw_search_first_blob(struct smbcli_tree *tree,
195 TALLOC_CTX *mem_ctx, /* used to allocate output blobs */
196 union smb_search_first *io,
198 DATA_BLOB *out_param_blob,
199 DATA_BLOB *out_data_blob)
201 struct smb_trans2 tp;
202 uint16_t setup = TRANSACT2_FINDFIRST;
208 tp.in.setup_count = 1;
209 tp.in.data = data_blob(NULL, 0);
210 tp.in.max_param = 10;
211 tp.in.max_data = smb_raw_max_trans_data(tree, 10);
212 tp.in.setup = &setup;
214 tp.in.params = data_blob_talloc(mem_ctx, NULL, 12);
215 if (!tp.in.params.data) {
216 return NT_STATUS_NO_MEMORY;
219 SSVAL(tp.in.params.data, 0, io->t2ffirst.in.search_attrib);
220 SSVAL(tp.in.params.data, 2, io->t2ffirst.in.max_count);
221 SSVAL(tp.in.params.data, 4, io->t2ffirst.in.flags);
222 SSVAL(tp.in.params.data, 6, info_level);
223 SIVAL(tp.in.params.data, 8, io->t2ffirst.in.storage_type);
225 smbcli_blob_append_string(tree->session, mem_ctx, &tp.in.params,
226 io->t2ffirst.in.pattern, STR_TERMINATE);
228 status = smb_raw_trans2(tree, mem_ctx, &tp);
229 if (!NT_STATUS_IS_OK(status)) {
233 out_param_blob->length = tp.out.params.length;
234 out_param_blob->data = tp.out.params.data;
235 out_data_blob->length = tp.out.data.length;
236 out_data_blob->data = tp.out.data.data;
242 /****************************************************************************
243 Very raw search first - returns param/data blobs.
244 Used in CIFS-on-CIFS NTVFS.
245 ****************************************************************************/
246 static NTSTATUS smb_raw_search_next_blob(struct smbcli_tree *tree,
248 union smb_search_next *io,
250 DATA_BLOB *out_param_blob,
251 DATA_BLOB *out_data_blob)
253 struct smb_trans2 tp;
254 uint16_t setup = TRANSACT2_FINDNEXT;
260 tp.in.setup_count = 1;
261 tp.in.data = data_blob(NULL, 0);
262 tp.in.max_param = 10;
263 tp.in.max_data = smb_raw_max_trans_data(tree, 10);
264 tp.in.setup = &setup;
266 tp.in.params = data_blob_talloc(mem_ctx, NULL, 12);
267 if (!tp.in.params.data) {
268 return NT_STATUS_NO_MEMORY;
271 SSVAL(tp.in.params.data, 0, io->t2fnext.in.handle);
272 SSVAL(tp.in.params.data, 2, io->t2fnext.in.max_count);
273 SSVAL(tp.in.params.data, 4, info_level);
274 SIVAL(tp.in.params.data, 6, io->t2fnext.in.resume_key);
275 SSVAL(tp.in.params.data, 10, io->t2fnext.in.flags);
277 smbcli_blob_append_string(tree->session, mem_ctx, &tp.in.params,
278 io->t2fnext.in.last_name,
281 status = smb_raw_trans2(tree, mem_ctx, &tp);
282 if (!NT_STATUS_IS_OK(status)) {
286 out_param_blob->length = tp.out.params.length;
287 out_param_blob->data = tp.out.params.data;
288 out_data_blob->length = tp.out.data.length;
289 out_data_blob->data = tp.out.data.data;
296 parse a trans2 search response.
297 Return the number of bytes consumed
298 return 0 for success with end of list
299 return -1 for a parse error
301 static int parse_trans2_search(struct smbcli_tree *tree,
303 enum smb_search_level level,
306 union smb_search_data *data)
311 case RAW_SEARCH_GENERIC:
312 case RAW_SEARCH_SEARCH:
313 case RAW_SEARCH_FFIRST:
314 case RAW_SEARCH_FUNIQUE:
315 /* handled elsewhere */
318 case RAW_SEARCH_STANDARD:
319 if (flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) {
320 if (blob->length < 4) return -1;
321 data->standard.resume_key = IVAL(blob->data, 0);
325 if (blob->length < 24) return -1;
326 data->standard.create_time = raw_pull_dos_date2(tree->session->transport,
328 data->standard.access_time = raw_pull_dos_date2(tree->session->transport,
330 data->standard.write_time = raw_pull_dos_date2(tree->session->transport,
332 data->standard.size = IVAL(blob->data, 12);
333 data->standard.alloc_size = IVAL(blob->data, 16);
334 data->standard.attrib = SVAL(blob->data, 20);
335 len = smbcli_blob_pull_string(tree->session, mem_ctx, blob,
336 &data->standard.name,
337 22, 23, STR_LEN8BIT | STR_TERMINATE | STR_LEN_NOTERM);
340 case RAW_SEARCH_EA_SIZE:
341 if (flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) {
342 if (blob->length < 4) return -1;
343 data->ea_size.resume_key = IVAL(blob->data, 0);
347 if (blob->length < 28) return -1;
348 data->ea_size.create_time = raw_pull_dos_date2(tree->session->transport,
350 data->ea_size.access_time = raw_pull_dos_date2(tree->session->transport,
352 data->ea_size.write_time = raw_pull_dos_date2(tree->session->transport,
354 data->ea_size.size = IVAL(blob->data, 12);
355 data->ea_size.alloc_size = IVAL(blob->data, 16);
356 data->ea_size.attrib = SVAL(blob->data, 20);
357 data->ea_size.ea_size = IVAL(blob->data, 22);
358 len = smbcli_blob_pull_string(tree->session, mem_ctx, blob,
360 26, 27, STR_LEN8BIT | STR_TERMINATE | STR_NOALIGN);
363 case RAW_SEARCH_DIRECTORY_INFO:
364 if (blob->length < 65) return -1;
365 ofs = IVAL(blob->data, 0);
366 data->directory_info.file_index = IVAL(blob->data, 4);
367 data->directory_info.create_time = smbcli_pull_nttime(blob->data, 8);
368 data->directory_info.access_time = smbcli_pull_nttime(blob->data, 16);
369 data->directory_info.write_time = smbcli_pull_nttime(blob->data, 24);
370 data->directory_info.change_time = smbcli_pull_nttime(blob->data, 32);
371 data->directory_info.size = BVAL(blob->data, 40);
372 data->directory_info.alloc_size = BVAL(blob->data, 48);
373 data->directory_info.attrib = IVAL(blob->data, 56);
374 len = smbcli_blob_pull_string(tree->session, mem_ctx, blob,
375 &data->directory_info.name,
377 if (ofs != 0 && ofs < 64+len) {
382 case RAW_SEARCH_FULL_DIRECTORY_INFO:
383 if (blob->length < 69) return -1;
384 ofs = IVAL(blob->data, 0);
385 data->full_directory_info.file_index = IVAL(blob->data, 4);
386 data->full_directory_info.create_time = smbcli_pull_nttime(blob->data, 8);
387 data->full_directory_info.access_time = smbcli_pull_nttime(blob->data, 16);
388 data->full_directory_info.write_time = smbcli_pull_nttime(blob->data, 24);
389 data->full_directory_info.change_time = smbcli_pull_nttime(blob->data, 32);
390 data->full_directory_info.size = BVAL(blob->data, 40);
391 data->full_directory_info.alloc_size = BVAL(blob->data, 48);
392 data->full_directory_info.attrib = IVAL(blob->data, 56);
393 data->full_directory_info.ea_size = IVAL(blob->data, 64);
394 len = smbcli_blob_pull_string(tree->session, mem_ctx, blob,
395 &data->full_directory_info.name,
397 if (ofs != 0 && ofs < 68+len) {
402 case RAW_SEARCH_NAME_INFO:
403 if (blob->length < 13) return -1;
404 ofs = IVAL(blob->data, 0);
405 data->name_info.file_index = IVAL(blob->data, 4);
406 len = smbcli_blob_pull_string(tree->session, mem_ctx, blob,
407 &data->name_info.name,
409 if (ofs != 0 && ofs < 12+len) {
415 case RAW_SEARCH_BOTH_DIRECTORY_INFO:
416 if (blob->length < 95) return -1;
417 ofs = IVAL(blob->data, 0);
418 data->both_directory_info.file_index = IVAL(blob->data, 4);
419 data->both_directory_info.create_time = smbcli_pull_nttime(blob->data, 8);
420 data->both_directory_info.access_time = smbcli_pull_nttime(blob->data, 16);
421 data->both_directory_info.write_time = smbcli_pull_nttime(blob->data, 24);
422 data->both_directory_info.change_time = smbcli_pull_nttime(blob->data, 32);
423 data->both_directory_info.size = BVAL(blob->data, 40);
424 data->both_directory_info.alloc_size = BVAL(blob->data, 48);
425 data->both_directory_info.attrib = IVAL(blob->data, 56);
426 data->both_directory_info.ea_size = IVAL(blob->data, 64);
427 smbcli_blob_pull_string(tree->session, mem_ctx, blob,
428 &data->both_directory_info.short_name,
429 68, 70, STR_LEN8BIT | STR_UNICODE);
430 len = smbcli_blob_pull_string(tree->session, mem_ctx, blob,
431 &data->both_directory_info.name,
433 if (ofs != 0 && ofs < 94+len) {
439 case RAW_SEARCH_ID_FULL_DIRECTORY_INFO:
440 if (blob->length < 81) return -1;
441 ofs = IVAL(blob->data, 0);
442 data->id_full_directory_info.file_index = IVAL(blob->data, 4);
443 data->id_full_directory_info.create_time = smbcli_pull_nttime(blob->data, 8);
444 data->id_full_directory_info.access_time = smbcli_pull_nttime(blob->data, 16);
445 data->id_full_directory_info.write_time = smbcli_pull_nttime(blob->data, 24);
446 data->id_full_directory_info.change_time = smbcli_pull_nttime(blob->data, 32);
447 data->id_full_directory_info.size = BVAL(blob->data, 40);
448 data->id_full_directory_info.alloc_size = BVAL(blob->data, 48);
449 data->id_full_directory_info.attrib = IVAL(blob->data, 56);
450 data->id_full_directory_info.ea_size = IVAL(blob->data, 64);
451 data->id_full_directory_info.file_id = BVAL(blob->data, 72);
452 len = smbcli_blob_pull_string(tree->session, mem_ctx, blob,
453 &data->id_full_directory_info.name,
455 if (ofs != 0 && ofs < 80+len) {
460 case RAW_SEARCH_ID_BOTH_DIRECTORY_INFO:
461 if (blob->length < 105) return -1;
462 ofs = IVAL(blob->data, 0);
463 data->id_both_directory_info.file_index = IVAL(blob->data, 4);
464 data->id_both_directory_info.create_time = smbcli_pull_nttime(blob->data, 8);
465 data->id_both_directory_info.access_time = smbcli_pull_nttime(blob->data, 16);
466 data->id_both_directory_info.write_time = smbcli_pull_nttime(blob->data, 24);
467 data->id_both_directory_info.change_time = smbcli_pull_nttime(blob->data, 32);
468 data->id_both_directory_info.size = BVAL(blob->data, 40);
469 data->id_both_directory_info.alloc_size = BVAL(blob->data, 48);
470 data->id_both_directory_info.attrib = SVAL(blob->data, 56);
471 data->id_both_directory_info.ea_size = IVAL(blob->data, 64);
472 smbcli_blob_pull_string(tree->session, mem_ctx, blob,
473 &data->id_both_directory_info.short_name,
474 68, 70, STR_LEN8BIT | STR_UNICODE);
475 data->id_both_directory_info.file_id = BVAL(blob->data, 96);
476 len = smbcli_blob_pull_string(tree->session, mem_ctx, blob,
477 &data->id_both_directory_info.name,
479 if (ofs != 0 && ofs < 104+len) {
484 case RAW_SEARCH_UNIX_INFO:
485 if (blob->length < 109) return -1;
486 ofs = IVAL(blob->data, 0);
487 data->unix_info.file_index = IVAL(blob->data, 4);
488 data->unix_info.size = BVAL(blob->data, 8);
489 data->unix_info.alloc_size = BVAL(blob->data, 16);
490 data->unix_info.status_change_time = smbcli_pull_nttime(blob->data, 24);
491 data->unix_info.access_time = smbcli_pull_nttime(blob->data, 32);
492 data->unix_info.change_time = smbcli_pull_nttime(blob->data, 40);
493 data->unix_info.uid = IVAL(blob->data, 48);
494 data->unix_info.gid = IVAL(blob->data, 56);
495 data->unix_info.file_type = IVAL(blob->data, 64);
496 data->unix_info.dev_major = BVAL(blob->data, 68);
497 data->unix_info.dev_minor = BVAL(blob->data, 76);
498 data->unix_info.unique_id = BVAL(blob->data, 84);
499 data->unix_info.permissions = IVAL(blob->data, 92);
500 data->unix_info.nlink = IVAL(blob->data, 100);
501 /* There is no length field for this name but we know it's null terminated. */
502 len = smbcli_blob_pull_unix_string(tree->session, mem_ctx, blob,
503 &data->unix_info.name, 108, 0);
504 if (ofs != 0 && ofs < 108+len) {
514 /****************************************************************************
515 Trans2 search backend - process output.
516 ****************************************************************************/
517 static NTSTATUS smb_raw_t2search_backend(struct smbcli_tree *tree,
519 enum smb_search_level level,
524 BOOL (*callback)(void *private, union smb_search_data *file))
530 blob2.data = blob->data;
531 blob2.length = blob->length;
533 for (i=0; i < count; i++) {
534 union smb_search_data search_data;
537 len = parse_trans2_search(tree, mem_ctx, level, flags, &blob2, &search_data);
539 return NT_STATUS_INVALID_PARAMETER;
542 /* the callback function can tell us that no more will
543 fit - in that case we stop, but it isn't an error */
544 if (!callback(private, &search_data)) {
558 /* Implements trans2findfirst2 and old search
560 NTSTATUS smb_raw_search_first(struct smbcli_tree *tree,
562 union smb_search_first *io, void *private,
563 BOOL (*callback)(void *private, union smb_search_data *file))
565 uint16_t info_level = 0;
566 DATA_BLOB p_blob, d_blob;
569 if (io->generic.level == RAW_SEARCH_SEARCH ||
570 io->generic.level == RAW_SEARCH_FFIRST ||
571 io->generic.level == RAW_SEARCH_FUNIQUE) {
572 return smb_raw_search_first_old(tree, mem_ctx, io, private, callback);
574 if (io->generic.level >= RAW_SEARCH_GENERIC) {
575 return NT_STATUS_INVALID_LEVEL;
577 info_level = (uint16_t)io->generic.level;
579 status = smb_raw_search_first_blob(tree, mem_ctx,
580 io, info_level, &p_blob, &d_blob);
581 if (!NT_STATUS_IS_OK(status)) {
585 if (p_blob.length < 10) {
586 DEBUG(1,("smb_raw_search_first: parms wrong size %d != expected_param_size\n",
588 return NT_STATUS_INVALID_PARAMETER;
591 /* process output data */
592 io->t2ffirst.out.handle = SVAL(p_blob.data, 0);
593 io->t2ffirst.out.count = SVAL(p_blob.data, 2);
594 io->t2ffirst.out.end_of_search = SVAL(p_blob.data, 4);
596 status = smb_raw_t2search_backend(tree, mem_ctx,
598 io->t2ffirst.in.flags, io->t2ffirst.out.count,
599 &d_blob, private, callback);
604 /* Implements trans2findnext2 and old smbsearch
606 NTSTATUS smb_raw_search_next(struct smbcli_tree *tree,
608 union smb_search_next *io, void *private,
609 BOOL (*callback)(void *private, union smb_search_data *file))
611 uint16_t info_level = 0;
612 DATA_BLOB p_blob, d_blob;
615 if (io->generic.level == RAW_SEARCH_SEARCH ||
616 io->generic.level == RAW_SEARCH_FFIRST) {
617 return smb_raw_search_next_old(tree, mem_ctx, io, private, callback);
619 if (io->generic.level >= RAW_SEARCH_GENERIC) {
620 return NT_STATUS_INVALID_LEVEL;
622 info_level = (uint16_t)io->generic.level;
624 status = smb_raw_search_next_blob(tree, mem_ctx,
625 io, info_level, &p_blob, &d_blob);
626 if (!NT_STATUS_IS_OK(status)) {
630 if (p_blob.length != 8) {
631 DEBUG(1,("smb_raw_search_next: parms wrong size %d != expected_param_size\n",
633 return NT_STATUS_INVALID_PARAMETER;
636 /* process output data */
637 io->t2fnext.out.count = SVAL(p_blob.data, 0);
638 io->t2fnext.out.end_of_search = SVAL(p_blob.data, 2);
640 status = smb_raw_t2search_backend(tree, mem_ctx,
642 io->t2fnext.in.flags, io->t2fnext.out.count,
643 &d_blob, private, callback);
649 Implements trans2findclose2
651 NTSTATUS smb_raw_search_close(struct smbcli_tree *tree,
652 union smb_search_close *io)
654 struct smbcli_request *req;
656 if (io->generic.level == RAW_FINDCLOSE_FCLOSE) {
657 return smb_raw_search_close_old(tree, io);
660 req = smbcli_request_setup(tree, SMBfindclose, 1, 0);
662 return NT_STATUS_NO_MEMORY;
665 SSVAL(req->out.vwv, VWV(0), io->findclose.in.handle);
667 if (smbcli_request_send(req)) {
668 smbcli_request_receive(req);
671 return smbcli_request_destroy(req);