4c7da6ec4d962b912e2ba4a7b0cd578240fddbd8
[kai/samba.git] / source / libcli / raw / rawsearch.c
1 /* 
2    Unix SMB/CIFS implementation.
3    client directory search routines
4    Copyright (C) James Myers 2003 <myersjj@samba.org>
5    
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.
10    
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.
15    
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.
19 */
20
21 #include "includes.h"
22
23 /****************************************************************************
24  Old style search backend - process output.
25 ****************************************************************************/
26 static void smb_raw_search_backend(struct cli_request *req,
27                                    TALLOC_CTX *mem_ctx,
28                                    uint16 count, 
29                                    void *private,
30                                    BOOL (*callback)(void *private, union smb_search_data *file))
31
32 {
33         union smb_search_data search_data;
34         int i;
35         char *p;
36
37         if (req->in.data_size < 3 + count*43) {
38                 req->status = NT_STATUS_INVALID_PARAMETER;
39                 return;
40         }
41         
42         p = req->in.data + 3;
43
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)) {
51                         break;
52                 }
53                 p += 43;
54         }
55 }
56
57 /****************************************************************************
58  Old style search first.
59 ****************************************************************************/
60 static NTSTATUS smb_raw_search_first_old(struct cli_tree *tree,
61                                          TALLOC_CTX *mem_ctx,
62                                          union smb_search_first *io, void *private,
63                                          BOOL (*callback)(void *private, union smb_search_data *file))
64
65 {
66         struct cli_request *req; 
67         
68         req = cli_request_setup(tree, SMBsearch, 2, 0);
69         if (!req) {
70                 return NT_STATUS_NO_MEMORY;
71         }
72         
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);
77
78         if (!cli_request_send(req) || 
79             !cli_request_receive(req)) {
80                 return cli_request_destroy(req);
81         }
82
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);
86         }
87
88         return cli_request_destroy(req);
89 }
90
91 /****************************************************************************
92  Old style search next.
93 ****************************************************************************/
94 static NTSTATUS smb_raw_search_next_old(struct cli_tree *tree,
95                                         TALLOC_CTX *mem_ctx,
96                                         union smb_search_next *io, void *private,
97                                         BOOL (*callback)(void *private, union smb_search_data *file))
98
99 {
100         struct cli_request *req; 
101         
102         req = cli_request_setup(tree, SMBsearch, 2, 0);
103         if (!req) {
104                 return NT_STATUS_NO_MEMORY;
105         }
106         
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);
111
112         if (!cli_request_send(req) ||
113             !cli_request_receive(req)) {
114                 return cli_request_destroy(req);
115         }
116
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);
120         }
121         
122         return cli_request_destroy(req);
123 }
124
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,
131                                           uint16 info_level,
132                                           DATA_BLOB *out_param_blob,
133                                           DATA_BLOB *out_data_blob)
134 {
135         struct smb_trans2 tp;
136         uint16 setup = TRANSACT2_FINDFIRST;
137         NTSTATUS status;
138         
139         tp.in.max_setup = 0;
140         tp.in.flags = 0; 
141         tp.in.timeout = 0;
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;
147         
148         tp.in.params = data_blob_talloc(mem_ctx, NULL, 12);
149         if (!tp.in.params.data) {
150                 return NT_STATUS_NO_MEMORY;
151         }
152
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);
158
159         cli_blob_append_string(tree->session, mem_ctx, &tp.in.params,
160                                io->t2ffirst.in.pattern, STR_TERMINATE);
161
162         status = smb_raw_trans2(tree, mem_ctx, &tp);
163         if (!NT_STATUS_IS_OK(status)) {
164                 return status;
165         }
166
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;
171
172         return NT_STATUS_OK;
173 }
174
175
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,
181                                          TALLOC_CTX *mem_ctx,
182                                          union smb_search_next *io,
183                                          uint16 info_level,
184                                          DATA_BLOB *out_param_blob,
185                                          DATA_BLOB *out_data_blob)
186 {
187         struct smb_trans2 tp;
188         uint16 setup = TRANSACT2_FINDNEXT;
189         NTSTATUS status;
190         
191         tp.in.max_setup = 0;
192         tp.in.flags = 0; 
193         tp.in.timeout = 0;
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;
199         
200         tp.in.params = data_blob_talloc(mem_ctx, NULL, 12);
201         if (!tp.in.params.data) {
202                 return NT_STATUS_NO_MEMORY;
203         }
204         
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);
210
211         cli_blob_append_string(tree->session, mem_ctx, &tp.in.params,
212                                io->t2fnext.in.last_name,
213                                STR_TERMINATE);
214
215         status = smb_raw_trans2(tree, mem_ctx, &tp);
216         if (!NT_STATUS_IS_OK(status)) {
217                 return status;
218         }
219
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;
224
225         return NT_STATUS_OK;
226 }
227
228
229 /*
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
234 */
235 static int parse_trans2_search(struct cli_tree *tree,
236                                TALLOC_CTX *mem_ctx, 
237                                enum search_level level,
238                                uint16 flags,
239                                DATA_BLOB *blob,
240                                union smb_search_data *data)
241 {
242         uint_t len, ofs;
243
244         switch (level) {
245         case RAW_SEARCH_GENERIC:
246         case RAW_SEARCH_SEARCH:
247                 /* handled elsewhere */
248                 return -1;
249
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);
254                         blob->data += 4;
255                         blob->length -= 4;
256                 }
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);
267                 return len + 23;
268
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);
273                         blob->data += 4;
274                         blob->length -= 4;
275                 }
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,
285                                            &data->ea_size.name,
286                                            26, 27, STR_LEN8BIT | STR_TERMINATE | STR_NOALIGN);
287                 return len + 27 + 1;
288
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,
302                                            60, 64, 0);
303                 if (ofs != 0 && ofs < 64+len) {
304                         return -1;
305                 }
306                 return ofs;
307
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,
322                                            60, 68, 0);
323                 if (ofs != 0 && ofs < 68+len) {
324                         return -1;
325                 }
326                 return ofs;
327
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,
334                                            8, 12, 0);
335                 if (ofs != 0 && ofs < 12+len) {
336                         return -1;
337                 }
338                 return ofs;
339
340
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,
358                                            60, 94, 0);
359                 if (ofs != 0 && ofs < 94+len) {
360                         return -1;
361                 }
362                 return ofs;
363
364
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,
380                                            60, 80, 0);
381                 if (ofs != 0 && ofs < 80+len) {
382                         return -1;
383                 }
384                 return ofs;
385
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,
404                                            60, 104, 0);
405                 if (ofs != 0 && ofs < 104+len) {
406                         return -1;
407                 }
408                 return ofs;
409         }
410         
411         /* invalid level */
412         return -1;
413 }
414
415 /****************************************************************************
416  Trans2 search backend - process output.
417 ****************************************************************************/
418 static NTSTATUS smb_raw_t2search_backend(struct cli_tree *tree,
419                                          TALLOC_CTX *mem_ctx,
420                                          enum search_level level,
421                                          uint16 flags,
422                                          int16 count,
423                                          DATA_BLOB *blob,
424                                          void *private,
425                                          BOOL (*callback)(void *private, union smb_search_data *file))
426
427 {
428         int i;
429         DATA_BLOB blob2;
430
431         blob2.data = blob->data;
432         blob2.length = blob->length;
433
434         for (i=0; i < count; i++) {
435                 union smb_search_data search_data;
436                 uint_t len;
437
438                 len = parse_trans2_search(tree, mem_ctx, level, flags, &blob2, &search_data);
439                 if (len == -1) {
440                         return NT_STATUS_INVALID_PARAMETER;
441                 }
442
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)) {
446                         break;
447                 }
448
449                 if (len == 0) break;
450
451                 blob2.data += len;
452                 blob2.length -= len;
453         }
454
455         return NT_STATUS_OK;
456 }
457
458
459 /* Implements trans2findfirst2 and old search
460  */
461 NTSTATUS smb_raw_search_first(struct cli_tree *tree,
462                               TALLOC_CTX *mem_ctx,
463                               union smb_search_first *io, void *private,
464                               BOOL (*callback)(void *private, union smb_search_data *file))
465 {
466         uint16 info_level = 0;
467         DATA_BLOB p_blob, d_blob;
468         NTSTATUS status;
469                         
470         if (io->generic.level == RAW_SEARCH_SEARCH) {
471                 return smb_raw_search_first_old(tree, mem_ctx, io, private, callback);
472         }
473         if (io->generic.level >= RAW_SEARCH_GENERIC) {
474                 return NT_STATUS_INVALID_LEVEL;
475         }
476         info_level = (uint16)io->generic.level;
477
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)) {
481                 return status;
482         }
483         
484         if (p_blob.length < 10) {
485                 DEBUG(1,("smb_raw_search_first: parms wrong size %d != expected_param_size\n",
486                         p_blob.length));
487                 return NT_STATUS_INVALID_PARAMETER;
488         }
489
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);
494                 
495         status = smb_raw_t2search_backend(tree, mem_ctx,
496                                           io->generic.level, 
497                                           io->t2ffirst.in.flags, io->t2ffirst.out.count,
498                                           &d_blob, private, callback);
499         
500         return status;
501 }
502
503 /* Implements trans2findnext2 and old smbsearch
504  */
505 NTSTATUS smb_raw_search_next(struct cli_tree *tree,
506                              TALLOC_CTX *mem_ctx,
507                              union smb_search_next *io, void *private,
508                              BOOL (*callback)(void *private, union smb_search_data *file))
509 {
510         uint16 info_level = 0;
511         DATA_BLOB p_blob, d_blob;
512         NTSTATUS status;
513
514         if (io->generic.level == RAW_SEARCH_SEARCH) {
515                 return smb_raw_search_next_old(tree, mem_ctx, io, private, callback);
516         }
517         if (io->generic.level >= RAW_SEARCH_GENERIC) {
518                 return NT_STATUS_INVALID_LEVEL;
519         }
520         info_level = (uint16)io->generic.level;
521
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)) {
525                 return status;
526         }
527         
528         if (p_blob.length != 8) {
529                 DEBUG(1,("smb_raw_search_next: parms wrong size %d != expected_param_size\n",
530                         p_blob.length));
531                 return NT_STATUS_INVALID_PARAMETER;
532         }
533
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);
537                 
538         status = smb_raw_t2search_backend(tree, mem_ctx,
539                                           io->generic.level, 
540                                           io->t2fnext.in.flags, io->t2fnext.out.count,
541                                           &d_blob, private, callback);
542         
543         return status;
544 }
545
546 /* 
547    Implements trans2findclose2
548  */
549 NTSTATUS smb_raw_search_close(struct cli_tree *tree,
550                               union smb_search_close *io)
551 {
552         struct cli_request *req;
553         
554         req = cli_request_setup(tree, SMBfindclose, 1, 0);
555         if (!req) {
556                 return NT_STATUS_NO_MEMORY;
557         }
558
559         SSVAL(req->out.vwv, VWV(0), io->findclose.in.handle);
560
561         if (cli_request_send(req)) {
562                 cli_request_receive(req);
563         }
564
565         return cli_request_destroy(req);
566 }