r204: Turns out that the string in the SEARCH unix_info level is that
[ira/wip.git] / source4 / 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 = raw_pull_dos_date(req->transport,
48                                                                   p + 22);
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)) {
52                         break;
53                 }
54                 p += 43;
55         }
56 }
57
58 /****************************************************************************
59  Old style search first.
60 ****************************************************************************/
61 static NTSTATUS smb_raw_search_first_old(struct cli_tree *tree,
62                                          TALLOC_CTX *mem_ctx,
63                                          union smb_search_first *io, void *private,
64                                          BOOL (*callback)(void *private, union smb_search_data *file))
65
66 {
67         struct cli_request *req; 
68         
69         req = cli_request_setup(tree, SMBsearch, 2, 0);
70         if (!req) {
71                 return NT_STATUS_NO_MEMORY;
72         }
73         
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);
78
79         if (!cli_request_send(req) || 
80             !cli_request_receive(req)) {
81                 return cli_request_destroy(req);
82         }
83
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);
87         }
88
89         return cli_request_destroy(req);
90 }
91
92 /****************************************************************************
93  Old style search next.
94 ****************************************************************************/
95 static NTSTATUS smb_raw_search_next_old(struct cli_tree *tree,
96                                         TALLOC_CTX *mem_ctx,
97                                         union smb_search_next *io, void *private,
98                                         BOOL (*callback)(void *private, union smb_search_data *file))
99
100 {
101         struct cli_request *req; 
102         
103         req = cli_request_setup(tree, SMBsearch, 2, 0);
104         if (!req) {
105                 return NT_STATUS_NO_MEMORY;
106         }
107         
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);
112
113         if (!cli_request_send(req) ||
114             !cli_request_receive(req)) {
115                 return cli_request_destroy(req);
116         }
117
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);
121         }
122         
123         return cli_request_destroy(req);
124 }
125
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,
132                                           uint16 info_level,
133                                           DATA_BLOB *out_param_blob,
134                                           DATA_BLOB *out_data_blob)
135 {
136         struct smb_trans2 tp;
137         uint16 setup = TRANSACT2_FINDFIRST;
138         NTSTATUS status;
139         
140         tp.in.max_setup = 0;
141         tp.in.flags = 0; 
142         tp.in.timeout = 0;
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;
148         
149         tp.in.params = data_blob_talloc(mem_ctx, NULL, 12);
150         if (!tp.in.params.data) {
151                 return NT_STATUS_NO_MEMORY;
152         }
153
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);
159
160         cli_blob_append_string(tree->session, mem_ctx, &tp.in.params,
161                                io->t2ffirst.in.pattern, STR_TERMINATE);
162
163         status = smb_raw_trans2(tree, mem_ctx, &tp);
164         if (!NT_STATUS_IS_OK(status)) {
165                 return status;
166         }
167
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;
172
173         return NT_STATUS_OK;
174 }
175
176
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,
182                                          TALLOC_CTX *mem_ctx,
183                                          union smb_search_next *io,
184                                          uint16 info_level,
185                                          DATA_BLOB *out_param_blob,
186                                          DATA_BLOB *out_data_blob)
187 {
188         struct smb_trans2 tp;
189         uint16 setup = TRANSACT2_FINDNEXT;
190         NTSTATUS status;
191         
192         tp.in.max_setup = 0;
193         tp.in.flags = 0; 
194         tp.in.timeout = 0;
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;
200         
201         tp.in.params = data_blob_talloc(mem_ctx, NULL, 12);
202         if (!tp.in.params.data) {
203                 return NT_STATUS_NO_MEMORY;
204         }
205         
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);
211
212         cli_blob_append_string(tree->session, mem_ctx, &tp.in.params,
213                                io->t2fnext.in.last_name,
214                                STR_TERMINATE);
215
216         status = smb_raw_trans2(tree, mem_ctx, &tp);
217         if (!NT_STATUS_IS_OK(status)) {
218                 return status;
219         }
220
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;
225
226         return NT_STATUS_OK;
227 }
228
229
230 /*
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
235 */
236 static int parse_trans2_search(struct cli_tree *tree,
237                                TALLOC_CTX *mem_ctx, 
238                                enum search_level level,
239                                uint16 flags,
240                                DATA_BLOB *blob,
241                                union smb_search_data *data)
242 {
243         uint_t len, ofs;
244
245         switch (level) {
246         case RAW_SEARCH_GENERIC:
247         case RAW_SEARCH_SEARCH:
248                 /* handled elsewhere */
249                 return -1;
250
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);
255                         blob->data += 4;
256                         blob->length -= 4;
257                 }
258                 if (blob->length < 24) return -1;
259                 data->standard.create_time = raw_pull_dos_date2(tree->session->transport,
260                                                                 blob->data + 0);
261                 data->standard.access_time = raw_pull_dos_date2(tree->session->transport,
262                                                                 blob->data + 4);
263                 data->standard.write_time  = raw_pull_dos_date2(tree->session->transport,
264                                                                 blob->data + 8);
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);
271                 return len + 23;
272
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);
277                         blob->data += 4;
278                         blob->length -= 4;
279                 }
280                 if (blob->length < 28) return -1;
281                 data->ea_size.create_time = raw_pull_dos_date2(tree->session->transport,
282                                                                blob->data + 0);
283                 data->ea_size.access_time = raw_pull_dos_date2(tree->session->transport,
284                                                                blob->data + 4);
285                 data->ea_size.write_time  = raw_pull_dos_date2(tree->session->transport,
286                                                                blob->data + 8);
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,
292                                            &data->ea_size.name,
293                                            26, 27, STR_LEN8BIT | STR_TERMINATE | STR_NOALIGN);
294                 return len + 27 + 1;
295
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,
309                                            60, 64, 0);
310                 if (ofs != 0 && ofs < 64+len) {
311                         return -1;
312                 }
313                 return ofs;
314
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,
329                                            60, 68, 0);
330                 if (ofs != 0 && ofs < 68+len) {
331                         return -1;
332                 }
333                 return ofs;
334
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,
341                                            8, 12, 0);
342                 if (ofs != 0 && ofs < 12+len) {
343                         return -1;
344                 }
345                 return ofs;
346
347
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,
365                                            60, 94, 0);
366                 if (ofs != 0 && ofs < 94+len) {
367                         return -1;
368                 }
369                 return ofs;
370
371
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,
387                                            60, 80, 0);
388                 if (ofs != 0 && ofs < 80+len) {
389                         return -1;
390                 }
391                 return ofs;
392
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,
411                                            60, 104, 0);
412                 if (ofs != 0 && ofs < 104+len) {
413                         return -1;
414                 }
415                 return ofs;
416         
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) {
438                         return -1;
439                 }
440                 return ofs;
441
442         }
443         /* invalid level */
444         return -1;
445 }
446
447 /****************************************************************************
448  Trans2 search backend - process output.
449 ****************************************************************************/
450 static NTSTATUS smb_raw_t2search_backend(struct cli_tree *tree,
451                                          TALLOC_CTX *mem_ctx,
452                                          enum search_level level,
453                                          uint16 flags,
454                                          int16 count,
455                                          DATA_BLOB *blob,
456                                          void *private,
457                                          BOOL (*callback)(void *private, union smb_search_data *file))
458
459 {
460         int i;
461         DATA_BLOB blob2;
462
463         blob2.data = blob->data;
464         blob2.length = blob->length;
465
466         for (i=0; i < count; i++) {
467                 union smb_search_data search_data;
468                 uint_t len;
469
470                 len = parse_trans2_search(tree, mem_ctx, level, flags, &blob2, &search_data);
471                 if (len == -1) {
472                         return NT_STATUS_INVALID_PARAMETER;
473                 }
474
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)) {
478                         break;
479                 }
480
481                 if (len == 0) break;
482
483                 blob2.data += len;
484                 blob2.length -= len;
485         }
486
487         return NT_STATUS_OK;
488 }
489
490
491 /* Implements trans2findfirst2 and old search
492  */
493 NTSTATUS smb_raw_search_first(struct cli_tree *tree,
494                               TALLOC_CTX *mem_ctx,
495                               union smb_search_first *io, void *private,
496                               BOOL (*callback)(void *private, union smb_search_data *file))
497 {
498         uint16 info_level = 0;
499         DATA_BLOB p_blob, d_blob;
500         NTSTATUS status;
501                         
502         if (io->generic.level == RAW_SEARCH_SEARCH) {
503                 return smb_raw_search_first_old(tree, mem_ctx, io, private, callback);
504         }
505         if (io->generic.level >= RAW_SEARCH_GENERIC) {
506                 return NT_STATUS_INVALID_LEVEL;
507         }
508         info_level = (uint16)io->generic.level;
509
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)) {
513                 return status;
514         }
515         
516         if (p_blob.length < 10) {
517                 DEBUG(1,("smb_raw_search_first: parms wrong size %d != expected_param_size\n",
518                         p_blob.length));
519                 return NT_STATUS_INVALID_PARAMETER;
520         }
521
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);
526                 
527         status = smb_raw_t2search_backend(tree, mem_ctx,
528                                           io->generic.level, 
529                                           io->t2ffirst.in.flags, io->t2ffirst.out.count,
530                                           &d_blob, private, callback);
531         
532         return status;
533 }
534
535 /* Implements trans2findnext2 and old smbsearch
536  */
537 NTSTATUS smb_raw_search_next(struct cli_tree *tree,
538                              TALLOC_CTX *mem_ctx,
539                              union smb_search_next *io, void *private,
540                              BOOL (*callback)(void *private, union smb_search_data *file))
541 {
542         uint16 info_level = 0;
543         DATA_BLOB p_blob, d_blob;
544         NTSTATUS status;
545
546         if (io->generic.level == RAW_SEARCH_SEARCH) {
547                 return smb_raw_search_next_old(tree, mem_ctx, io, private, callback);
548         }
549         if (io->generic.level >= RAW_SEARCH_GENERIC) {
550                 return NT_STATUS_INVALID_LEVEL;
551         }
552         info_level = (uint16)io->generic.level;
553
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)) {
557                 return status;
558         }
559         
560         if (p_blob.length != 8) {
561                 DEBUG(1,("smb_raw_search_next: parms wrong size %d != expected_param_size\n",
562                         p_blob.length));
563                 return NT_STATUS_INVALID_PARAMETER;
564         }
565
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);
569                 
570         status = smb_raw_t2search_backend(tree, mem_ctx,
571                                           io->generic.level, 
572                                           io->t2fnext.in.flags, io->t2fnext.out.count,
573                                           &d_blob, private, callback);
574         
575         return status;
576 }
577
578 /* 
579    Implements trans2findclose2
580  */
581 NTSTATUS smb_raw_search_close(struct cli_tree *tree,
582                               union smb_search_close *io)
583 {
584         struct cli_request *req;
585         
586         req = cli_request_setup(tree, SMBfindclose, 1, 0);
587         if (!req) {
588                 return NT_STATUS_NO_MEMORY;
589         }
590
591         SSVAL(req->out.vwv, VWV(0), io->findclose.in.handle);
592
593         if (cli_request_send(req)) {
594                 cli_request_receive(req);
595         }
596
597         return cli_request_destroy(req);
598 }