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.
23 /****************************************************************************
24 Old style search backend - process output.
25 ****************************************************************************/
26 static void smb_raw_search_backend(struct cli_request *req,
30 BOOL (*callback)(void *private, union smb_search_data *file))
33 union smb_search_data search_data;
37 if (req->in.data_size < 3 + count*43) {
38 req->status = NT_STATUS_INVALID_PARAMETER;
44 for (i=0; i < count; i++) {
45 search_data.search.search_id = cli_req_pull_blob(req, mem_ctx, p, 21);
46 search_data.search.attrib = CVAL(p, 21);
47 search_data.search.write_time = raw_pull_dos_date(req->transport,
49 search_data.search.size = IVAL(p, 26);
50 cli_req_pull_ascii(req, mem_ctx, &search_data.search.name, p+30, 13, STR_ASCII);
51 if (!callback(private, &search_data)) {
58 /****************************************************************************
59 Old style search first.
60 ****************************************************************************/
61 static NTSTATUS smb_raw_search_first_old(struct cli_tree *tree,
63 union smb_search_first *io, void *private,
64 BOOL (*callback)(void *private, union smb_search_data *file))
67 struct cli_request *req;
69 req = cli_request_setup(tree, SMBsearch, 2, 0);
71 return NT_STATUS_NO_MEMORY;
74 SSVAL(req->out.vwv, VWV(0), io->search_first.in.max_count);
75 SSVAL(req->out.vwv, VWV(1), io->search_first.in.search_attrib);
76 cli_req_append_ascii4(req, io->search_first.in.pattern, STR_TERMINATE);
77 cli_req_append_var_block(req, NULL, 0);
79 if (!cli_request_send(req) ||
80 !cli_request_receive(req)) {
81 return cli_request_destroy(req);
84 if (NT_STATUS_IS_OK(req->status)) {
85 io->search_first.out.count = SVAL(req->in.vwv, VWV(0));
86 smb_raw_search_backend(req, mem_ctx, io->search_first.out.count, private, callback);
89 return cli_request_destroy(req);
92 /****************************************************************************
93 Old style search next.
94 ****************************************************************************/
95 static NTSTATUS smb_raw_search_next_old(struct cli_tree *tree,
97 union smb_search_next *io, void *private,
98 BOOL (*callback)(void *private, union smb_search_data *file))
101 struct cli_request *req;
103 req = cli_request_setup(tree, SMBsearch, 2, 0);
105 return NT_STATUS_NO_MEMORY;
108 SSVAL(req->out.vwv, VWV(0), io->search_next.in.max_count);
109 SSVAL(req->out.vwv, VWV(1), io->search_next.in.search_attrib);
110 cli_req_append_ascii4(req, "", STR_TERMINATE);
111 cli_req_append_var_block(req, io->search_next.in.search_id.data, 21);
113 if (!cli_request_send(req) ||
114 !cli_request_receive(req)) {
115 return cli_request_destroy(req);
118 if (NT_STATUS_IS_OK(req->status)) {
119 io->search_next.out.count = SVAL(req->in.vwv, VWV(0));
120 smb_raw_search_backend(req, mem_ctx, io->search_next.out.count, private, callback);
123 return cli_request_destroy(req);
126 /****************************************************************************
127 Very raw search first - returns param/data blobs.
128 ****************************************************************************/
129 static NTSTATUS smb_raw_search_first_blob(struct cli_tree *tree,
130 TALLOC_CTX *mem_ctx, /* used to allocate output blobs */
131 union smb_search_first *io,
133 DATA_BLOB *out_param_blob,
134 DATA_BLOB *out_data_blob)
136 struct smb_trans2 tp;
137 uint16 setup = TRANSACT2_FINDFIRST;
143 tp.in.setup_count = 1;
144 tp.in.data = data_blob(NULL, 0);
145 tp.in.max_param = 1024;
146 tp.in.max_data = 8192;
147 tp.in.setup = &setup;
149 tp.in.params = data_blob_talloc(mem_ctx, NULL, 12);
150 if (!tp.in.params.data) {
151 return NT_STATUS_NO_MEMORY;
154 SSVAL(tp.in.params.data, 0, io->t2ffirst.in.search_attrib);
155 SSVAL(tp.in.params.data, 2, io->t2ffirst.in.max_count);
156 SSVAL(tp.in.params.data, 4, io->t2ffirst.in.flags);
157 SSVAL(tp.in.params.data, 6, info_level);
158 SIVAL(tp.in.params.data, 8, io->t2ffirst.in.storage_type);
160 cli_blob_append_string(tree->session, mem_ctx, &tp.in.params,
161 io->t2ffirst.in.pattern, STR_TERMINATE);
163 status = smb_raw_trans2(tree, mem_ctx, &tp);
164 if (!NT_STATUS_IS_OK(status)) {
168 out_param_blob->length = tp.out.params.length;
169 out_param_blob->data = tp.out.params.data;
170 out_data_blob->length = tp.out.data.length;
171 out_data_blob->data = tp.out.data.data;
177 /****************************************************************************
178 Very raw search first - returns param/data blobs.
179 Used in CIFS-on-CIFS NTVFS.
180 ****************************************************************************/
181 static NTSTATUS smb_raw_search_next_blob(struct cli_tree *tree,
183 union smb_search_next *io,
185 DATA_BLOB *out_param_blob,
186 DATA_BLOB *out_data_blob)
188 struct smb_trans2 tp;
189 uint16 setup = TRANSACT2_FINDNEXT;
195 tp.in.setup_count = 1;
196 tp.in.data = data_blob(NULL, 0);
197 tp.in.max_param = 1024;
198 tp.in.max_data = 8192;
199 tp.in.setup = &setup;
201 tp.in.params = data_blob_talloc(mem_ctx, NULL, 12);
202 if (!tp.in.params.data) {
203 return NT_STATUS_NO_MEMORY;
206 SSVAL(tp.in.params.data, 0, io->t2fnext.in.handle);
207 SSVAL(tp.in.params.data, 2, io->t2fnext.in.max_count);
208 SSVAL(tp.in.params.data, 4, info_level);
209 SIVAL(tp.in.params.data, 6, io->t2fnext.in.resume_key);
210 SSVAL(tp.in.params.data, 10, io->t2fnext.in.flags);
212 cli_blob_append_string(tree->session, mem_ctx, &tp.in.params,
213 io->t2fnext.in.last_name,
216 status = smb_raw_trans2(tree, mem_ctx, &tp);
217 if (!NT_STATUS_IS_OK(status)) {
221 out_param_blob->length = tp.out.params.length;
222 out_param_blob->data = tp.out.params.data;
223 out_data_blob->length = tp.out.data.length;
224 out_data_blob->data = tp.out.data.data;
231 parse a trans2 search response.
232 Return the number of bytes consumed
233 return 0 for success with end of list
234 return -1 for a parse error
236 static int parse_trans2_search(struct cli_tree *tree,
238 enum search_level level,
241 union smb_search_data *data)
246 case RAW_SEARCH_GENERIC:
247 case RAW_SEARCH_SEARCH:
248 /* handled elsewhere */
251 case RAW_SEARCH_STANDARD:
252 if (flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) {
253 if (blob->length < 4) return -1;
254 data->standard.resume_key = IVAL(blob->data, 0);
258 if (blob->length < 24) return -1;
259 data->standard.create_time = raw_pull_dos_date2(tree->session->transport,
261 data->standard.access_time = raw_pull_dos_date2(tree->session->transport,
263 data->standard.write_time = raw_pull_dos_date2(tree->session->transport,
265 data->standard.size = IVAL(blob->data, 12);
266 data->standard.alloc_size = IVAL(blob->data, 16);
267 data->standard.attrib = SVAL(blob->data, 20);
268 len = cli_blob_pull_string(tree->session, mem_ctx, blob,
269 &data->standard.name,
270 22, 23, STR_LEN8BIT | STR_TERMINATE | STR_LEN_NOTERM);
273 case RAW_SEARCH_EA_SIZE:
274 if (flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) {
275 if (blob->length < 4) return -1;
276 data->ea_size.resume_key = IVAL(blob->data, 0);
280 if (blob->length < 28) return -1;
281 data->ea_size.create_time = raw_pull_dos_date2(tree->session->transport,
283 data->ea_size.access_time = raw_pull_dos_date2(tree->session->transport,
285 data->ea_size.write_time = raw_pull_dos_date2(tree->session->transport,
287 data->ea_size.size = IVAL(blob->data, 12);
288 data->ea_size.alloc_size = IVAL(blob->data, 16);
289 data->ea_size.attrib = SVAL(blob->data, 20);
290 data->ea_size.ea_size = IVAL(blob->data, 22);
291 len = cli_blob_pull_string(tree->session, mem_ctx, blob,
293 26, 27, STR_LEN8BIT | STR_TERMINATE | STR_NOALIGN);
296 case RAW_SEARCH_DIRECTORY_INFO:
297 if (blob->length < 65) return -1;
298 ofs = IVAL(blob->data, 0);
299 data->directory_info.file_index = IVAL(blob->data, 4);
300 data->directory_info.create_time = cli_pull_nttime(blob->data, 8);
301 data->directory_info.access_time = cli_pull_nttime(blob->data, 16);
302 data->directory_info.write_time = cli_pull_nttime(blob->data, 24);
303 data->directory_info.change_time = cli_pull_nttime(blob->data, 32);
304 data->directory_info.size = BVAL(blob->data, 40);
305 data->directory_info.alloc_size = BVAL(blob->data, 48);
306 data->directory_info.attrib = IVAL(blob->data, 56);
307 len = cli_blob_pull_string(tree->session, mem_ctx, blob,
308 &data->directory_info.name,
310 if (ofs != 0 && ofs < 64+len) {
315 case RAW_SEARCH_FULL_DIRECTORY_INFO:
316 if (blob->length < 69) return -1;
317 ofs = IVAL(blob->data, 0);
318 data->full_directory_info.file_index = IVAL(blob->data, 4);
319 data->full_directory_info.create_time = cli_pull_nttime(blob->data, 8);
320 data->full_directory_info.access_time = cli_pull_nttime(blob->data, 16);
321 data->full_directory_info.write_time = cli_pull_nttime(blob->data, 24);
322 data->full_directory_info.change_time = cli_pull_nttime(blob->data, 32);
323 data->full_directory_info.size = BVAL(blob->data, 40);
324 data->full_directory_info.alloc_size = BVAL(blob->data, 48);
325 data->full_directory_info.attrib = IVAL(blob->data, 56);
326 data->full_directory_info.ea_size = IVAL(blob->data, 64);
327 len = cli_blob_pull_string(tree->session, mem_ctx, blob,
328 &data->full_directory_info.name,
330 if (ofs != 0 && ofs < 68+len) {
335 case RAW_SEARCH_NAME_INFO:
336 if (blob->length < 13) return -1;
337 ofs = IVAL(blob->data, 0);
338 data->name_info.file_index = IVAL(blob->data, 4);
339 len = cli_blob_pull_string(tree->session, mem_ctx, blob,
340 &data->name_info.name,
342 if (ofs != 0 && ofs < 12+len) {
348 case RAW_SEARCH_BOTH_DIRECTORY_INFO:
349 if (blob->length < 95) return -1;
350 ofs = IVAL(blob->data, 0);
351 data->both_directory_info.file_index = IVAL(blob->data, 4);
352 data->both_directory_info.create_time = cli_pull_nttime(blob->data, 8);
353 data->both_directory_info.access_time = cli_pull_nttime(blob->data, 16);
354 data->both_directory_info.write_time = cli_pull_nttime(blob->data, 24);
355 data->both_directory_info.change_time = cli_pull_nttime(blob->data, 32);
356 data->both_directory_info.size = BVAL(blob->data, 40);
357 data->both_directory_info.alloc_size = BVAL(blob->data, 48);
358 data->both_directory_info.attrib = IVAL(blob->data, 56);
359 data->both_directory_info.ea_size = IVAL(blob->data, 64);
360 cli_blob_pull_string(tree->session, mem_ctx, blob,
361 &data->both_directory_info.short_name,
362 68, 70, STR_LEN8BIT | STR_UNICODE);
363 len = cli_blob_pull_string(tree->session, mem_ctx, blob,
364 &data->both_directory_info.name,
366 if (ofs != 0 && ofs < 94+len) {
372 case RAW_SEARCH_ID_FULL_DIRECTORY_INFO:
373 if (blob->length < 81) return -1;
374 ofs = IVAL(blob->data, 0);
375 data->id_full_directory_info.file_index = IVAL(blob->data, 4);
376 data->id_full_directory_info.create_time = cli_pull_nttime(blob->data, 8);
377 data->id_full_directory_info.access_time = cli_pull_nttime(blob->data, 16);
378 data->id_full_directory_info.write_time = cli_pull_nttime(blob->data, 24);
379 data->id_full_directory_info.change_time = cli_pull_nttime(blob->data, 32);
380 data->id_full_directory_info.size = BVAL(blob->data, 40);
381 data->id_full_directory_info.alloc_size = BVAL(blob->data, 48);
382 data->id_full_directory_info.attrib = IVAL(blob->data, 56);
383 data->id_full_directory_info.ea_size = IVAL(blob->data, 64);
384 data->id_full_directory_info.file_id = BVAL(blob->data, 72);
385 len = cli_blob_pull_string(tree->session, mem_ctx, blob,
386 &data->id_full_directory_info.name,
388 if (ofs != 0 && ofs < 80+len) {
393 case RAW_SEARCH_ID_BOTH_DIRECTORY_INFO:
394 if (blob->length < 105) return -1;
395 ofs = IVAL(blob->data, 0);
396 data->id_both_directory_info.file_index = IVAL(blob->data, 4);
397 data->id_both_directory_info.create_time = cli_pull_nttime(blob->data, 8);
398 data->id_both_directory_info.access_time = cli_pull_nttime(blob->data, 16);
399 data->id_both_directory_info.write_time = cli_pull_nttime(blob->data, 24);
400 data->id_both_directory_info.change_time = cli_pull_nttime(blob->data, 32);
401 data->id_both_directory_info.size = BVAL(blob->data, 40);
402 data->id_both_directory_info.alloc_size = BVAL(blob->data, 48);
403 data->id_both_directory_info.attrib = SVAL(blob->data, 56);
404 data->id_both_directory_info.ea_size = IVAL(blob->data, 64);
405 cli_blob_pull_string(tree->session, mem_ctx, blob,
406 &data->id_both_directory_info.short_name,
407 68, 70, STR_LEN8BIT | STR_UNICODE);
408 data->id_both_directory_info.file_id = BVAL(blob->data, 96);
409 len = cli_blob_pull_string(tree->session, mem_ctx, blob,
410 &data->id_both_directory_info.name,
412 if (ofs != 0 && ofs < 104+len) {
417 case RAW_SEARCH_UNIX_INFO:
418 if (blob->length < 109) return -1;
419 ofs = IVAL(blob->data, 0);
420 data->unix_info.file_index = IVAL(blob->data, 4);
421 data->unix_info.size = BVAL(blob->data, 8);
422 data->unix_info.alloc_size = BVAL(blob->data, 16);
423 data->unix_info.status_change_time = cli_pull_nttime(blob->data, 24);
424 data->unix_info.access_time = cli_pull_nttime(blob->data, 32);
425 data->unix_info.change_time = cli_pull_nttime(blob->data, 40);
426 data->unix_info.uid = IVAL(blob->data, 48);
427 data->unix_info.gid = IVAL(blob->data, 56);
428 data->unix_info.file_type = IVAL(blob->data, 64);
429 data->unix_info.dev_major = BVAL(blob->data, 68);
430 data->unix_info.dev_minor = BVAL(blob->data, 76);
431 data->unix_info.unique_id = BVAL(blob->data, 84);
432 data->unix_info.permissions = IVAL(blob->data, 92);
433 data->unix_info.nlink = IVAL(blob->data, 100);
434 /* There is no length field for this name but we know it's null terminated. */
435 len = cli_blob_pull_unix_string(tree->session, mem_ctx, blob,
436 &data->unix_info.name, 108, 0);
437 if (ofs != 0 && ofs < 108+len) {
447 /****************************************************************************
448 Trans2 search backend - process output.
449 ****************************************************************************/
450 static NTSTATUS smb_raw_t2search_backend(struct cli_tree *tree,
452 enum search_level level,
457 BOOL (*callback)(void *private, union smb_search_data *file))
463 blob2.data = blob->data;
464 blob2.length = blob->length;
466 for (i=0; i < count; i++) {
467 union smb_search_data search_data;
470 len = parse_trans2_search(tree, mem_ctx, level, flags, &blob2, &search_data);
472 return NT_STATUS_INVALID_PARAMETER;
475 /* the callback function can tell us that no more will
476 fit - in that case we stop, but it isn't an error */
477 if (!callback(private, &search_data)) {
491 /* Implements trans2findfirst2 and old search
493 NTSTATUS smb_raw_search_first(struct cli_tree *tree,
495 union smb_search_first *io, void *private,
496 BOOL (*callback)(void *private, union smb_search_data *file))
498 uint16 info_level = 0;
499 DATA_BLOB p_blob, d_blob;
502 if (io->generic.level == RAW_SEARCH_SEARCH) {
503 return smb_raw_search_first_old(tree, mem_ctx, io, private, callback);
505 if (io->generic.level >= RAW_SEARCH_GENERIC) {
506 return NT_STATUS_INVALID_LEVEL;
508 info_level = (uint16)io->generic.level;
510 status = smb_raw_search_first_blob(tree, mem_ctx,
511 io, info_level, &p_blob, &d_blob);
512 if (!NT_STATUS_IS_OK(status)) {
516 if (p_blob.length < 10) {
517 DEBUG(1,("smb_raw_search_first: parms wrong size %d != expected_param_size\n",
519 return NT_STATUS_INVALID_PARAMETER;
522 /* process output data */
523 io->t2ffirst.out.handle = SVAL(p_blob.data, 0);
524 io->t2ffirst.out.count = SVAL(p_blob.data, 2);
525 io->t2ffirst.out.end_of_search = SVAL(p_blob.data, 4);
527 status = smb_raw_t2search_backend(tree, mem_ctx,
529 io->t2ffirst.in.flags, io->t2ffirst.out.count,
530 &d_blob, private, callback);
535 /* Implements trans2findnext2 and old smbsearch
537 NTSTATUS smb_raw_search_next(struct cli_tree *tree,
539 union smb_search_next *io, void *private,
540 BOOL (*callback)(void *private, union smb_search_data *file))
542 uint16 info_level = 0;
543 DATA_BLOB p_blob, d_blob;
546 if (io->generic.level == RAW_SEARCH_SEARCH) {
547 return smb_raw_search_next_old(tree, mem_ctx, io, private, callback);
549 if (io->generic.level >= RAW_SEARCH_GENERIC) {
550 return NT_STATUS_INVALID_LEVEL;
552 info_level = (uint16)io->generic.level;
554 status = smb_raw_search_next_blob(tree, mem_ctx,
555 io, info_level, &p_blob, &d_blob);
556 if (!NT_STATUS_IS_OK(status)) {
560 if (p_blob.length != 8) {
561 DEBUG(1,("smb_raw_search_next: parms wrong size %d != expected_param_size\n",
563 return NT_STATUS_INVALID_PARAMETER;
566 /* process output data */
567 io->t2fnext.out.count = SVAL(p_blob.data, 0);
568 io->t2fnext.out.end_of_search = SVAL(p_blob.data, 2);
570 status = smb_raw_t2search_backend(tree, mem_ctx,
572 io->t2fnext.in.flags, io->t2fnext.out.count,
573 &d_blob, private, callback);
579 Implements trans2findclose2
581 NTSTATUS smb_raw_search_close(struct cli_tree *tree,
582 union smb_search_close *io)
584 struct cli_request *req;
586 req = cli_request_setup(tree, SMBfindclose, 1, 0);
588 return NT_STATUS_NO_MEMORY;
591 SSVAL(req->out.vwv, VWV(0), io->findclose.in.handle);
593 if (cli_request_send(req)) {
594 cli_request_receive(req);
597 return cli_request_destroy(req);