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 = make_unix_date(p + 22);
48 search_data.search.size = IVAL(p, 26);
49 cli_req_pull_ascii(req, mem_ctx, &search_data.search.name, p+30, 13, STR_ASCII);
50 if (!callback(private, &search_data)) {
57 /****************************************************************************
58 Old style search first.
59 ****************************************************************************/
60 static NTSTATUS smb_raw_search_first_old(struct cli_tree *tree,
62 union smb_search_first *io, void *private,
63 BOOL (*callback)(void *private, union smb_search_data *file))
66 struct cli_request *req;
68 req = cli_request_setup(tree, SMBsearch, 2, 0);
70 return NT_STATUS_NO_MEMORY;
73 SSVAL(req->out.vwv, VWV(0), io->search_first.in.max_count);
74 SSVAL(req->out.vwv, VWV(1), io->search_first.in.search_attrib);
75 cli_req_append_ascii4(req, io->search_first.in.pattern, STR_TERMINATE);
76 cli_req_append_var_block(req, NULL, 0);
78 if (!cli_request_send(req) ||
79 !cli_request_receive(req)) {
80 return cli_request_destroy(req);
83 if (NT_STATUS_IS_OK(req->status)) {
84 io->search_first.out.count = SVAL(req->in.vwv, VWV(0));
85 smb_raw_search_backend(req, mem_ctx, io->search_first.out.count, private, callback);
88 return cli_request_destroy(req);
91 /****************************************************************************
92 Old style search next.
93 ****************************************************************************/
94 static NTSTATUS smb_raw_search_next_old(struct cli_tree *tree,
96 union smb_search_next *io, void *private,
97 BOOL (*callback)(void *private, union smb_search_data *file))
100 struct cli_request *req;
102 req = cli_request_setup(tree, SMBsearch, 2, 0);
104 return NT_STATUS_NO_MEMORY;
107 SSVAL(req->out.vwv, VWV(0), io->search_next.in.max_count);
108 SSVAL(req->out.vwv, VWV(1), io->search_next.in.search_attrib);
109 cli_req_append_ascii4(req, "", STR_TERMINATE);
110 cli_req_append_var_block(req, io->search_next.in.search_id.data, 21);
112 if (!cli_request_send(req) ||
113 !cli_request_receive(req)) {
114 return cli_request_destroy(req);
117 if (NT_STATUS_IS_OK(req->status)) {
118 io->search_next.out.count = SVAL(req->in.vwv, VWV(0));
119 smb_raw_search_backend(req, mem_ctx, io->search_next.out.count, private, callback);
122 return cli_request_destroy(req);
125 /****************************************************************************
126 Very raw search first - returns param/data blobs.
127 ****************************************************************************/
128 static NTSTATUS smb_raw_search_first_blob(struct cli_tree *tree,
129 TALLOC_CTX *mem_ctx, /* used to allocate output blobs */
130 union smb_search_first *io,
132 DATA_BLOB *out_param_blob,
133 DATA_BLOB *out_data_blob)
135 struct smb_trans2 tp;
136 uint16 setup = TRANSACT2_FINDFIRST;
142 tp.in.setup_count = 1;
143 tp.in.data = data_blob(NULL, 0);
144 tp.in.max_param = 1024;
145 tp.in.max_data = 8192;
146 tp.in.setup = &setup;
148 tp.in.params = data_blob_talloc(mem_ctx, NULL, 12);
149 if (!tp.in.params.data) {
150 return NT_STATUS_NO_MEMORY;
153 SSVAL(tp.in.params.data, 0, io->t2ffirst.in.search_attrib);
154 SSVAL(tp.in.params.data, 2, io->t2ffirst.in.max_count);
155 SSVAL(tp.in.params.data, 4, io->t2ffirst.in.flags);
156 SSVAL(tp.in.params.data, 6, info_level);
157 SIVAL(tp.in.params.data, 8, io->t2ffirst.in.storage_type);
159 cli_blob_append_string(tree->session, mem_ctx, &tp.in.params,
160 io->t2ffirst.in.pattern, STR_TERMINATE);
162 status = smb_raw_trans2(tree, mem_ctx, &tp);
163 if (!NT_STATUS_IS_OK(status)) {
167 out_param_blob->length = tp.out.params.length;
168 out_param_blob->data = tp.out.params.data;
169 out_data_blob->length = tp.out.data.length;
170 out_data_blob->data = tp.out.data.data;
176 /****************************************************************************
177 Very raw search first - returns param/data blobs.
178 Used in CIFS-on-CIFS NTVFS.
179 ****************************************************************************/
180 static NTSTATUS smb_raw_search_next_blob(struct cli_tree *tree,
182 union smb_search_next *io,
184 DATA_BLOB *out_param_blob,
185 DATA_BLOB *out_data_blob)
187 struct smb_trans2 tp;
188 uint16 setup = TRANSACT2_FINDNEXT;
194 tp.in.setup_count = 1;
195 tp.in.data = data_blob(NULL, 0);
196 tp.in.max_param = 1024;
197 tp.in.max_data = 8192;
198 tp.in.setup = &setup;
200 tp.in.params = data_blob_talloc(mem_ctx, NULL, 12);
201 if (!tp.in.params.data) {
202 return NT_STATUS_NO_MEMORY;
205 SSVAL(tp.in.params.data, 0, io->t2fnext.in.handle);
206 SSVAL(tp.in.params.data, 2, io->t2fnext.in.max_count);
207 SSVAL(tp.in.params.data, 4, info_level);
208 SIVAL(tp.in.params.data, 6, io->t2fnext.in.resume_key);
209 SSVAL(tp.in.params.data, 10, io->t2fnext.in.flags);
211 cli_blob_append_string(tree->session, mem_ctx, &tp.in.params,
212 io->t2fnext.in.last_name,
215 status = smb_raw_trans2(tree, mem_ctx, &tp);
216 if (!NT_STATUS_IS_OK(status)) {
220 out_param_blob->length = tp.out.params.length;
221 out_param_blob->data = tp.out.params.data;
222 out_data_blob->length = tp.out.data.length;
223 out_data_blob->data = tp.out.data.data;
230 parse a trans2 search response.
231 Return the number of bytes consumed
232 return 0 for success with end of list
233 return -1 for a parse error
235 static int parse_trans2_search(struct cli_tree *tree,
237 enum search_level level,
240 union smb_search_data *data)
245 case RAW_SEARCH_GENERIC:
246 case RAW_SEARCH_SEARCH:
247 /* handled elsewhere */
250 case RAW_SEARCH_STANDARD:
251 if (flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) {
252 if (blob->length < 4) return -1;
253 data->standard.resume_key = IVAL(blob->data, 0);
257 if (blob->length < 24) return -1;
258 data->standard.create_time = make_unix_date2(blob->data + 0);
259 data->standard.access_time = make_unix_date2(blob->data + 4);
260 data->standard.write_time = make_unix_date2(blob->data + 8);
261 data->standard.size = IVAL(blob->data, 12);
262 data->standard.alloc_size = IVAL(blob->data, 16);
263 data->standard.attrib = SVAL(blob->data, 20);
264 len = cli_blob_pull_string(tree->session, mem_ctx, blob,
265 &data->standard.name,
266 22, 23, STR_LEN8BIT | STR_TERMINATE | STR_LEN_NOTERM);
269 case RAW_SEARCH_EA_SIZE:
270 if (flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) {
271 if (blob->length < 4) return -1;
272 data->ea_size.resume_key = IVAL(blob->data, 0);
276 if (blob->length < 28) return -1;
277 data->ea_size.create_time = make_unix_date2(blob->data + 0);
278 data->ea_size.access_time = make_unix_date2(blob->data + 4);
279 data->ea_size.write_time = make_unix_date2(blob->data + 8);
280 data->ea_size.size = IVAL(blob->data, 12);
281 data->ea_size.alloc_size = IVAL(blob->data, 16);
282 data->ea_size.attrib = SVAL(blob->data, 20);
283 data->ea_size.ea_size = IVAL(blob->data, 22);
284 len = cli_blob_pull_string(tree->session, mem_ctx, blob,
286 26, 27, STR_LEN8BIT | STR_TERMINATE | STR_NOALIGN);
289 case RAW_SEARCH_DIRECTORY_INFO:
290 if (blob->length < 65) return -1;
291 ofs = IVAL(blob->data, 0);
292 data->directory_info.file_index = IVAL(blob->data, 4);
293 data->directory_info.create_time = cli_pull_nttime(blob->data, 8);
294 data->directory_info.access_time = cli_pull_nttime(blob->data, 16);
295 data->directory_info.write_time = cli_pull_nttime(blob->data, 24);
296 data->directory_info.change_time = cli_pull_nttime(blob->data, 32);
297 data->directory_info.size = BVAL(blob->data, 40);
298 data->directory_info.alloc_size = BVAL(blob->data, 48);
299 data->directory_info.attrib = IVAL(blob->data, 56);
300 len = cli_blob_pull_string(tree->session, mem_ctx, blob,
301 &data->directory_info.name,
303 if (ofs != 0 && ofs < 64+len) {
308 case RAW_SEARCH_FULL_DIRECTORY_INFO:
309 if (blob->length < 69) return -1;
310 ofs = IVAL(blob->data, 0);
311 data->full_directory_info.file_index = IVAL(blob->data, 4);
312 data->full_directory_info.create_time = cli_pull_nttime(blob->data, 8);
313 data->full_directory_info.access_time = cli_pull_nttime(blob->data, 16);
314 data->full_directory_info.write_time = cli_pull_nttime(blob->data, 24);
315 data->full_directory_info.change_time = cli_pull_nttime(blob->data, 32);
316 data->full_directory_info.size = BVAL(blob->data, 40);
317 data->full_directory_info.alloc_size = BVAL(blob->data, 48);
318 data->full_directory_info.attrib = IVAL(blob->data, 56);
319 data->full_directory_info.ea_size = IVAL(blob->data, 64);
320 len = cli_blob_pull_string(tree->session, mem_ctx, blob,
321 &data->full_directory_info.name,
323 if (ofs != 0 && ofs < 68+len) {
328 case RAW_SEARCH_NAME_INFO:
329 if (blob->length < 13) return -1;
330 ofs = IVAL(blob->data, 0);
331 data->name_info.file_index = IVAL(blob->data, 4);
332 len = cli_blob_pull_string(tree->session, mem_ctx, blob,
333 &data->name_info.name,
335 if (ofs != 0 && ofs < 12+len) {
341 case RAW_SEARCH_BOTH_DIRECTORY_INFO:
342 if (blob->length < 95) return -1;
343 ofs = IVAL(blob->data, 0);
344 data->both_directory_info.file_index = IVAL(blob->data, 4);
345 data->both_directory_info.create_time = cli_pull_nttime(blob->data, 8);
346 data->both_directory_info.access_time = cli_pull_nttime(blob->data, 16);
347 data->both_directory_info.write_time = cli_pull_nttime(blob->data, 24);
348 data->both_directory_info.change_time = cli_pull_nttime(blob->data, 32);
349 data->both_directory_info.size = BVAL(blob->data, 40);
350 data->both_directory_info.alloc_size = BVAL(blob->data, 48);
351 data->both_directory_info.attrib = IVAL(blob->data, 56);
352 data->both_directory_info.ea_size = IVAL(blob->data, 64);
353 cli_blob_pull_string(tree->session, mem_ctx, blob,
354 &data->both_directory_info.short_name,
355 68, 70, STR_LEN8BIT | STR_UNICODE);
356 len = cli_blob_pull_string(tree->session, mem_ctx, blob,
357 &data->both_directory_info.name,
359 if (ofs != 0 && ofs < 94+len) {
365 case RAW_SEARCH_ID_FULL_DIRECTORY_INFO:
366 if (blob->length < 81) return -1;
367 ofs = IVAL(blob->data, 0);
368 data->id_full_directory_info.file_index = IVAL(blob->data, 4);
369 data->id_full_directory_info.create_time = cli_pull_nttime(blob->data, 8);
370 data->id_full_directory_info.access_time = cli_pull_nttime(blob->data, 16);
371 data->id_full_directory_info.write_time = cli_pull_nttime(blob->data, 24);
372 data->id_full_directory_info.change_time = cli_pull_nttime(blob->data, 32);
373 data->id_full_directory_info.size = BVAL(blob->data, 40);
374 data->id_full_directory_info.alloc_size = BVAL(blob->data, 48);
375 data->id_full_directory_info.attrib = IVAL(blob->data, 56);
376 data->id_full_directory_info.ea_size = IVAL(blob->data, 64);
377 data->id_full_directory_info.file_id = BVAL(blob->data, 72);
378 len = cli_blob_pull_string(tree->session, mem_ctx, blob,
379 &data->id_full_directory_info.name,
381 if (ofs != 0 && ofs < 80+len) {
386 case RAW_SEARCH_ID_BOTH_DIRECTORY_INFO:
387 if (blob->length < 105) return -1;
388 ofs = IVAL(blob->data, 0);
389 data->id_both_directory_info.file_index = IVAL(blob->data, 4);
390 data->id_both_directory_info.create_time = cli_pull_nttime(blob->data, 8);
391 data->id_both_directory_info.access_time = cli_pull_nttime(blob->data, 16);
392 data->id_both_directory_info.write_time = cli_pull_nttime(blob->data, 24);
393 data->id_both_directory_info.change_time = cli_pull_nttime(blob->data, 32);
394 data->id_both_directory_info.size = BVAL(blob->data, 40);
395 data->id_both_directory_info.alloc_size = BVAL(blob->data, 48);
396 data->id_both_directory_info.attrib = SVAL(blob->data, 56);
397 data->id_both_directory_info.ea_size = IVAL(blob->data, 64);
398 cli_blob_pull_string(tree->session, mem_ctx, blob,
399 &data->id_both_directory_info.short_name,
400 68, 70, STR_LEN8BIT | STR_UNICODE);
401 data->id_both_directory_info.file_id = BVAL(blob->data, 96);
402 len = cli_blob_pull_string(tree->session, mem_ctx, blob,
403 &data->id_both_directory_info.name,
405 if (ofs != 0 && ofs < 104+len) {
415 /****************************************************************************
416 Trans2 search backend - process output.
417 ****************************************************************************/
418 static NTSTATUS smb_raw_t2search_backend(struct cli_tree *tree,
420 enum search_level level,
425 BOOL (*callback)(void *private, union smb_search_data *file))
431 blob2.data = blob->data;
432 blob2.length = blob->length;
434 for (i=0; i < count; i++) {
435 union smb_search_data search_data;
438 len = parse_trans2_search(tree, mem_ctx, level, flags, &blob2, &search_data);
440 return NT_STATUS_INVALID_PARAMETER;
443 /* the callback function can tell us that no more will
444 fit - in that case we stop, but it isn't an error */
445 if (!callback(private, &search_data)) {
459 /* Implements trans2findfirst2 and old search
461 NTSTATUS smb_raw_search_first(struct cli_tree *tree,
463 union smb_search_first *io, void *private,
464 BOOL (*callback)(void *private, union smb_search_data *file))
466 uint16 info_level = 0;
467 DATA_BLOB p_blob, d_blob;
470 if (io->generic.level == RAW_SEARCH_SEARCH) {
471 return smb_raw_search_first_old(tree, mem_ctx, io, private, callback);
473 if (io->generic.level >= RAW_SEARCH_GENERIC) {
474 return NT_STATUS_INVALID_LEVEL;
476 info_level = (uint16)io->generic.level;
478 status = smb_raw_search_first_blob(tree, mem_ctx,
479 io, info_level, &p_blob, &d_blob);
480 if (!NT_STATUS_IS_OK(status)) {
484 if (p_blob.length < 10) {
485 DEBUG(1,("smb_raw_search_first: parms wrong size %d != expected_param_size\n",
487 return NT_STATUS_INVALID_PARAMETER;
490 /* process output data */
491 io->t2ffirst.out.handle = SVAL(p_blob.data, 0);
492 io->t2ffirst.out.count = SVAL(p_blob.data, 2);
493 io->t2ffirst.out.end_of_search = SVAL(p_blob.data, 4);
495 status = smb_raw_t2search_backend(tree, mem_ctx,
497 io->t2ffirst.in.flags, io->t2ffirst.out.count,
498 &d_blob, private, callback);
503 /* Implements trans2findnext2 and old smbsearch
505 NTSTATUS smb_raw_search_next(struct cli_tree *tree,
507 union smb_search_next *io, void *private,
508 BOOL (*callback)(void *private, union smb_search_data *file))
510 uint16 info_level = 0;
511 DATA_BLOB p_blob, d_blob;
514 if (io->generic.level == RAW_SEARCH_SEARCH) {
515 return smb_raw_search_next_old(tree, mem_ctx, io, private, callback);
517 if (io->generic.level >= RAW_SEARCH_GENERIC) {
518 return NT_STATUS_INVALID_LEVEL;
520 info_level = (uint16)io->generic.level;
522 status = smb_raw_search_next_blob(tree, mem_ctx,
523 io, info_level, &p_blob, &d_blob);
524 if (!NT_STATUS_IS_OK(status)) {
528 if (p_blob.length != 8) {
529 DEBUG(1,("smb_raw_search_next: parms wrong size %d != expected_param_size\n",
531 return NT_STATUS_INVALID_PARAMETER;
534 /* process output data */
535 io->t2fnext.out.count = SVAL(p_blob.data, 0);
536 io->t2fnext.out.end_of_search = SVAL(p_blob.data, 2);
538 status = smb_raw_t2search_backend(tree, mem_ctx,
540 io->t2fnext.in.flags, io->t2fnext.out.count,
541 &d_blob, private, callback);
547 Implements trans2findclose2
549 NTSTATUS smb_raw_search_close(struct cli_tree *tree,
550 union smb_search_close *io)
552 struct cli_request *req;
554 req = cli_request_setup(tree, SMBfindclose, 1, 0);
556 return NT_STATUS_NO_MEMORY;
559 SSVAL(req->out.vwv, VWV(0), io->findclose.in.handle);
561 if (cli_request_send(req)) {
562 cli_request_receive(req);
565 return cli_request_destroy(req);