r2462: added a test for the error code for no matching filename
[kamenim/samba.git] / source4 / torture / raw / search.c
1 /* 
2    Unix SMB/CIFS implementation.
3    RAW_SEARCH_* individual test suite
4    Copyright (C) Andrew Tridgell 2003
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 #define BASEDIR "\\testsearch"
25
26 /*
27   callback function for single_search
28 */
29 static BOOL single_search_callback(void *private, union smb_search_data *file)
30 {
31         union smb_search_data *data = private;
32
33         *data = *file;
34
35         return True;
36 }
37
38 /*
39   do a single file (non-wildcard) search 
40 */
41 static NTSTATUS single_search(struct smbcli_state *cli, 
42                               TALLOC_CTX *mem_ctx,
43                               const char *pattern,
44                               enum smb_search_level level,
45                               union smb_search_data *data)
46 {
47         union smb_search_first io;
48         NTSTATUS status;
49
50         io.generic.level = level;
51         if (level == RAW_SEARCH_SEARCH) {
52                 io.search_first.in.max_count = 1;
53                 io.search_first.in.search_attrib = 0;
54                 io.search_first.in.pattern = pattern;
55         } else {
56                 io.t2ffirst.in.search_attrib = 0;
57                 io.t2ffirst.in.max_count = 1;
58                 io.t2ffirst.in.flags = FLAG_TRANS2_FIND_CLOSE;
59                 io.t2ffirst.in.storage_type = 0;
60                 io.t2ffirst.in.pattern = pattern;
61         }
62
63         status = smb_raw_search_first(cli->tree, mem_ctx,
64                                       &io, (void *)data, single_search_callback);
65         
66         return status;
67 }
68
69
70 static struct {
71         const char *name;
72         enum smb_search_level level;
73         uint32_t capability_mask;
74         NTSTATUS status;
75         union smb_search_data data;
76 } levels[] = {
77         {"SEARCH",                 RAW_SEARCH_SEARCH, },
78         {"STANDARD",               RAW_SEARCH_STANDARD, },
79         {"EA_SIZE",                RAW_SEARCH_EA_SIZE, },
80         {"DIRECTORY_INFO",         RAW_SEARCH_DIRECTORY_INFO, },
81         {"FULL_DIRECTORY_INFO",    RAW_SEARCH_FULL_DIRECTORY_INFO, },
82         {"NAME_INFO",              RAW_SEARCH_NAME_INFO, },
83         {"BOTH_DIRECTORY_INFO",    RAW_SEARCH_BOTH_DIRECTORY_INFO, },
84         {"ID_FULL_DIRECTORY_INFO", RAW_SEARCH_ID_FULL_DIRECTORY_INFO, },
85         {"ID_BOTH_DIRECTORY_INFO", RAW_SEARCH_ID_BOTH_DIRECTORY_INFO, },
86         {"UNIX_INFO",              RAW_SEARCH_UNIX_INFO, CAP_UNIX}
87 };
88
89 /* find a level in the table by name */
90 static union smb_search_data *find(const char *name)
91 {
92         int i;
93         for (i=0;i<ARRAY_SIZE(levels);i++) {
94                 if (NT_STATUS_IS_OK(levels[i].status) && 
95                     strcmp(levels[i].name, name) == 0) {
96                         return &levels[i].data;
97                 }
98         }
99         return NULL;
100 }
101
102 /* 
103    basic testing of all RAW_SEARCH_* calls using a single file
104 */
105 static BOOL test_one_file(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
106 {
107         BOOL ret = True;
108         int fnum;
109         const char *fname = "\\torture_search.txt";
110         const char *fname2 = "\\torture_search-NOTEXIST.txt";
111         NTSTATUS status;
112         int i;
113         union smb_fileinfo all_info, alt_info, name_info, internal_info;
114         union smb_search_data *s;
115
116         printf("Testing one file searches\n");
117
118         fnum = create_complex_file(cli, mem_ctx, fname);
119         if (fnum == -1) {
120                 printf("ERROR: open of %s failed (%s)\n", fname, smbcli_errstr(cli->tree));
121                 ret = False;
122                 goto done;
123         }
124
125         /* call all the levels */
126         for (i=0;i<ARRAY_SIZE(levels);i++) {
127                 NTSTATUS expected_status;
128                 uint32_t cap = cli->transport->negotiate.capabilities;
129
130                 printf("testing %s\n", levels[i].name);
131
132                 levels[i].status = single_search(cli, mem_ctx, fname, 
133                                                  levels[i].level, &levels[i].data);
134
135                 /* see if this server claims to support this level */
136                 if ((cap & levels[i].capability_mask) != levels[i].capability_mask) {
137                         printf("search level %s(%d) not supported by server\n",
138                                levels[i].name, (int)levels[i].level);
139                         continue;
140                 }
141
142                 if (!NT_STATUS_IS_OK(levels[i].status)) {
143                         printf("search level %s(%d) failed - %s\n",
144                                levels[i].name, (int)levels[i].level, 
145                                nt_errstr(levels[i].status));
146                         ret = False;
147                         continue;
148                 }
149
150                 status = single_search(cli, mem_ctx, fname2, 
151                                        levels[i].level, &levels[i].data);
152                 
153                 expected_status = NT_STATUS_NO_SUCH_FILE;
154                 if (levels[i].level == RAW_SEARCH_SEARCH) {
155                         expected_status = STATUS_NO_MORE_FILES;
156                 }
157                 if (!NT_STATUS_EQUAL(status, expected_status)) {
158                         printf("search level %s(%d) should fail with %s - %s\n",
159                                levels[i].name, (int)levels[i].level, 
160                                nt_errstr(expected_status),
161                                nt_errstr(status));
162                         ret = False;
163                 }
164         }
165
166         /* get the all_info file into to check against */
167         all_info.generic.level = RAW_FILEINFO_ALL_INFO;
168         all_info.generic.in.fname = fname;
169         status = smb_raw_pathinfo(cli->tree, mem_ctx, &all_info);
170         if (!NT_STATUS_IS_OK(status)) {
171                 printf("RAW_FILEINFO_ALL_INFO failed - %s\n", nt_errstr(status));
172                 ret = False;
173                 goto done;
174         }
175
176         alt_info.generic.level = RAW_FILEINFO_ALT_NAME_INFO;
177         alt_info.generic.in.fname = fname;
178         status = smb_raw_pathinfo(cli->tree, mem_ctx, &alt_info);
179         if (!NT_STATUS_IS_OK(status)) {
180                 printf("RAW_FILEINFO_ALT_NAME_INFO failed - %s\n", nt_errstr(status));
181                 ret = False;
182                 goto done;
183         }
184
185         internal_info.generic.level = RAW_FILEINFO_INTERNAL_INFORMATION;
186         internal_info.generic.in.fname = fname;
187         status = smb_raw_pathinfo(cli->tree, mem_ctx, &internal_info);
188         if (!NT_STATUS_IS_OK(status)) {
189                 printf("RAW_FILEINFO_INTERNAL_INFORMATION failed - %s\n", nt_errstr(status));
190                 ret = False;
191                 goto done;
192         }
193
194         name_info.generic.level = RAW_FILEINFO_NAME_INFO;
195         name_info.generic.in.fname = fname;
196         status = smb_raw_pathinfo(cli->tree, mem_ctx, &name_info);
197         if (!NT_STATUS_IS_OK(status)) {
198                 printf("RAW_FILEINFO_NAME_INFO failed - %s\n", nt_errstr(status));
199                 ret = False;
200                 goto done;
201         }
202
203 #define CHECK_VAL(name, sname1, field1, v, sname2, field2) do { \
204         s = find(name); \
205         if (s) { \
206                 if ((s->sname1.field1) != (v.sname2.out.field2)) { \
207                         printf("(%d) %s/%s [0x%x] != %s/%s [0x%x]\n", \
208                                __LINE__, \
209                                 #sname1, #field1, (int)s->sname1.field1, \
210                                 #sname2, #field2, (int)v.sname2.out.field2); \
211                         ret = False; \
212                 } \
213         }} while (0)
214
215 #define CHECK_TIME(name, sname1, field1, v, sname2, field2) do { \
216         s = find(name); \
217         if (s) { \
218                 if (s->sname1.field1 != (~1 & nt_time_to_unix(v.sname2.out.field2))) { \
219                         printf("(%d) %s/%s [%s] != %s/%s [%s]\n", \
220                                __LINE__, \
221                                 #sname1, #field1, timestring(mem_ctx, s->sname1.field1), \
222                                 #sname2, #field2, nt_time_string(mem_ctx, v.sname2.out.field2)); \
223                         ret = False; \
224                 } \
225         }} while (0)
226
227 #define CHECK_NTTIME(name, sname1, field1, v, sname2, field2) do { \
228         s = find(name); \
229         if (s) { \
230                 if (s->sname1.field1 != v.sname2.out.field2) { \
231                         printf("(%d) %s/%s [%s] != %s/%s [%s]\n", \
232                                __LINE__, \
233                                 #sname1, #field1, nt_time_string(mem_ctx, s->sname1.field1), \
234                                 #sname2, #field2, nt_time_string(mem_ctx, v.sname2.out.field2)); \
235                         ret = False; \
236                 } \
237         }} while (0)
238
239 #define CHECK_STR(name, sname1, field1, v, sname2, field2) do { \
240         s = find(name); \
241         if (s) { \
242                 if (!s->sname1.field1 || strcmp(s->sname1.field1, v.sname2.out.field2.s)) { \
243                         printf("(%d) %s/%s [%s] != %s/%s [%s]\n", \
244                                __LINE__, \
245                                 #sname1, #field1, s->sname1.field1, \
246                                 #sname2, #field2, v.sname2.out.field2.s); \
247                         ret = False; \
248                 } \
249         }} while (0)
250
251 #define CHECK_WSTR(name, sname1, field1, v, sname2, field2, flags) do { \
252         s = find(name); \
253         if (s) { \
254                 if (!s->sname1.field1.s || \
255                     strcmp(s->sname1.field1.s, v.sname2.out.field2.s) || \
256                     wire_bad_flags(&s->sname1.field1, flags, cli)) { \
257                         printf("(%d) %s/%s [%s] != %s/%s [%s]\n", \
258                                __LINE__, \
259                                 #sname1, #field1, s->sname1.field1.s, \
260                                 #sname2, #field2, v.sname2.out.field2.s); \
261                         ret = False; \
262                 } \
263         }} while (0)
264
265 #define CHECK_NAME(name, sname1, field1, fname, flags) do { \
266         s = find(name); \
267         if (s) { \
268                 if (!s->sname1.field1.s || \
269                     strcmp(s->sname1.field1.s, fname) || \
270                     wire_bad_flags(&s->sname1.field1, flags, cli)) { \
271                         printf("(%d) %s/%s [%s] != %s\n", \
272                                __LINE__, \
273                                 #sname1, #field1, s->sname1.field1.s, \
274                                 fname); \
275                         ret = False; \
276                 } \
277         }} while (0)
278
279 #define CHECK_UNIX_NAME(name, sname1, field1, fname, flags) do { \
280         s = find(name); \
281         if (s) { \
282                 if (!s->sname1.field1 || \
283                     strcmp(s->sname1.field1, fname)) { \
284                         printf("(%d) %s/%s [%s] != %s\n", \
285                                __LINE__, \
286                                 #sname1, #field1, s->sname1.field1, \
287                                 fname); \
288                         ret = False; \
289                 } \
290         }} while (0)
291         
292         /* check that all the results are as expected */
293         CHECK_VAL("SEARCH",              search,              attrib, all_info, all_info, attrib&0xFFF);
294         CHECK_VAL("STANDARD",            standard,            attrib, all_info, all_info, attrib&0xFFF);
295         CHECK_VAL("EA_SIZE",             ea_size,             attrib, all_info, all_info, attrib&0xFFF);
296         CHECK_VAL("DIRECTORY_INFO",      directory_info,      attrib, all_info, all_info, attrib);
297         CHECK_VAL("FULL_DIRECTORY_INFO", full_directory_info, attrib, all_info, all_info, attrib);
298         CHECK_VAL("BOTH_DIRECTORY_INFO", both_directory_info, attrib, all_info, all_info, attrib);
299         CHECK_VAL("ID_FULL_DIRECTORY_INFO", id_full_directory_info,           attrib, all_info, all_info, attrib);
300         CHECK_VAL("ID_BOTH_DIRECTORY_INFO", id_both_directory_info,           attrib, all_info, all_info, attrib);
301
302         CHECK_TIME("SEARCH",             search,              write_time, all_info, all_info, write_time);
303         CHECK_TIME("STANDARD",           standard,            write_time, all_info, all_info, write_time);
304         CHECK_TIME("EA_SIZE",            ea_size,             write_time, all_info, all_info, write_time);
305         CHECK_TIME("STANDARD",           standard,            create_time, all_info, all_info, create_time);
306         CHECK_TIME("EA_SIZE",            ea_size,             create_time, all_info, all_info, create_time);
307         CHECK_TIME("STANDARD",           standard,            access_time, all_info, all_info, access_time);
308         CHECK_TIME("EA_SIZE",            ea_size,             access_time, all_info, all_info, access_time);
309
310         CHECK_NTTIME("DIRECTORY_INFO",      directory_info,      write_time, all_info, all_info, write_time);
311         CHECK_NTTIME("FULL_DIRECTORY_INFO", full_directory_info, write_time, all_info, all_info, write_time);
312         CHECK_NTTIME("BOTH_DIRECTORY_INFO", both_directory_info, write_time, all_info, all_info, write_time);
313         CHECK_NTTIME("ID_FULL_DIRECTORY_INFO", id_full_directory_info,           write_time, all_info, all_info, write_time);
314         CHECK_NTTIME("ID_BOTH_DIRECTORY_INFO", id_both_directory_info,           write_time, all_info, all_info, write_time);
315
316         CHECK_NTTIME("DIRECTORY_INFO",      directory_info,      create_time, all_info, all_info, create_time);
317         CHECK_NTTIME("FULL_DIRECTORY_INFO", full_directory_info, create_time, all_info, all_info, create_time);
318         CHECK_NTTIME("BOTH_DIRECTORY_INFO", both_directory_info, create_time, all_info, all_info, create_time);
319         CHECK_NTTIME("ID_FULL_DIRECTORY_INFO", id_full_directory_info,           create_time, all_info, all_info, create_time);
320         CHECK_NTTIME("ID_BOTH_DIRECTORY_INFO", id_both_directory_info,           create_time, all_info, all_info, create_time);
321
322         CHECK_NTTIME("DIRECTORY_INFO",      directory_info,      access_time, all_info, all_info, access_time);
323         CHECK_NTTIME("FULL_DIRECTORY_INFO", full_directory_info, access_time, all_info, all_info, access_time);
324         CHECK_NTTIME("BOTH_DIRECTORY_INFO", both_directory_info, access_time, all_info, all_info, access_time);
325         CHECK_NTTIME("ID_FULL_DIRECTORY_INFO", id_full_directory_info,           access_time, all_info, all_info, access_time);
326         CHECK_NTTIME("ID_BOTH_DIRECTORY_INFO", id_both_directory_info,           access_time, all_info, all_info, access_time);
327
328         CHECK_NTTIME("DIRECTORY_INFO",      directory_info,      create_time, all_info, all_info, create_time);
329         CHECK_NTTIME("FULL_DIRECTORY_INFO", full_directory_info, create_time, all_info, all_info, create_time);
330         CHECK_NTTIME("BOTH_DIRECTORY_INFO", both_directory_info, create_time, all_info, all_info, create_time);
331         CHECK_NTTIME("ID_FULL_DIRECTORY_INFO", id_full_directory_info,           create_time, all_info, all_info, create_time);
332         CHECK_NTTIME("ID_BOTH_DIRECTORY_INFO", id_both_directory_info,           create_time, all_info, all_info, create_time);
333
334         CHECK_VAL("SEARCH",              search,              size, all_info, all_info, size);
335         CHECK_VAL("STANDARD",            standard,            size, all_info, all_info, size);
336         CHECK_VAL("EA_SIZE",             ea_size,             size, all_info, all_info, size);
337         CHECK_VAL("DIRECTORY_INFO",      directory_info,      size, all_info, all_info, size);
338         CHECK_VAL("FULL_DIRECTORY_INFO", full_directory_info, size, all_info, all_info, size);
339         CHECK_VAL("BOTH_DIRECTORY_INFO", both_directory_info, size, all_info, all_info, size);
340         CHECK_VAL("ID_FULL_DIRECTORY_INFO", id_full_directory_info,           size, all_info, all_info, size);
341         CHECK_VAL("ID_BOTH_DIRECTORY_INFO", id_both_directory_info,           size, all_info, all_info, size);
342         CHECK_VAL("UNIX_INFO",           unix_info,           size, all_info, all_info, size);
343
344         CHECK_VAL("STANDARD",            standard,            alloc_size, all_info, all_info, alloc_size);
345         CHECK_VAL("EA_SIZE",             ea_size,             alloc_size, all_info, all_info, alloc_size);
346         CHECK_VAL("DIRECTORY_INFO",      directory_info,      alloc_size, all_info, all_info, alloc_size);
347         CHECK_VAL("FULL_DIRECTORY_INFO", full_directory_info, alloc_size, all_info, all_info, alloc_size);
348         CHECK_VAL("BOTH_DIRECTORY_INFO", both_directory_info, alloc_size, all_info, all_info, alloc_size);
349         CHECK_VAL("ID_FULL_DIRECTORY_INFO", id_full_directory_info,           alloc_size, all_info, all_info, alloc_size);
350         CHECK_VAL("ID_BOTH_DIRECTORY_INFO", id_both_directory_info,           alloc_size, all_info, all_info, alloc_size);
351         CHECK_VAL("UNIX_INFO",           unix_info,           alloc_size, all_info, all_info, alloc_size);
352
353         CHECK_VAL("EA_SIZE",             ea_size,             ea_size, all_info, all_info, ea_size);
354         CHECK_VAL("FULL_DIRECTORY_INFO", full_directory_info, ea_size, all_info, all_info, ea_size);
355         CHECK_VAL("BOTH_DIRECTORY_INFO", both_directory_info, ea_size, all_info, all_info, ea_size);
356         CHECK_VAL("ID_FULL_DIRECTORY_INFO", id_full_directory_info,           ea_size, all_info, all_info, ea_size);
357         CHECK_VAL("ID_BOTH_DIRECTORY_INFO", id_both_directory_info,           ea_size, all_info, all_info, ea_size);
358
359         CHECK_STR("SEARCH", search, name, alt_info, alt_name_info, fname);
360         CHECK_WSTR("BOTH_DIRECTORY_INFO", both_directory_info, short_name, alt_info, alt_name_info, fname, STR_UNICODE);
361
362         CHECK_NAME("STANDARD",            standard,            name, fname+1, 0);
363         CHECK_NAME("EA_SIZE",             ea_size,             name, fname+1, 0);
364         CHECK_NAME("DIRECTORY_INFO",      directory_info,      name, fname+1, STR_TERMINATE_ASCII);
365         CHECK_NAME("FULL_DIRECTORY_INFO", full_directory_info, name, fname+1, STR_TERMINATE_ASCII);
366         CHECK_NAME("NAME_INFO",           name_info,           name, fname+1, STR_TERMINATE_ASCII);
367         CHECK_NAME("BOTH_DIRECTORY_INFO", both_directory_info, name, fname+1, STR_TERMINATE_ASCII);
368         CHECK_NAME("ID_FULL_DIRECTORY_INFO", id_full_directory_info,           name, fname+1, STR_TERMINATE_ASCII);
369         CHECK_NAME("ID_BOTH_DIRECTORY_INFO", id_both_directory_info,           name, fname+1, STR_TERMINATE_ASCII);
370         CHECK_UNIX_NAME("UNIX_INFO",           unix_info,           name, fname+1, STR_TERMINATE_ASCII);
371
372         CHECK_VAL("ID_FULL_DIRECTORY_INFO", id_full_directory_info, file_id, internal_info, internal_information, file_id);
373         CHECK_VAL("ID_BOTH_DIRECTORY_INFO", id_both_directory_info, file_id, internal_info, internal_information, file_id);
374
375 done:
376         smb_raw_exit(cli->session);
377         smbcli_unlink(cli->tree, fname);
378
379         return ret;
380 }
381
382
383 struct multiple_result {
384         TALLOC_CTX *mem_ctx;
385         int count;
386         union smb_search_data *list;
387 };
388
389 /*
390   callback function for multiple_search
391 */
392 static BOOL multiple_search_callback(void *private, union smb_search_data *file)
393 {
394         struct multiple_result *data = private;
395
396
397         data->count++;
398         data->list = talloc_realloc(data->list, 
399                                     data->count * (sizeof(data->list[0])));
400
401         data->list[data->count-1] = *file;
402
403         return True;
404 }
405
406 enum continue_type {CONT_FLAGS, CONT_NAME, CONT_RESUME_KEY};
407
408 /*
409   do a single file (non-wildcard) search 
410 */
411 static NTSTATUS multiple_search(struct smbcli_state *cli, 
412                                 TALLOC_CTX *mem_ctx,
413                                 const char *pattern,
414                                 enum smb_search_level level,
415                                 enum continue_type cont_type,
416                                 void *data)
417 {
418         union smb_search_first io;
419         union smb_search_next io2;
420         NTSTATUS status;
421         const int per_search = 300;
422         struct multiple_result *result = data;
423
424         io.generic.level = level;
425         if (level == RAW_SEARCH_SEARCH) {
426                 io.search_first.in.max_count = per_search;
427                 io.search_first.in.search_attrib = 0;
428                 io.search_first.in.pattern = pattern;
429         } else {
430                 io.t2ffirst.in.search_attrib = 0;
431                 io.t2ffirst.in.max_count = per_search;
432                 io.t2ffirst.in.flags = 0;
433                 io.t2ffirst.in.storage_type = 0;
434                 io.t2ffirst.in.pattern = pattern;
435                 if (cont_type == CONT_RESUME_KEY) {
436                         io.t2ffirst.in.flags = FLAG_TRANS2_FIND_REQUIRE_RESUME | 
437                                 FLAG_TRANS2_FIND_BACKUP_INTENT;
438                 }
439         }
440
441         status = smb_raw_search_first(cli->tree, mem_ctx,
442                                       &io, data, multiple_search_callback);
443         
444
445         while (NT_STATUS_IS_OK(status)) {
446                 io2.generic.level = level;
447                 if (level == RAW_SEARCH_SEARCH) {
448                         io2.search_next.in.max_count = per_search;
449                         io2.search_next.in.search_attrib = 0;
450                         io2.search_next.in.search_id = result->list[result->count-1].search.search_id;
451                 } else {
452                         io2.t2fnext.in.handle = io.t2ffirst.out.handle;
453                         io2.t2fnext.in.max_count = per_search;
454                         io2.t2fnext.in.resume_key = 0;
455                         io2.t2fnext.in.flags = 0;
456                         io2.t2fnext.in.last_name = "";
457                         switch (cont_type) {
458                         case CONT_RESUME_KEY:
459                                 if (level == RAW_SEARCH_STANDARD) {
460                                         io2.t2fnext.in.resume_key = 
461                                                 result->list[result->count-1].standard.resume_key;
462                                 } else if (level == RAW_SEARCH_EA_SIZE) {
463                                         io2.t2fnext.in.resume_key = 
464                                                 result->list[result->count-1].ea_size.resume_key;
465                                 } else if (level == RAW_SEARCH_DIRECTORY_INFO) {
466                                         io2.t2fnext.in.resume_key = 
467                                                 result->list[result->count-1].directory_info.file_index;
468                                 } else {
469                                         io2.t2fnext.in.resume_key = 
470                                                 result->list[result->count-1].both_directory_info.file_index;
471                                 }
472                                 if (io2.t2fnext.in.resume_key == 0) {
473                                         printf("Server does not support resume by key\n");
474                                         return NT_STATUS_NOT_SUPPORTED;
475                                 }
476                                 io2.t2fnext.in.flags = FLAG_TRANS2_FIND_REQUIRE_RESUME |
477                                         FLAG_TRANS2_FIND_BACKUP_INTENT;
478                                 break;
479                         case CONT_NAME:
480                                 if (level == RAW_SEARCH_STANDARD) {
481                                         io2.t2fnext.in.last_name = 
482                                                 result->list[result->count-1].standard.name.s;
483                                 } else if (level == RAW_SEARCH_EA_SIZE) {
484                                         io2.t2fnext.in.last_name = 
485                                                 result->list[result->count-1].ea_size.name.s;
486                                 } else if (level == RAW_SEARCH_DIRECTORY_INFO) {
487                                         io2.t2fnext.in.last_name = 
488                                                 result->list[result->count-1].directory_info.name.s;
489                                 } else {
490                                         io2.t2fnext.in.last_name = 
491                                                 result->list[result->count-1].both_directory_info.name.s;
492                                 }
493                                 break;
494                         case CONT_FLAGS:
495                                 io2.t2fnext.in.flags = FLAG_TRANS2_FIND_CONTINUE;
496                                 break;
497                         }
498                 }
499
500                 status = smb_raw_search_next(cli->tree, mem_ctx,
501                                              &io2, data, multiple_search_callback);
502                 if (!NT_STATUS_IS_OK(status)) {
503                         break;
504                 }
505                 if (level == RAW_SEARCH_SEARCH) {
506                         if (io2.search_next.out.count == 0) {
507                                 break;
508                         }
509                 } else if (io2.t2fnext.out.count == 0 ||
510                            io2.t2fnext.out.end_of_search) {
511                         break;
512                 }
513         }
514
515         return status;
516 }
517
518 #define CHECK_STATUS(status, correct) do { \
519         if (!NT_STATUS_EQUAL(status, correct)) { \
520                 printf("(%d) Incorrect status %s - should be %s\n", \
521                        __LINE__, nt_errstr(status), nt_errstr(correct)); \
522                 ret = False; \
523                 goto done; \
524         }} while (0)
525
526 #define CHECK_VALUE(v, correct) do { \
527         if ((v) != (correct)) { \
528                 printf("(%d) Incorrect value %s=%d - should be %d\n", \
529                        __LINE__, #v, v, correct); \
530                 ret = False; \
531         }} while (0)
532
533
534 static int search_both_compare(union smb_search_data *d1, union smb_search_data *d2)
535 {
536         return strcmp_safe(d1->both_directory_info.name.s, d2->both_directory_info.name.s);
537 }
538
539 static int search_standard_compare(union smb_search_data *d1, union smb_search_data *d2)
540 {
541         return strcmp_safe(d1->standard.name.s, d2->standard.name.s);
542 }
543
544 static int search_ea_size_compare(union smb_search_data *d1, union smb_search_data *d2)
545 {
546         return strcmp_safe(d1->ea_size.name.s, d2->ea_size.name.s);
547 }
548
549 static int search_directory_info_compare(union smb_search_data *d1, union smb_search_data *d2)
550 {
551         return strcmp_safe(d1->directory_info.name.s, d2->directory_info.name.s);
552 }
553
554 static int search_old_compare(union smb_search_data *d1, union smb_search_data *d2)
555 {
556         return strcmp_safe(d1->search.name, d2->search.name);
557 }
558
559
560 /* 
561    basic testing of search calls using many files
562 */
563 static BOOL test_many_files(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
564 {
565         const int num_files = 700;
566         int i, fnum, t;
567         char *fname;
568         BOOL ret = True;
569         NTSTATUS status;
570         struct multiple_result result;
571         struct {
572                 const char *name;
573                 const char *cont_name;
574                 enum smb_search_level level;
575                 enum continue_type cont_type;
576         } search_types[] = {
577                 {"BOTH_DIRECTORY_INFO", "NAME",  RAW_SEARCH_BOTH_DIRECTORY_INFO, CONT_NAME},
578                 {"BOTH_DIRECTORY_INFO", "FLAGS", RAW_SEARCH_BOTH_DIRECTORY_INFO, CONT_FLAGS},
579                 {"BOTH_DIRECTORY_INFO", "KEY",   RAW_SEARCH_BOTH_DIRECTORY_INFO, CONT_RESUME_KEY},
580                 {"STANDARD",            "FLAGS", RAW_SEARCH_STANDARD,            CONT_FLAGS},
581                 {"STANDARD",            "KEY",   RAW_SEARCH_STANDARD,            CONT_RESUME_KEY},
582                 {"STANDARD",            "NAME",  RAW_SEARCH_STANDARD,            CONT_NAME},
583                 {"EA_SIZE",             "FLAGS", RAW_SEARCH_EA_SIZE,             CONT_FLAGS},
584                 {"EA_SIZE",             "KEY",   RAW_SEARCH_EA_SIZE,             CONT_RESUME_KEY},
585                 {"EA_SIZE",             "NAME",  RAW_SEARCH_EA_SIZE,             CONT_NAME},
586                 {"DIRECTORY_INFO",      "FLAGS", RAW_SEARCH_DIRECTORY_INFO,      CONT_FLAGS},
587                 {"DIRECTORY_INFO",      "KEY",   RAW_SEARCH_DIRECTORY_INFO,      CONT_RESUME_KEY},
588                 {"DIRECTORY_INFO",      "NAME",  RAW_SEARCH_DIRECTORY_INFO,      CONT_NAME},
589                 {"SEARCH",              "ID",    RAW_SEARCH_SEARCH,              CONT_RESUME_KEY}
590         };
591
592         if (smbcli_deltree(cli->tree, BASEDIR) == -1 || 
593             NT_STATUS_IS_ERR(smbcli_mkdir(cli->tree, BASEDIR))) {
594                 printf("Failed to create " BASEDIR " - %s\n", smbcli_errstr(cli->tree));
595                 return False;
596         }
597
598         printf("Creating %d files\n", num_files);
599
600         for (i=0;i<num_files;i++) {
601                 asprintf(&fname, BASEDIR "\\t%03d-%d.txt", i, i);
602                 fnum = smbcli_open(cli->tree, fname, O_CREAT|O_RDWR, DENY_NONE);
603                 if (fnum == -1) {
604                         printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree));
605                         ret = False;
606                         goto done;
607                 }
608                 free(fname);
609                 smbcli_close(cli->tree, fnum);
610         }
611
612
613         for (t=0;t<ARRAY_SIZE(search_types);t++) {
614                 ZERO_STRUCT(result);
615                 result.mem_ctx = mem_ctx;
616         
617                 printf("Continue %s via %s\n", search_types[t].name, search_types[t].cont_name);
618
619                 status = multiple_search(cli, mem_ctx, BASEDIR "\\*.*", 
620                                          search_types[t].level,
621                                          search_types[t].cont_type,
622                                          &result);
623         
624                 if (!NT_STATUS_IS_OK(status)) {
625                         printf("search failed - %s\n", nt_errstr(status));
626                         ret = False;
627                         continue;
628                 }
629                 CHECK_VALUE(result.count, num_files);
630
631                 if (search_types[t].level == RAW_SEARCH_BOTH_DIRECTORY_INFO) {
632                         qsort(result.list, result.count, sizeof(result.list[0]), 
633                               QSORT_CAST  search_both_compare);
634                 } else if (search_types[t].level == RAW_SEARCH_STANDARD) {
635                         qsort(result.list, result.count, sizeof(result.list[0]), 
636                               QSORT_CAST search_standard_compare);
637                 } else if (search_types[t].level == RAW_SEARCH_EA_SIZE) {
638                         qsort(result.list, result.count, sizeof(result.list[0]), 
639                               QSORT_CAST search_ea_size_compare);
640                 } else if (search_types[t].level == RAW_SEARCH_DIRECTORY_INFO) {
641                         qsort(result.list, result.count, sizeof(result.list[0]), 
642                               QSORT_CAST search_directory_info_compare);
643                 } else {
644                         qsort(result.list, result.count, sizeof(result.list[0]), 
645                               QSORT_CAST search_old_compare);
646                 }
647
648                 for (i=0;i<num_files;i++) {
649                         const char *s;
650                         if (search_types[t].level == RAW_SEARCH_BOTH_DIRECTORY_INFO) {
651                                 s = result.list[i].both_directory_info.name.s;
652                         } else if (search_types[t].level == RAW_SEARCH_STANDARD) {
653                                 s = result.list[i].standard.name.s;
654                         } else if (search_types[t].level == RAW_SEARCH_EA_SIZE) {
655                                 s = result.list[i].ea_size.name.s;
656                         } else if (search_types[t].level == RAW_SEARCH_DIRECTORY_INFO) {
657                                 s = result.list[i].directory_info.name.s;
658                         } else {
659                                 s = result.list[i].search.name;
660                         }
661                         asprintf(&fname, "t%03d-%d.txt", i, i);
662                         if (strcmp(fname, s)) {
663                                 printf("Incorrect name %s at entry %d\n", s, i);
664                                 ret = False;
665                                 break;
666                         }
667                         free(fname);
668                 }
669         }
670
671 done:
672         smb_raw_exit(cli->session);
673         smbcli_deltree(cli->tree, BASEDIR);
674
675         return ret;
676 }
677
678 /*
679   check a individual file result
680 */
681 static BOOL check_result(struct multiple_result *result, const char *name, BOOL exist, uint32_t attrib)
682 {
683         int i;
684         for (i=0;i<result->count;i++) {
685                 if (strcmp(name, result->list[i].both_directory_info.name.s) == 0) break;
686         }
687         if (i == result->count) {
688                 if (exist) {
689                         printf("failed: '%s' should exist with attribute %s\n", 
690                                name, attrib_string(NULL, attrib));
691                         return False;
692                 }
693                 return True;
694         }
695
696         if (!exist) {
697                 printf("failed: '%s' should NOT exist (has attribute %s)\n", 
698                        name, attrib_string(NULL, result->list[i].both_directory_info.attrib));
699                 return False;
700         }
701
702         if ((result->list[i].both_directory_info.attrib&0xFFF) != attrib) {
703                 printf("failed: '%s' should have attribute 0x%x (has 0x%x)\n",
704                        name, 
705                        attrib, result->list[i].both_directory_info.attrib);
706                 return False;
707         }
708         return True;
709 }
710
711 /* 
712    test what happens when the directory is modified during a search
713 */
714 static BOOL test_modify_search(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
715 {
716         const int num_files = 20;
717         int i, fnum;
718         char *fname;
719         BOOL ret = True;
720         NTSTATUS status;
721         struct multiple_result result;
722         union smb_search_first io;
723         union smb_search_next io2;
724         union smb_setfileinfo sfinfo;
725
726         if (smbcli_deltree(cli->tree, BASEDIR) == -1 || 
727             NT_STATUS_IS_ERR(smbcli_mkdir(cli->tree, BASEDIR))) {
728                 printf("Failed to create " BASEDIR " - %s\n", smbcli_errstr(cli->tree));
729                 return False;
730         }
731
732         printf("Creating %d files\n", num_files);
733
734         for (i=0;i<num_files;i++) {
735                 asprintf(&fname, BASEDIR "\\t%03d-%d.txt", i, i);
736                 fnum = smbcli_open(cli->tree, fname, O_CREAT|O_RDWR, DENY_NONE);
737                 if (fnum == -1) {
738                         printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree));
739                         ret = False;
740                         goto done;
741                 }
742                 free(fname);
743                 smbcli_close(cli->tree, fnum);
744         }
745
746         printf("pulling the first 10 files\n");
747         ZERO_STRUCT(result);
748
749         io.generic.level = RAW_SEARCH_BOTH_DIRECTORY_INFO;
750         io.t2ffirst.in.search_attrib = 0;
751         io.t2ffirst.in.max_count = 0;
752         io.t2ffirst.in.flags = 0;
753         io.t2ffirst.in.storage_type = 0;
754         io.t2ffirst.in.pattern = BASEDIR "\\*.*";
755
756         status = smb_raw_search_first(cli->tree, mem_ctx,
757                                       &io, &result, multiple_search_callback);
758         CHECK_STATUS(status, NT_STATUS_OK);
759         CHECK_VALUE(result.count, 1);
760
761         io2.generic.level = RAW_SEARCH_BOTH_DIRECTORY_INFO;
762         io2.t2fnext.in.handle = io.t2ffirst.out.handle;
763         io2.t2fnext.in.max_count = num_files/2 - 1;
764         io2.t2fnext.in.resume_key = 0;
765         io2.t2fnext.in.flags = 0;
766         io2.t2fnext.in.last_name = result.list[result.count-1].both_directory_info.name.s;
767
768         status = smb_raw_search_next(cli->tree, mem_ctx,
769                                      &io2, &result, multiple_search_callback);
770         CHECK_STATUS(status, NT_STATUS_OK);
771         CHECK_VALUE(result.count, num_files/2);
772
773         printf("Changing attributes and deleting\n");
774         smbcli_open(cli->tree, BASEDIR "\\T003-03.txt.2", O_CREAT|O_RDWR, DENY_NONE);
775         smbcli_open(cli->tree, BASEDIR "\\T013-13.txt.2", O_CREAT|O_RDWR, DENY_NONE);
776         fnum = create_complex_file(cli, mem_ctx, BASEDIR "\\T013-13.txt.3");
777         smbcli_unlink(cli->tree, BASEDIR "\\T014-14.txt");
778         torture_set_file_attribute(cli->tree, BASEDIR "\\T015-15.txt", FILE_ATTRIBUTE_HIDDEN);
779         torture_set_file_attribute(cli->tree, BASEDIR "\\T016-16.txt", FILE_ATTRIBUTE_NORMAL);
780         torture_set_file_attribute(cli->tree, BASEDIR "\\T017-17.txt", FILE_ATTRIBUTE_SYSTEM);  
781         sfinfo.generic.level = RAW_SFILEINFO_DISPOSITION_INFORMATION;
782         sfinfo.generic.file.fnum = fnum;
783         sfinfo.disposition_info.in.delete_on_close = 1;
784         status = smb_raw_setfileinfo(cli->tree, &sfinfo);
785         CHECK_STATUS(status, NT_STATUS_OK);
786
787         io2.generic.level = RAW_SEARCH_BOTH_DIRECTORY_INFO;
788         io2.t2fnext.in.handle = io.t2ffirst.out.handle;
789         io2.t2fnext.in.max_count = num_files/2;
790         io2.t2fnext.in.resume_key = 0;
791         io2.t2fnext.in.flags = 0;
792         io2.t2fnext.in.last_name = result.list[result.count-1].both_directory_info.name.s;
793
794         status = smb_raw_search_next(cli->tree, mem_ctx,
795                                      &io2, &result, multiple_search_callback);
796         CHECK_STATUS(status, NT_STATUS_OK);
797         CHECK_VALUE(result.count, 19);
798
799         ret &= check_result(&result, "t009-9.txt", True, FILE_ATTRIBUTE_ARCHIVE);
800         ret &= check_result(&result, "t014-14.txt", False, 0);
801         ret &= check_result(&result, "t015-15.txt", False, 0);
802         ret &= check_result(&result, "t016-16.txt", True, FILE_ATTRIBUTE_NORMAL);
803         ret &= check_result(&result, "t017-17.txt", False, 0);
804         ret &= check_result(&result, "t018-18.txt", True, FILE_ATTRIBUTE_ARCHIVE);
805         ret &= check_result(&result, "t019-19.txt", True, FILE_ATTRIBUTE_ARCHIVE);
806         ret &= check_result(&result, "T013-13.txt.2", True, FILE_ATTRIBUTE_ARCHIVE);
807         ret &= check_result(&result, "T003-3.txt.2", False, 0);
808         ret &= check_result(&result, "T013-13.txt.3", True, FILE_ATTRIBUTE_ARCHIVE);
809
810         if (!ret) {
811                 for (i=0;i<result.count;i++) {
812                         printf("%s %s (0x%x)\n", 
813                                result.list[i].both_directory_info.name.s, 
814                                attrib_string(mem_ctx, result.list[i].both_directory_info.attrib),
815                                result.list[i].both_directory_info.attrib);
816                 }
817         }
818
819 done:
820         smb_raw_exit(cli->session);
821         smbcli_deltree(cli->tree, BASEDIR);
822
823         return ret;
824 }
825
826
827 /* 
828    testing if directories always come back sorted
829 */
830 static BOOL test_sorted(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
831 {
832         const int num_files = 700;
833         int i, fnum;
834         char *fname;
835         BOOL ret = True;
836         NTSTATUS status;
837         struct multiple_result result;
838
839         if (smbcli_deltree(cli->tree, BASEDIR) == -1 || 
840             NT_STATUS_IS_ERR(smbcli_mkdir(cli->tree, BASEDIR))) {
841                 printf("Failed to create " BASEDIR " - %s\n", smbcli_errstr(cli->tree));
842                 return False;
843         }
844
845         printf("Creating %d files\n", num_files);
846
847         for (i=0;i<num_files;i++) {
848                 asprintf(&fname, BASEDIR "\\%s.txt", generate_random_str(mem_ctx, 10));
849                 fnum = smbcli_open(cli->tree, fname, O_CREAT|O_RDWR, DENY_NONE);
850                 if (fnum == -1) {
851                         printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree));
852                         ret = False;
853                         goto done;
854                 }
855                 free(fname);
856                 smbcli_close(cli->tree, fnum);
857         }
858
859
860         ZERO_STRUCT(result);
861         result.mem_ctx = mem_ctx;
862         
863         status = multiple_search(cli, mem_ctx, BASEDIR "\\*.*", 
864                                  RAW_SEARCH_BOTH_DIRECTORY_INFO,
865                                  CONT_NAME, &result);   
866         CHECK_STATUS(status, NT_STATUS_OK);
867         CHECK_VALUE(result.count, num_files);
868
869         for (i=0;i<num_files-1;i++) {
870                 const char *name1, *name2;
871                 name1 = result.list[i].both_directory_info.name.s;
872                 name2 = result.list[i+1].both_directory_info.name.s;
873                 if (StrCaseCmp(name1, name2) > 0) {
874                         printf("non-alphabetical order at entry %d  '%s' '%s'\n", 
875                                i, name1, name2);
876                         printf("Server does not produce sorted directory listings\n");
877                         ret = False;
878                         goto done;
879                 }
880         }
881
882 done:
883         smb_raw_exit(cli->session);
884         smbcli_deltree(cli->tree, BASEDIR);
885
886         return ret;
887 }
888
889
890 /* 
891    basic testing of all RAW_SEARCH_* calls using a single file
892 */
893 BOOL torture_raw_search(int dummy)
894 {
895         struct smbcli_state *cli;
896         BOOL ret = True;
897         TALLOC_CTX *mem_ctx;
898
899         if (!torture_open_connection(&cli)) {
900                 return False;
901         }
902
903         mem_ctx = talloc_init("torture_search");
904
905         if (!test_one_file(cli, mem_ctx)) {
906                 ret = False;
907         }
908
909         if (!test_many_files(cli, mem_ctx)) {
910                 ret = False;
911         }
912
913         if (!test_sorted(cli, mem_ctx)) {
914                 ret = False;
915         }
916
917         if (!test_modify_search(cli, mem_ctx)) {
918                 ret = False;
919         }
920
921         torture_close_connection(cli);
922         talloc_destroy(mem_ctx);
923         
924         return ret;
925 }