924c7f2bf6be1109be62382c672a36f802734228
[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 3 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, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "includes.h"
21 #include "system/filesys.h"
22 #include "libcli/raw/libcliraw.h"
23 #include "libcli/raw/raw_proto.h"
24 #include "libcli/libcli.h"
25 #include "torture/util.h"
26 #include "lib/util/tsort.h"
27 #include "torture/raw/proto.h"
28
29
30 #define BASEDIR "\\testsearch"
31
32 #define CHECK_STATUS_LEVEL(__tctx, __status, __level, __supp)           \
33         do {                                                            \
34                 if (NT_STATUS_EQUAL(__status,                           \
35                         NT_STATUS_NOT_SUPPORTED) ||                     \
36                     NT_STATUS_EQUAL(__status,                           \
37                         NT_STATUS_NOT_IMPLEMENTED)) {                   \
38                         __supp = false;                                 \
39                 } else {                                                \
40                         __supp = true;                                  \
41                 }                                                       \
42                 if (__supp) {                                           \
43                         torture_assert_ntstatus_ok_goto(__tctx,         \
44                             __status, ret, done, #__level" failed");    \
45                 } else {                                                \
46                         torture_warning(__tctx, "(%s) Info "            \
47                             "level "#__level" is %s",                   \
48                             __location__, nt_errstr(__status));         \
49                 }                                                       \
50         } while (0)
51
52 /*
53   callback function for single_search
54 */
55 static bool single_search_callback(void *private_data, const union smb_search_data *file)
56 {
57         union smb_search_data *data = (union smb_search_data *)private_data;
58
59         *data = *file;
60
61         return true;
62 }
63
64 /*
65   do a single file (non-wildcard) search 
66 */
67 NTSTATUS torture_single_search(struct smbcli_state *cli, 
68                                TALLOC_CTX *tctx,
69                                const char *pattern,
70                                enum smb_search_level level,
71                                enum smb_search_data_level data_level,
72                                uint16_t attrib,
73                                union smb_search_data *data)
74 {
75         union smb_search_first io;
76         union smb_search_close c;
77         NTSTATUS status;
78
79         switch (level) {
80         case RAW_SEARCH_SEARCH:
81         case RAW_SEARCH_FFIRST:
82         case RAW_SEARCH_FUNIQUE:
83                 io.search_first.level = level;
84                 io.search_first.data_level = RAW_SEARCH_DATA_SEARCH;
85                 io.search_first.in.max_count = 1;
86                 io.search_first.in.search_attrib = attrib;
87                 io.search_first.in.pattern = pattern;
88                 break;
89
90         case RAW_SEARCH_TRANS2:
91                 io.t2ffirst.level = RAW_SEARCH_TRANS2;
92                 io.t2ffirst.data_level = data_level;
93                 io.t2ffirst.in.search_attrib = attrib;
94                 io.t2ffirst.in.max_count = 1;
95                 io.t2ffirst.in.flags = FLAG_TRANS2_FIND_CLOSE;
96                 io.t2ffirst.in.storage_type = 0;
97                 io.t2ffirst.in.pattern = pattern;
98                 break;
99
100         case RAW_SEARCH_SMB2:
101                 return NT_STATUS_INVALID_LEVEL;
102         }
103
104         status = smb_raw_search_first(cli->tree, tctx,
105                                       &io, (void *)data, single_search_callback);
106
107         if (NT_STATUS_IS_OK(status) && level == RAW_SEARCH_FFIRST) {
108                 c.fclose.level = RAW_FINDCLOSE_FCLOSE;
109                 c.fclose.in.max_count = 1;
110                 c.fclose.in.search_attrib = 0;
111                 c.fclose.in.id = data->search.id;
112                 status = smb_raw_search_close(cli->tree, &c);
113         }
114         
115         return status;
116 }
117
118
119 static struct {
120         const char *name;
121         enum smb_search_level level;
122         enum smb_search_data_level data_level;
123         int name_offset;
124         int resume_key_offset;
125         uint32_t capability_mask;
126         NTSTATUS status;
127         union smb_search_data data;
128 } levels[] = {
129         {"FFIRST",
130          RAW_SEARCH_FFIRST, RAW_SEARCH_DATA_SEARCH,
131          offsetof(union smb_search_data, search.name),
132          -1,
133         },
134         {"FUNIQUE",
135          RAW_SEARCH_FUNIQUE, RAW_SEARCH_DATA_SEARCH,
136          offsetof(union smb_search_data, search.name),
137          -1,
138         },
139         {"SEARCH",
140          RAW_SEARCH_SEARCH, RAW_SEARCH_DATA_SEARCH,
141          offsetof(union smb_search_data, search.name),
142          -1,
143         },
144         {"STANDARD",
145          RAW_SEARCH_TRANS2, RAW_SEARCH_DATA_STANDARD, 
146          offsetof(union smb_search_data, standard.name.s),
147          offsetof(union smb_search_data, standard.resume_key),
148         },
149         {"EA_SIZE",
150          RAW_SEARCH_TRANS2, RAW_SEARCH_DATA_EA_SIZE, 
151          offsetof(union smb_search_data, ea_size.name.s),
152          offsetof(union smb_search_data, ea_size.resume_key),
153         },
154         {"DIRECTORY_INFO",
155          RAW_SEARCH_TRANS2, RAW_SEARCH_DATA_DIRECTORY_INFO, 
156          offsetof(union smb_search_data, directory_info.name.s),
157          offsetof(union smb_search_data, directory_info.file_index),
158         },
159         {"FULL_DIRECTORY_INFO",
160          RAW_SEARCH_TRANS2, RAW_SEARCH_DATA_FULL_DIRECTORY_INFO, 
161          offsetof(union smb_search_data, full_directory_info.name.s),
162          offsetof(union smb_search_data, full_directory_info.file_index),
163         },
164         {"NAME_INFO",
165          RAW_SEARCH_TRANS2, RAW_SEARCH_DATA_NAME_INFO, 
166          offsetof(union smb_search_data, name_info.name.s),
167          offsetof(union smb_search_data, name_info.file_index),
168         },
169         {"BOTH_DIRECTORY_INFO",
170          RAW_SEARCH_TRANS2, RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO, 
171          offsetof(union smb_search_data, both_directory_info.name.s),
172          offsetof(union smb_search_data, both_directory_info.file_index),
173         },
174         {"ID_FULL_DIRECTORY_INFO",
175          RAW_SEARCH_TRANS2, RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO, 
176          offsetof(union smb_search_data, id_full_directory_info.name.s),
177          offsetof(union smb_search_data, id_full_directory_info.file_index),
178         },
179         {"ID_BOTH_DIRECTORY_INFO",
180          RAW_SEARCH_TRANS2, RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO, 
181          offsetof(union smb_search_data, id_both_directory_info.name.s),
182          offsetof(union smb_search_data, id_both_directory_info.file_index),
183         },
184         {"UNIX_INFO",
185          RAW_SEARCH_TRANS2, RAW_SEARCH_DATA_UNIX_INFO, 
186          offsetof(union smb_search_data, unix_info.name),
187          offsetof(union smb_search_data, unix_info.file_index),
188          CAP_UNIX}
189 };
190
191
192 /*
193   return level name
194 */
195 static const char *level_name(enum smb_search_level level,
196                               enum smb_search_data_level data_level)
197 {
198         int i;
199         for (i=0;i<ARRAY_SIZE(levels);i++) {
200                 if (level == levels[i].level &&
201                     data_level == levels[i].data_level) {
202                         return levels[i].name;
203                 }
204         }
205         return NULL;
206 }
207
208 /*
209   extract the name from a smb_data structure and level
210 */
211 static const char *extract_name(void *data, enum smb_search_level level,
212                                 enum smb_search_data_level data_level)
213 {
214         int i;
215         for (i=0;i<ARRAY_SIZE(levels);i++) {
216                 if (level == levels[i].level &&
217                     data_level == levels[i].data_level) {
218                         return *(const char **)(levels[i].name_offset + (char *)data);
219                 }
220         }
221         return NULL;
222 }
223
224 /*
225   extract the name from a smb_data structure and level
226 */
227 static uint32_t extract_resume_key(void *data, enum smb_search_level level,
228                                    enum smb_search_data_level data_level)
229 {
230         int i;
231         for (i=0;i<ARRAY_SIZE(levels);i++) {
232                 if (level == levels[i].level &&
233                     data_level == levels[i].data_level) {
234                         return *(uint32_t *)(levels[i].resume_key_offset + (char *)data);
235                 }
236         }
237         return 0;
238 }
239
240 /* find a level in the table by name */
241 static union smb_search_data *find(const char *name)
242 {
243         int i;
244         for (i=0;i<ARRAY_SIZE(levels);i++) {
245                 if (NT_STATUS_IS_OK(levels[i].status) && 
246                     strcmp(levels[i].name, name) == 0) {
247                         return &levels[i].data;
248                 }
249         }
250         return NULL;
251 }
252
253 /* 
254    basic testing of all RAW_SEARCH_* calls using a single file
255 */
256 static bool test_one_file(struct torture_context *tctx, 
257                           struct smbcli_state *cli)
258 {
259         bool ret = true;
260         int fnum;
261         const char *fname = "\\torture_search.txt";
262         const char *fname2 = "\\torture_search-NOTEXIST.txt";
263         NTSTATUS status;
264         int i;
265         union smb_fileinfo all_info, alt_info, name_info, internal_info;
266         bool all_info_supported, alt_info_supported, name_info_supported,
267             internal_info_supported;
268         union smb_search_data *s;
269
270         fnum = create_complex_file(cli, tctx, fname);
271         if (fnum == -1) {
272                 printf("ERROR: open of %s failed (%s)\n", fname, smbcli_errstr(cli->tree));
273                 ret = false;
274                 goto done;
275         }
276
277         /* call all the levels */
278         for (i=0;i<ARRAY_SIZE(levels);i++) {
279                 NTSTATUS expected_status;
280                 uint32_t cap = cli->transport->negotiate.capabilities;
281
282                 torture_comment(tctx, "Testing %s\n", levels[i].name);
283
284                 levels[i].status = torture_single_search(cli, tctx, fname, 
285                                                          levels[i].level,
286                                                          levels[i].data_level,
287                                                          0,
288                                                          &levels[i].data);
289
290                 /* see if this server claims to support this level */
291                 if (((cap & levels[i].capability_mask) != levels[i].capability_mask)
292                     || NT_STATUS_EQUAL(levels[i].status, NT_STATUS_NOT_SUPPORTED)) {
293                         printf("search level %s(%d) not supported by server\n",
294                                levels[i].name, (int)levels[i].level);
295                         continue;
296                 }
297
298                 if (!NT_STATUS_IS_OK(levels[i].status)) {
299                         printf("search level %s(%d) failed - %s\n",
300                                levels[i].name, (int)levels[i].level, 
301                                nt_errstr(levels[i].status));
302                         ret = false;
303                         continue;
304                 }
305
306                 status = torture_single_search(cli, tctx, fname2, 
307                                                levels[i].level,
308                                                levels[i].data_level,
309                                                0,
310                                                &levels[i].data);
311                 
312                 expected_status = NT_STATUS_NO_SUCH_FILE;
313                 if (levels[i].level == RAW_SEARCH_SEARCH ||
314                     levels[i].level == RAW_SEARCH_FFIRST ||
315                     levels[i].level == RAW_SEARCH_FUNIQUE) {
316                         expected_status = STATUS_NO_MORE_FILES;
317                 }
318                 if (!NT_STATUS_EQUAL(status, expected_status)) {
319                         printf("search level %s(%d) should fail with %s - %s\n",
320                                levels[i].name, (int)levels[i].level, 
321                                nt_errstr(expected_status),
322                                nt_errstr(status));
323                         ret = false;
324                 }
325         }
326
327         /* get the all_info file into to check against */
328         all_info.generic.level = RAW_FILEINFO_ALL_INFO;
329         all_info.generic.in.file.path = fname;
330         status = smb_raw_pathinfo(cli->tree, tctx, &all_info);
331         CHECK_STATUS_LEVEL(tctx, status, "RAW_FILEINFO_ALL_INFO",
332             all_info_supported);
333
334         alt_info.generic.level = RAW_FILEINFO_ALT_NAME_INFO;
335         alt_info.generic.in.file.path = fname;
336         status = smb_raw_pathinfo(cli->tree, tctx, &alt_info);
337         CHECK_STATUS_LEVEL(tctx, status, "RAW_FILEINFO_ALT_NAME_INFO",
338             alt_info_supported);
339
340         internal_info.generic.level = RAW_FILEINFO_INTERNAL_INFORMATION;
341         internal_info.generic.in.file.path = fname;
342         status = smb_raw_pathinfo(cli->tree, tctx, &internal_info);
343         CHECK_STATUS_LEVEL(tctx, status, "RAW_FILEINFO_INTERNAL_INFORMATION",
344             internal_info_supported);
345
346         name_info.generic.level = RAW_FILEINFO_NAME_INFO;
347         name_info.generic.in.file.path = fname;
348         status = smb_raw_pathinfo(cli->tree, tctx, &name_info);
349         CHECK_STATUS_LEVEL(tctx, status, "RAW_FILEINFO_NAME_INFO",
350             name_info_supported);
351
352 #define CHECK_VAL(name, sname1, field1, v, sname2, field2) do { \
353         s = find(name); \
354         if (s) { \
355                 if ((s->sname1.field1) != (v.sname2.out.field2)) { \
356                         printf("(%s) %s/%s [0x%x] != %s/%s [0x%x]\n", \
357                                __location__, \
358                                 #sname1, #field1, (int)s->sname1.field1, \
359                                 #sname2, #field2, (int)v.sname2.out.field2); \
360                         ret = false; \
361                 } \
362         }} while (0)
363
364 #define CHECK_TIME(name, sname1, field1, v, sname2, field2) do { \
365         s = find(name); \
366         if (s) { \
367                 if (s->sname1.field1 != (~1 & nt_time_to_unix(v.sname2.out.field2))) { \
368                         printf("(%s) %s/%s [%s] != %s/%s [%s]\n", \
369                                __location__, \
370                                 #sname1, #field1, timestring(tctx, s->sname1.field1), \
371                                 #sname2, #field2, nt_time_string(tctx, v.sname2.out.field2)); \
372                         ret = false; \
373                 } \
374         }} while (0)
375
376 #define CHECK_NTTIME(name, sname1, field1, v, sname2, field2) do { \
377         s = find(name); \
378         if (s) { \
379                 if (s->sname1.field1 != v.sname2.out.field2) { \
380                         printf("(%s) %s/%s [%s] != %s/%s [%s]\n", \
381                                __location__, \
382                                 #sname1, #field1, nt_time_string(tctx, s->sname1.field1), \
383                                 #sname2, #field2, nt_time_string(tctx, v.sname2.out.field2)); \
384                         ret = false; \
385                 } \
386         }} while (0)
387
388 #define CHECK_STR(name, sname1, field1, v, sname2, field2) do { \
389         s = find(name); \
390         if (s) { \
391                 if (!s->sname1.field1 || strcmp(s->sname1.field1, v.sname2.out.field2.s)) { \
392                         printf("(%s) %s/%s [%s] != %s/%s [%s]\n", \
393                                __location__, \
394                                 #sname1, #field1, s->sname1.field1, \
395                                 #sname2, #field2, v.sname2.out.field2.s); \
396                         ret = false; \
397                 } \
398         }} while (0)
399
400 #define CHECK_WSTR(name, sname1, field1, v, sname2, field2, flags) do { \
401         s = find(name); \
402         if (s) { \
403                 if (!s->sname1.field1.s || \
404                     strcmp(s->sname1.field1.s, v.sname2.out.field2.s) || \
405                     wire_bad_flags(&s->sname1.field1, flags, cli->transport)) { \
406                         printf("(%s) %s/%s [%s] != %s/%s [%s]\n", \
407                                __location__, \
408                                 #sname1, #field1, s->sname1.field1.s, \
409                                 #sname2, #field2, v.sname2.out.field2.s); \
410                         ret = false; \
411                 } \
412         }} while (0)
413
414 #define CHECK_NAME(name, sname1, field1, fname, flags) do { \
415         s = find(name); \
416         if (s) { \
417                 if (!s->sname1.field1.s || \
418                     strcmp(s->sname1.field1.s, fname) || \
419                     wire_bad_flags(&s->sname1.field1, flags, cli->transport)) { \
420                         printf("(%s) %s/%s [%s] != %s\n", \
421                                __location__, \
422                                 #sname1, #field1, s->sname1.field1.s, \
423                                 fname); \
424                         ret = false; \
425                 } \
426         }} while (0)
427
428 #define CHECK_UNIX_NAME(name, sname1, field1, fname, flags) do { \
429         s = find(name); \
430         if (s) { \
431                 if (!s->sname1.field1 || \
432                     strcmp(s->sname1.field1, fname)) { \
433                         printf("(%s) %s/%s [%s] != %s\n", \
434                                __location__, \
435                                 #sname1, #field1, s->sname1.field1, \
436                                 fname); \
437                         ret = false; \
438                 } \
439         }} while (0)
440         
441         /* check that all the results are as expected */
442         CHECK_VAL("SEARCH",              search,              attrib, all_info, all_info, attrib&0xFFF);
443         CHECK_VAL("STANDARD",            standard,            attrib, all_info, all_info, attrib&0xFFF);
444         CHECK_VAL("EA_SIZE",             ea_size,             attrib, all_info, all_info, attrib&0xFFF);
445         CHECK_VAL("DIRECTORY_INFO",      directory_info,      attrib, all_info, all_info, attrib);
446         CHECK_VAL("FULL_DIRECTORY_INFO", full_directory_info, attrib, all_info, all_info, attrib);
447         CHECK_VAL("BOTH_DIRECTORY_INFO", both_directory_info, attrib, all_info, all_info, attrib);
448         CHECK_VAL("ID_FULL_DIRECTORY_INFO", id_full_directory_info,           attrib, all_info, all_info, attrib);
449         CHECK_VAL("ID_BOTH_DIRECTORY_INFO", id_both_directory_info,           attrib, all_info, all_info, attrib);
450
451         CHECK_TIME("SEARCH",             search,              write_time, all_info, all_info, write_time);
452         CHECK_TIME("STANDARD",           standard,            write_time, all_info, all_info, write_time);
453         CHECK_TIME("EA_SIZE",            ea_size,             write_time, all_info, all_info, write_time);
454         CHECK_TIME("STANDARD",           standard,            create_time, all_info, all_info, create_time);
455         CHECK_TIME("EA_SIZE",            ea_size,             create_time, all_info, all_info, create_time);
456         CHECK_TIME("STANDARD",           standard,            access_time, all_info, all_info, access_time);
457         CHECK_TIME("EA_SIZE",            ea_size,             access_time, all_info, all_info, access_time);
458
459         CHECK_NTTIME("DIRECTORY_INFO",      directory_info,      write_time, all_info, all_info, write_time);
460         CHECK_NTTIME("FULL_DIRECTORY_INFO", full_directory_info, write_time, all_info, all_info, write_time);
461         CHECK_NTTIME("BOTH_DIRECTORY_INFO", both_directory_info, write_time, all_info, all_info, write_time);
462         CHECK_NTTIME("ID_FULL_DIRECTORY_INFO", id_full_directory_info,           write_time, all_info, all_info, write_time);
463         CHECK_NTTIME("ID_BOTH_DIRECTORY_INFO", id_both_directory_info,           write_time, all_info, all_info, write_time);
464
465         CHECK_NTTIME("DIRECTORY_INFO",      directory_info,      create_time, all_info, all_info, create_time);
466         CHECK_NTTIME("FULL_DIRECTORY_INFO", full_directory_info, create_time, all_info, all_info, create_time);
467         CHECK_NTTIME("BOTH_DIRECTORY_INFO", both_directory_info, create_time, all_info, all_info, create_time);
468         CHECK_NTTIME("ID_FULL_DIRECTORY_INFO", id_full_directory_info,           create_time, all_info, all_info, create_time);
469         CHECK_NTTIME("ID_BOTH_DIRECTORY_INFO", id_both_directory_info,           create_time, all_info, all_info, create_time);
470
471         CHECK_NTTIME("DIRECTORY_INFO",      directory_info,      access_time, all_info, all_info, access_time);
472         CHECK_NTTIME("FULL_DIRECTORY_INFO", full_directory_info, access_time, all_info, all_info, access_time);
473         CHECK_NTTIME("BOTH_DIRECTORY_INFO", both_directory_info, access_time, all_info, all_info, access_time);
474         CHECK_NTTIME("ID_FULL_DIRECTORY_INFO", id_full_directory_info,           access_time, all_info, all_info, access_time);
475         CHECK_NTTIME("ID_BOTH_DIRECTORY_INFO", id_both_directory_info,           access_time, all_info, all_info, access_time);
476
477         CHECK_NTTIME("DIRECTORY_INFO",      directory_info,      change_time, all_info, all_info, change_time);
478         CHECK_NTTIME("FULL_DIRECTORY_INFO", full_directory_info, change_time, all_info, all_info, change_time);
479         CHECK_NTTIME("BOTH_DIRECTORY_INFO", both_directory_info, change_time, all_info, all_info, change_time);
480         CHECK_NTTIME("ID_FULL_DIRECTORY_INFO", id_full_directory_info,           change_time, all_info, all_info, change_time);
481         CHECK_NTTIME("ID_BOTH_DIRECTORY_INFO", id_both_directory_info,           change_time, all_info, all_info, change_time);
482
483         CHECK_VAL("SEARCH",              search,              size, all_info, all_info, size);
484         CHECK_VAL("STANDARD",            standard,            size, all_info, all_info, size);
485         CHECK_VAL("EA_SIZE",             ea_size,             size, all_info, all_info, size);
486         CHECK_VAL("DIRECTORY_INFO",      directory_info,      size, all_info, all_info, size);
487         CHECK_VAL("FULL_DIRECTORY_INFO", full_directory_info, size, all_info, all_info, size);
488         CHECK_VAL("BOTH_DIRECTORY_INFO", both_directory_info, size, all_info, all_info, size);
489         CHECK_VAL("ID_FULL_DIRECTORY_INFO", id_full_directory_info,           size, all_info, all_info, size);
490         CHECK_VAL("ID_BOTH_DIRECTORY_INFO", id_both_directory_info,           size, all_info, all_info, size);
491         CHECK_VAL("UNIX_INFO",           unix_info,           size, all_info, all_info, size);
492
493         CHECK_VAL("STANDARD",            standard,            alloc_size, all_info, all_info, alloc_size);
494         CHECK_VAL("EA_SIZE",             ea_size,             alloc_size, all_info, all_info, alloc_size);
495         CHECK_VAL("DIRECTORY_INFO",      directory_info,      alloc_size, all_info, all_info, alloc_size);
496         CHECK_VAL("FULL_DIRECTORY_INFO", full_directory_info, alloc_size, all_info, all_info, alloc_size);
497         CHECK_VAL("BOTH_DIRECTORY_INFO", both_directory_info, alloc_size, all_info, all_info, alloc_size);
498         CHECK_VAL("ID_FULL_DIRECTORY_INFO", id_full_directory_info,           alloc_size, all_info, all_info, alloc_size);
499         CHECK_VAL("ID_BOTH_DIRECTORY_INFO", id_both_directory_info,           alloc_size, all_info, all_info, alloc_size);
500         CHECK_VAL("UNIX_INFO",           unix_info,           alloc_size, all_info, all_info, alloc_size);
501
502         CHECK_VAL("EA_SIZE",             ea_size,             ea_size, all_info, all_info, ea_size);
503         CHECK_VAL("FULL_DIRECTORY_INFO", full_directory_info, ea_size, all_info, all_info, ea_size);
504         CHECK_VAL("BOTH_DIRECTORY_INFO", both_directory_info, ea_size, all_info, all_info, ea_size);
505         CHECK_VAL("ID_FULL_DIRECTORY_INFO", id_full_directory_info,           ea_size, all_info, all_info, ea_size);
506         CHECK_VAL("ID_BOTH_DIRECTORY_INFO", id_both_directory_info,           ea_size, all_info, all_info, ea_size);
507
508         if (alt_info_supported) {
509                 CHECK_STR("SEARCH", search, name, alt_info, alt_name_info,
510                     fname);
511                 CHECK_WSTR("BOTH_DIRECTORY_INFO", both_directory_info,
512                     short_name, alt_info, alt_name_info, fname, STR_UNICODE);
513         }
514
515         CHECK_NAME("STANDARD",            standard,            name, fname+1, 0);
516         CHECK_NAME("EA_SIZE",             ea_size,             name, fname+1, 0);
517         CHECK_NAME("DIRECTORY_INFO",      directory_info,      name, fname+1, STR_TERMINATE_ASCII);
518         CHECK_NAME("FULL_DIRECTORY_INFO", full_directory_info, name, fname+1, STR_TERMINATE_ASCII);
519
520         if (name_info_supported) {
521                 CHECK_NAME("NAME_INFO", name_info, name, fname+1,
522                     STR_TERMINATE_ASCII);
523         }
524
525         CHECK_NAME("BOTH_DIRECTORY_INFO", both_directory_info, name, fname+1, STR_TERMINATE_ASCII);
526         CHECK_NAME("ID_FULL_DIRECTORY_INFO", id_full_directory_info,           name, fname+1, STR_TERMINATE_ASCII);
527         CHECK_NAME("ID_BOTH_DIRECTORY_INFO", id_both_directory_info,           name, fname+1, STR_TERMINATE_ASCII);
528         CHECK_UNIX_NAME("UNIX_INFO",           unix_info,           name, fname+1, STR_TERMINATE_ASCII);
529
530         if (internal_info_supported) {
531                 CHECK_VAL("ID_FULL_DIRECTORY_INFO", id_full_directory_info,
532                     file_id, internal_info, internal_information, file_id);
533                 CHECK_VAL("ID_BOTH_DIRECTORY_INFO", id_both_directory_info,
534                     file_id, internal_info, internal_information, file_id);
535         }
536
537 done:
538         smb_raw_exit(cli->session);
539         smbcli_unlink(cli->tree, fname);
540
541         return ret;
542 }
543
544
545 struct multiple_result {
546         TALLOC_CTX *tctx;
547         int count;
548         union smb_search_data *list;
549 };
550
551 /*
552   callback function for multiple_search
553 */
554 static bool multiple_search_callback(void *private_data, const union smb_search_data *file)
555 {
556         struct multiple_result *data = (struct multiple_result *)private_data;
557
558
559         data->count++;
560         data->list = talloc_realloc(data->tctx,
561                                       data->list, 
562                                       union smb_search_data,
563                                       data->count);
564
565         data->list[data->count-1] = *file;
566
567         return true;
568 }
569
570 enum continue_type {CONT_FLAGS, CONT_NAME, CONT_RESUME_KEY};
571
572 /*
573   do a single file (non-wildcard) search 
574 */
575 static NTSTATUS multiple_search(struct smbcli_state *cli, 
576                                 TALLOC_CTX *tctx,
577                                 const char *pattern,
578                                 enum smb_search_data_level data_level,
579                                 enum continue_type cont_type,
580                                 void *data)
581 {
582         union smb_search_first io;
583         union smb_search_next io2;
584         NTSTATUS status;
585         const int per_search = 100;
586         struct multiple_result *result = (struct multiple_result *)data;
587
588         if (data_level == RAW_SEARCH_DATA_SEARCH) {
589                 io.search_first.level = RAW_SEARCH_SEARCH;
590                 io.search_first.data_level = RAW_SEARCH_DATA_SEARCH;
591                 io.search_first.in.max_count = per_search;
592                 io.search_first.in.search_attrib = 0;
593                 io.search_first.in.pattern = pattern;
594         } else {
595                 io.t2ffirst.level = RAW_SEARCH_TRANS2;
596                 io.t2ffirst.data_level = data_level;
597                 io.t2ffirst.in.search_attrib = 0;
598                 io.t2ffirst.in.max_count = per_search;
599                 io.t2ffirst.in.flags = FLAG_TRANS2_FIND_CLOSE_IF_END;
600                 io.t2ffirst.in.storage_type = 0;
601                 io.t2ffirst.in.pattern = pattern;
602                 if (cont_type == CONT_RESUME_KEY) {
603                         io.t2ffirst.in.flags |= FLAG_TRANS2_FIND_REQUIRE_RESUME | 
604                                 FLAG_TRANS2_FIND_BACKUP_INTENT;
605                 }
606         }
607
608         status = smb_raw_search_first(cli->tree, tctx,
609                                       &io, data, multiple_search_callback);
610         
611
612         while (NT_STATUS_IS_OK(status)) {
613                 if (data_level == RAW_SEARCH_DATA_SEARCH) {
614                         io2.search_next.level = RAW_SEARCH_SEARCH;
615                         io2.search_next.data_level = RAW_SEARCH_DATA_SEARCH;
616                         io2.search_next.in.max_count = per_search;
617                         io2.search_next.in.search_attrib = 0;
618                         io2.search_next.in.id = result->list[result->count-1].search.id;
619                 } else {
620                         io2.t2fnext.level = RAW_SEARCH_TRANS2;
621                         io2.t2fnext.data_level = data_level;
622                         io2.t2fnext.in.handle = io.t2ffirst.out.handle;
623                         io2.t2fnext.in.max_count = per_search;
624                         io2.t2fnext.in.resume_key = 0;
625                         io2.t2fnext.in.flags = FLAG_TRANS2_FIND_CLOSE_IF_END;
626                         io2.t2fnext.in.last_name = "";
627                         switch (cont_type) {
628                         case CONT_RESUME_KEY:
629                                 io2.t2fnext.in.resume_key = extract_resume_key(&result->list[result->count-1],
630                                                                                io2.t2fnext.level, io2.t2fnext.data_level);
631                                 if (io2.t2fnext.in.resume_key == 0) {
632                                         printf("Server does not support resume by key for level %s\n",
633                                                level_name(io2.t2fnext.level, io2.t2fnext.data_level));
634                                         return NT_STATUS_NOT_SUPPORTED;
635                                 }
636                                 io2.t2fnext.in.flags |= FLAG_TRANS2_FIND_REQUIRE_RESUME |
637                                         FLAG_TRANS2_FIND_BACKUP_INTENT;
638                                 break;
639                         case CONT_NAME:
640                                 io2.t2fnext.in.last_name = extract_name(&result->list[result->count-1],
641                                                                         io2.t2fnext.level, io2.t2fnext.data_level);
642                                 break;
643                         case CONT_FLAGS:
644                                 io2.t2fnext.in.flags |= FLAG_TRANS2_FIND_CONTINUE;
645                                 break;
646                         }
647                 }
648
649                 status = smb_raw_search_next(cli->tree, tctx,
650                                              &io2, data, multiple_search_callback);
651                 if (!NT_STATUS_IS_OK(status)) {
652                         break;
653                 }
654                 if (data_level == RAW_SEARCH_DATA_SEARCH) {
655                         if (io2.search_next.out.count == 0) {
656                                 break;
657                         }
658                 } else if (io2.t2fnext.out.count == 0 ||
659                            io2.t2fnext.out.end_of_search) {
660                         break;
661                 }
662         }
663
664         return status;
665 }
666
667 #define CHECK_STATUS(status, correct) torture_assert_ntstatus_equal(tctx, status, correct, "incorrect status")
668
669 #define CHECK_VALUE(v, correct) torture_assert_int_equal(tctx, (v), (correct), "incorrect value");
670
671 #define CHECK_STRING(v, correct) torture_assert_casestr_equal(tctx, v, correct, "incorrect value");
672
673
674 static enum smb_search_data_level compare_data_level;
675
676 static int search_compare(union smb_search_data *d1, union smb_search_data *d2)
677 {
678         const char *s1, *s2;
679         enum smb_search_level level;
680
681         if (compare_data_level == RAW_SEARCH_DATA_SEARCH) {
682                 level = RAW_SEARCH_SEARCH;
683         } else {
684                 level = RAW_SEARCH_TRANS2;
685         }
686
687         s1 = extract_name(d1, level, compare_data_level);
688         s2 = extract_name(d2, level, compare_data_level);
689         return strcmp_safe(s1, s2);
690 }
691
692
693
694 /* 
695    basic testing of search calls using many files
696 */
697 static bool test_many_files(struct torture_context *tctx, 
698                             struct smbcli_state *cli)
699 {
700         const int num_files = 700;
701         int i, fnum, t;
702         char *fname;
703         bool ret = true;
704         NTSTATUS status;
705         struct multiple_result result;
706         struct {
707                 const char *name;
708                 const char *cont_name;
709                 enum smb_search_data_level data_level;
710                 enum continue_type cont_type;
711         } search_types[] = {
712                 {"SEARCH",              "ID",    RAW_SEARCH_DATA_SEARCH,              CONT_RESUME_KEY},
713                 {"BOTH_DIRECTORY_INFO", "NAME",  RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO, CONT_NAME},
714                 {"BOTH_DIRECTORY_INFO", "FLAGS", RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO, CONT_FLAGS},
715                 {"BOTH_DIRECTORY_INFO", "KEY",   RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO, CONT_RESUME_KEY},
716                 {"STANDARD",            "FLAGS", RAW_SEARCH_DATA_STANDARD,            CONT_FLAGS},
717                 {"STANDARD",            "KEY",   RAW_SEARCH_DATA_STANDARD,            CONT_RESUME_KEY},
718                 {"STANDARD",            "NAME",  RAW_SEARCH_DATA_STANDARD,            CONT_NAME},
719                 {"EA_SIZE",             "FLAGS", RAW_SEARCH_DATA_EA_SIZE,             CONT_FLAGS},
720                 {"EA_SIZE",             "KEY",   RAW_SEARCH_DATA_EA_SIZE,             CONT_RESUME_KEY},
721                 {"EA_SIZE",             "NAME",  RAW_SEARCH_DATA_EA_SIZE,             CONT_NAME},
722                 {"DIRECTORY_INFO",      "FLAGS", RAW_SEARCH_DATA_DIRECTORY_INFO,      CONT_FLAGS},
723                 {"DIRECTORY_INFO",      "KEY",   RAW_SEARCH_DATA_DIRECTORY_INFO,      CONT_RESUME_KEY},
724                 {"DIRECTORY_INFO",      "NAME",  RAW_SEARCH_DATA_DIRECTORY_INFO,      CONT_NAME},
725                 {"FULL_DIRECTORY_INFO",    "FLAGS", RAW_SEARCH_DATA_FULL_DIRECTORY_INFO,    CONT_FLAGS},
726                 {"FULL_DIRECTORY_INFO",    "KEY",   RAW_SEARCH_DATA_FULL_DIRECTORY_INFO,    CONT_RESUME_KEY},
727                 {"FULL_DIRECTORY_INFO",    "NAME",  RAW_SEARCH_DATA_FULL_DIRECTORY_INFO,    CONT_NAME},
728                 {"ID_FULL_DIRECTORY_INFO", "FLAGS", RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO, CONT_FLAGS},
729                 {"ID_FULL_DIRECTORY_INFO", "KEY",   RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO, CONT_RESUME_KEY},
730                 {"ID_FULL_DIRECTORY_INFO", "NAME",  RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO, CONT_NAME},
731                 {"ID_BOTH_DIRECTORY_INFO", "NAME",  RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO, CONT_NAME},
732                 {"ID_BOTH_DIRECTORY_INFO", "FLAGS", RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO, CONT_FLAGS},
733                 {"ID_BOTH_DIRECTORY_INFO", "KEY",   RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO, CONT_RESUME_KEY}
734         };
735
736         torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
737
738         torture_comment(tctx, "Testing with %d files\n", num_files);
739
740         for (i=0;i<num_files;i++) {
741                 fname = talloc_asprintf(cli, BASEDIR "\\t%03d-%d.txt", i, i);
742                 fnum = smbcli_open(cli->tree, fname, O_CREAT|O_RDWR, DENY_NONE);
743                 torture_assert(tctx, fnum != -1, "Failed to create");
744                 talloc_free(fname);
745                 smbcli_close(cli->tree, fnum);
746         }
747
748
749         for (t=0;t<ARRAY_SIZE(search_types);t++) {
750                 ZERO_STRUCT(result);
751
752                 if ((search_types[t].cont_type == CONT_RESUME_KEY) &&
753                     (search_types[t].data_level != RAW_SEARCH_DATA_SEARCH) &&
754                     !torture_setting_bool(tctx, "resume_key_support", true)) {
755                         torture_comment(tctx,
756                                         "SKIP: Continue %s via %s\n",
757                                         search_types[t].name, search_types[t].cont_name);
758                         continue;
759                 }
760
761                 result.tctx = talloc_new(tctx);
762         
763                 torture_comment(tctx,
764                                 "Continue %s via %s\n", search_types[t].name, search_types[t].cont_name);
765
766                 status = multiple_search(cli, tctx, BASEDIR "\\*.*", 
767                                          search_types[t].data_level,
768                                          search_types[t].cont_type,
769                                          &result);
770                 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
771                         torture_warning(tctx, "search level %s not supported "
772                                         "by server",
773                                         search_types[t].name);
774                         continue;
775                 }
776                 torture_assert_ntstatus_ok(tctx, status, "search failed");
777                 CHECK_VALUE(result.count, num_files);
778
779                 compare_data_level = search_types[t].data_level;
780
781                 TYPESAFE_QSORT(result.list, result.count, search_compare);
782
783                 for (i=0;i<result.count;i++) {
784                         const char *s;
785                         enum smb_search_level level;
786                         if (compare_data_level == RAW_SEARCH_DATA_SEARCH) {
787                                 level = RAW_SEARCH_SEARCH;
788                         } else {
789                                 level = RAW_SEARCH_TRANS2;
790                         }
791                         s = extract_name(&result.list[i], level, compare_data_level);
792                         fname = talloc_asprintf(cli, "t%03d-%d.txt", i, i);
793                         torture_assert_str_equal(tctx, fname, s, "Incorrect name");
794                         talloc_free(fname);
795                 }
796                 talloc_free(result.tctx);
797         }
798
799         smb_raw_exit(cli->session);
800         smbcli_deltree(cli->tree, BASEDIR);
801
802         return ret;
803 }
804
805 /*
806   check a individual file result
807 */
808 static bool check_result(struct multiple_result *result, const char *name, bool exist, uint32_t attrib)
809 {
810         int i;
811         for (i=0;i<result->count;i++) {
812                 if (strcmp(name, result->list[i].both_directory_info.name.s) == 0) break;
813         }
814         if (i == result->count) {
815                 if (exist) {
816                         printf("failed: '%s' should exist with attribute %s\n", 
817                                name, attrib_string(result->list, attrib));
818                         return false;
819                 }
820                 return true;
821         }
822
823         if (!exist) {
824                 printf("failed: '%s' should NOT exist (has attribute %s)\n", 
825                        name, attrib_string(result->list, result->list[i].both_directory_info.attrib));
826                 return false;
827         }
828
829         if ((result->list[i].both_directory_info.attrib&0xFFF) != attrib) {
830                 printf("failed: '%s' should have attribute 0x%x (has 0x%x)\n",
831                        name, 
832                        attrib, result->list[i].both_directory_info.attrib);
833                 return false;
834         }
835         return true;
836 }
837
838 /* 
839    test what happens when the directory is modified during a search
840 */
841 static bool test_modify_search(struct torture_context *tctx, 
842                                                            struct smbcli_state *cli)
843 {
844         const int num_files = 20;
845         int i, fnum;
846         char *fname;
847         bool ret = true;
848         NTSTATUS status;
849         struct multiple_result result;
850         union smb_search_first io;
851         union smb_search_next io2;
852         union smb_setfileinfo sfinfo;
853
854         torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
855
856         printf("Creating %d files\n", num_files);
857
858         for (i=num_files-1;i>=0;i--) {
859                 fname = talloc_asprintf(cli, BASEDIR "\\t%03d-%d.txt", i, i);
860                 fnum = smbcli_open(cli->tree, fname, O_CREAT|O_RDWR, DENY_NONE);
861                 if (fnum == -1) {
862                         printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree));
863                         ret = false;
864                         goto done;
865                 }
866                 talloc_free(fname);
867                 smbcli_close(cli->tree, fnum);
868         }
869
870         printf("pulling the first file\n");
871         ZERO_STRUCT(result);
872         result.tctx = talloc_new(tctx);
873
874         io.t2ffirst.level = RAW_SEARCH_TRANS2;
875         io.t2ffirst.data_level = RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO;
876         io.t2ffirst.in.search_attrib = 0;
877         io.t2ffirst.in.max_count = 0;
878         io.t2ffirst.in.flags = 0;
879         io.t2ffirst.in.storage_type = 0;
880         io.t2ffirst.in.pattern = BASEDIR "\\*.*";
881
882         status = smb_raw_search_first(cli->tree, tctx,
883                                       &io, &result, multiple_search_callback);
884         CHECK_STATUS(status, NT_STATUS_OK);
885         CHECK_VALUE(result.count, 1);
886         
887         printf("pulling the second file\n");
888         io2.t2fnext.level = RAW_SEARCH_TRANS2;
889         io2.t2fnext.data_level = RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO;
890         io2.t2fnext.in.handle = io.t2ffirst.out.handle;
891         io2.t2fnext.in.max_count = 1;
892         io2.t2fnext.in.resume_key = 0;
893         io2.t2fnext.in.flags = 0;
894         io2.t2fnext.in.last_name = result.list[result.count-1].both_directory_info.name.s;
895
896         status = smb_raw_search_next(cli->tree, tctx,
897                                      &io2, &result, multiple_search_callback);
898         CHECK_STATUS(status, NT_STATUS_OK);
899         CHECK_VALUE(result.count, 2);
900
901         result.count = 0;
902
903         printf("Changing attributes and deleting\n");
904         smbcli_open(cli->tree, BASEDIR "\\T003-03.txt.2", O_CREAT|O_RDWR, DENY_NONE);
905         smbcli_open(cli->tree, BASEDIR "\\T013-13.txt.2", O_CREAT|O_RDWR, DENY_NONE);
906         fnum = create_complex_file(cli, tctx, BASEDIR "\\T013-13.txt.3");
907         smbcli_unlink(cli->tree, BASEDIR "\\T014-14.txt");
908         torture_set_file_attribute(cli->tree, BASEDIR "\\T015-15.txt", FILE_ATTRIBUTE_HIDDEN);
909         torture_set_file_attribute(cli->tree, BASEDIR "\\T016-16.txt", FILE_ATTRIBUTE_NORMAL);
910         torture_set_file_attribute(cli->tree, BASEDIR "\\T017-17.txt", FILE_ATTRIBUTE_SYSTEM);  
911         torture_set_file_attribute(cli->tree, BASEDIR "\\T018-18.txt", 0);      
912         sfinfo.generic.level = RAW_SFILEINFO_DISPOSITION_INFORMATION;
913         sfinfo.generic.in.file.fnum = fnum;
914         sfinfo.disposition_info.in.delete_on_close = 1;
915         status = smb_raw_setfileinfo(cli->tree, &sfinfo);
916         CHECK_STATUS(status, NT_STATUS_OK);
917
918         io2.t2fnext.level = RAW_SEARCH_TRANS2;
919         io2.t2fnext.data_level = RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO;
920         io2.t2fnext.in.handle = io.t2ffirst.out.handle;
921         io2.t2fnext.in.max_count = num_files + 3;
922         io2.t2fnext.in.resume_key = 0;
923         io2.t2fnext.in.flags = 0;
924         io2.t2fnext.in.last_name = ".";
925
926         status = smb_raw_search_next(cli->tree, tctx,
927                                      &io2, &result, multiple_search_callback);
928         CHECK_STATUS(status, NT_STATUS_OK);
929         CHECK_VALUE(result.count, 20);
930
931         ret &= check_result(&result, "t009-9.txt", true, FILE_ATTRIBUTE_ARCHIVE);
932         ret &= check_result(&result, "t014-14.txt", false, 0);
933         ret &= check_result(&result, "t015-15.txt", false, 0);
934         ret &= check_result(&result, "t016-16.txt", true, FILE_ATTRIBUTE_NORMAL);
935         ret &= check_result(&result, "t017-17.txt", false, 0);
936         ret &= check_result(&result, "t018-18.txt", true, FILE_ATTRIBUTE_ARCHIVE);
937         ret &= check_result(&result, "t019-19.txt", true, FILE_ATTRIBUTE_ARCHIVE);
938         ret &= check_result(&result, "T013-13.txt.2", true, FILE_ATTRIBUTE_ARCHIVE);
939         ret &= check_result(&result, "T003-3.txt.2", false, 0);
940         ret &= check_result(&result, "T013-13.txt.3", true, FILE_ATTRIBUTE_ARCHIVE);
941
942         if (!ret) {
943                 for (i=0;i<result.count;i++) {
944                         printf("%s %s (0x%x)\n", 
945                                result.list[i].both_directory_info.name.s, 
946                                attrib_string(tctx, result.list[i].both_directory_info.attrib),
947                                result.list[i].both_directory_info.attrib);
948                 }
949         }
950
951 done:
952         smb_raw_exit(cli->session);
953         smbcli_deltree(cli->tree, BASEDIR);
954
955         return ret;
956 }
957
958
959 /* 
960    testing if directories always come back sorted
961 */
962 static bool test_sorted(struct torture_context *tctx, struct smbcli_state *cli)
963 {
964         const int num_files = 700;
965         int i, fnum;
966         char *fname;
967         bool ret = true;
968         NTSTATUS status;
969         struct multiple_result result;
970
971         torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
972
973         printf("Creating %d files\n", num_files);
974
975         for (i=0;i<num_files;i++) {
976                 fname = talloc_asprintf(cli, BASEDIR "\\%s.txt", generate_random_str_list(tctx, 10, "abcdefgh"));
977                 fnum = smbcli_open(cli->tree, fname, O_CREAT|O_RDWR, DENY_NONE);
978                 if (fnum == -1) {
979                         printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree));
980                         ret = false;
981                         goto done;
982                 }
983                 talloc_free(fname);
984                 smbcli_close(cli->tree, fnum);
985         }
986
987
988         ZERO_STRUCT(result);
989         result.tctx = tctx;
990         
991         status = multiple_search(cli, tctx, BASEDIR "\\*.*", 
992                                  RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO,
993                                  CONT_NAME, &result);   
994         CHECK_STATUS(status, NT_STATUS_OK);
995         CHECK_VALUE(result.count, num_files);
996
997         for (i=0;i<num_files-1;i++) {
998                 const char *name1, *name2;
999                 name1 = result.list[i].both_directory_info.name.s;
1000                 name2 = result.list[i+1].both_directory_info.name.s;
1001                 if (strcasecmp_m(name1, name2) > 0) {
1002                         printf("non-alphabetical order at entry %d  '%s' '%s'\n", 
1003                                i, name1, name2);
1004                         printf("Server does not produce sorted directory listings (not an error)\n");
1005                         goto done;
1006                 }
1007         }
1008
1009         talloc_free(result.list);
1010
1011 done:
1012         smb_raw_exit(cli->session);
1013         smbcli_deltree(cli->tree, BASEDIR);
1014
1015         return ret;
1016 }
1017
1018
1019
1020 /* 
1021    basic testing of many old style search calls using separate dirs
1022 */
1023 static bool test_many_dirs(struct torture_context *tctx, 
1024                                                    struct smbcli_state *cli)
1025 {
1026         const int num_dirs = 20;
1027         int i, fnum, n;
1028         char *fname, *dname;
1029         bool ret = true;
1030         NTSTATUS status;
1031         union smb_search_data *file, *file2, *file3;
1032
1033         if (!torture_setting_bool(tctx, "raw_search_search", true)) {
1034                 torture_comment(tctx, "Skipping these tests as the server "
1035                         "doesn't support old style search calls\n");
1036                 return true;
1037         }
1038         torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
1039
1040         printf("Creating %d dirs\n", num_dirs);
1041
1042         for (i=0;i<num_dirs;i++) {
1043                 dname = talloc_asprintf(cli, BASEDIR "\\d%d", i);
1044                 status = smbcli_mkdir(cli->tree, dname);
1045                 if (!NT_STATUS_IS_OK(status)) {
1046                         printf("(%s) Failed to create %s - %s\n", 
1047                                __location__, dname, nt_errstr(status));
1048                         ret = false;
1049                         goto done;
1050                 }
1051
1052                 for (n=0;n<3;n++) {
1053                         fname = talloc_asprintf(cli, BASEDIR "\\d%d\\f%d-%d.txt", i, i, n);
1054                         fnum = smbcli_open(cli->tree, fname, O_CREAT|O_RDWR, DENY_NONE);
1055                         if (fnum == -1) {
1056                                 printf("(%s) Failed to create %s - %s\n", 
1057                                        __location__, fname, smbcli_errstr(cli->tree));
1058                                 ret = false;
1059                                 goto done;
1060                         }
1061                         talloc_free(fname);
1062                         smbcli_close(cli->tree, fnum);
1063                 }
1064
1065                 talloc_free(dname);
1066         }
1067
1068         file  = talloc_zero_array(tctx, union smb_search_data, num_dirs);
1069         file2 = talloc_zero_array(tctx, union smb_search_data, num_dirs);
1070         file3 = talloc_zero_array(tctx, union smb_search_data, num_dirs);
1071
1072         printf("Search first on %d dirs\n", num_dirs);
1073
1074         for (i=0;i<num_dirs;i++) {
1075                 union smb_search_first io;
1076                 io.search_first.level = RAW_SEARCH_SEARCH;
1077                 io.search_first.data_level = RAW_SEARCH_DATA_SEARCH;
1078                 io.search_first.in.max_count = 1;
1079                 io.search_first.in.search_attrib = 0;
1080                 io.search_first.in.pattern = talloc_asprintf(tctx, BASEDIR "\\d%d\\*.txt", i);
1081                 fname = talloc_asprintf(tctx, "f%d-", i);
1082
1083                 io.search_first.out.count = 0;
1084
1085                 status = smb_raw_search_first(cli->tree, tctx,
1086                                               &io, (void *)&file[i], single_search_callback);
1087                 if (io.search_first.out.count != 1) {
1088                         printf("(%s) search first gave %d entries for dir %d - %s\n",
1089                                __location__, io.search_first.out.count, i, nt_errstr(status));
1090                         ret = false;
1091                         goto done;
1092                 }
1093                 CHECK_STATUS(status, NT_STATUS_OK);
1094                 if (strncasecmp(file[i].search.name, fname, strlen(fname)) != 0) {
1095                         printf("(%s) incorrect name '%s' expected '%s'[12].txt\n", 
1096                                __location__, file[i].search.name, fname);
1097                         ret = false;
1098                         goto done;
1099                 }
1100
1101                 talloc_free(fname);
1102         }
1103
1104         printf("Search next on %d dirs\n", num_dirs);
1105
1106         for (i=0;i<num_dirs;i++) {
1107                 union smb_search_next io2;
1108
1109                 io2.search_next.level = RAW_SEARCH_SEARCH;
1110                 io2.search_next.data_level = RAW_SEARCH_DATA_SEARCH;
1111                 io2.search_next.in.max_count = 1;
1112                 io2.search_next.in.search_attrib = 0;
1113                 io2.search_next.in.id = file[i].search.id;
1114                 fname = talloc_asprintf(tctx, "f%d-", i);
1115
1116                 io2.search_next.out.count = 0;
1117
1118                 status = smb_raw_search_next(cli->tree, tctx,
1119                                              &io2, (void *)&file2[i], single_search_callback);
1120                 if (io2.search_next.out.count != 1) {
1121                         printf("(%s) search next gave %d entries for dir %d - %s\n",
1122                                __location__, io2.search_next.out.count, i, nt_errstr(status));
1123                         ret = false;
1124                         goto done;
1125                 }
1126                 CHECK_STATUS(status, NT_STATUS_OK);
1127                 if (strncasecmp(file2[i].search.name, fname, strlen(fname)) != 0) {
1128                         printf("(%s) incorrect name '%s' expected '%s'[12].txt\n", 
1129                                __location__, file2[i].search.name, fname);
1130                         ret = false;
1131                         goto done;
1132                 }
1133
1134                 talloc_free(fname);
1135         }
1136
1137
1138         printf("Search next (rewind) on %d dirs\n", num_dirs);
1139
1140         for (i=0;i<num_dirs;i++) {
1141                 union smb_search_next io2;
1142
1143                 io2.search_next.level = RAW_SEARCH_SEARCH;
1144                 io2.search_next.data_level = RAW_SEARCH_DATA_SEARCH;
1145                 io2.search_next.in.max_count = 1;
1146                 io2.search_next.in.search_attrib = 0;
1147                 io2.search_next.in.id = file[i].search.id;
1148                 fname = talloc_asprintf(tctx, "f%d-", i);
1149                 io2.search_next.out.count = 0;
1150
1151                 status = smb_raw_search_next(cli->tree, tctx,
1152                                              &io2, (void *)&file3[i], single_search_callback);
1153                 if (io2.search_next.out.count != 1) {
1154                         printf("(%s) search next gave %d entries for dir %d - %s\n",
1155                                __location__, io2.search_next.out.count, i, nt_errstr(status));
1156                         ret = false;
1157                         goto done;
1158                 }
1159                 CHECK_STATUS(status, NT_STATUS_OK);
1160
1161                 if (strncasecmp(file3[i].search.name, file2[i].search.name, 3) != 0) {
1162                         printf("(%s) incorrect name '%s' on rewind at dir %d\n", 
1163                                __location__, file2[i].search.name, i);
1164                         ret = false;
1165                         goto done;
1166                 }
1167
1168                 if (torture_setting_bool(tctx, "rewind_support", true) &&
1169                     strcmp(file3[i].search.name, file2[i].search.name) != 0) {
1170                         printf("(%s) server did not rewind - got '%s' expected '%s'\n", 
1171                                __location__, file3[i].search.name, file2[i].search.name);
1172                         ret = false;
1173                         goto done;
1174                 }
1175
1176                 talloc_free(fname);
1177         }
1178
1179
1180 done:
1181         smb_raw_exit(cli->session);
1182         smbcli_deltree(cli->tree, BASEDIR);
1183
1184         return ret;
1185 }
1186
1187
1188 /* 
1189    testing of OS/2 style delete
1190 */
1191 static bool test_os2_delete(struct torture_context *tctx, 
1192                                                         struct smbcli_state *cli)
1193 {
1194         const int num_files = 700;
1195         const int delete_count = 4;
1196         int total_deleted = 0;
1197         int i, fnum;
1198         char *fname;
1199         bool ret = true;
1200         NTSTATUS status;
1201         union smb_search_first io;
1202         union smb_search_next io2;
1203         struct multiple_result result;
1204
1205         if (!torture_setting_bool(tctx, "search_ea_size", true)){
1206                 torture_comment(tctx,
1207                                 "Server does not support RAW_SEARCH_EA_SIZE "
1208                                 "level. Skipping this test\n");
1209                 return true;
1210         }
1211
1212         torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
1213
1214         printf("Testing OS/2 style delete on %d files\n", num_files);
1215
1216         for (i=0;i<num_files;i++) {
1217                 fname = talloc_asprintf(cli, BASEDIR "\\file%u.txt", i);
1218                 fnum = smbcli_open(cli->tree, fname, O_CREAT|O_RDWR, DENY_NONE);
1219                 if (fnum == -1) {
1220                         printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree));
1221                         ret = false;
1222                         goto done;
1223                 }
1224                 talloc_free(fname);
1225                 smbcli_close(cli->tree, fnum);
1226         }
1227
1228
1229         ZERO_STRUCT(result);
1230         result.tctx = tctx;
1231
1232         io.t2ffirst.level = RAW_SEARCH_TRANS2;
1233         io.t2ffirst.data_level = RAW_SEARCH_DATA_EA_SIZE;
1234         io.t2ffirst.in.search_attrib = 0;
1235         io.t2ffirst.in.max_count = 100;
1236         io.t2ffirst.in.flags = FLAG_TRANS2_FIND_REQUIRE_RESUME;
1237         io.t2ffirst.in.storage_type = 0;
1238         io.t2ffirst.in.pattern = BASEDIR "\\*";
1239
1240         status = smb_raw_search_first(cli->tree, tctx,
1241                                       &io, &result, multiple_search_callback);
1242         CHECK_STATUS(status, NT_STATUS_OK);
1243
1244         for (i=0;i<MIN(result.count, delete_count);i++) {
1245                 fname = talloc_asprintf(cli, BASEDIR "\\%s", result.list[i].ea_size.name.s);
1246                 status = smbcli_unlink(cli->tree, fname);
1247                 CHECK_STATUS(status, NT_STATUS_OK);
1248                 total_deleted++;
1249                 talloc_free(fname);
1250         }
1251
1252         io2.t2fnext.level = RAW_SEARCH_TRANS2;
1253         io2.t2fnext.data_level = RAW_SEARCH_DATA_EA_SIZE;
1254         io2.t2fnext.in.handle = io.t2ffirst.out.handle;
1255         io2.t2fnext.in.max_count = 100;
1256         io2.t2fnext.in.resume_key = result.list[i-1].ea_size.resume_key;
1257         io2.t2fnext.in.flags = FLAG_TRANS2_FIND_REQUIRE_RESUME;
1258         io2.t2fnext.in.last_name = result.list[i-1].ea_size.name.s;
1259
1260         do {
1261                 ZERO_STRUCT(result);
1262                 result.tctx = tctx;
1263
1264                 status = smb_raw_search_next(cli->tree, tctx,
1265                                              &io2, &result, multiple_search_callback);
1266                 if (!NT_STATUS_IS_OK(status)) {
1267                         break;
1268                 }
1269
1270                 for (i=0;i<MIN(result.count, delete_count);i++) {
1271                         fname = talloc_asprintf(cli, BASEDIR "\\%s", result.list[i].ea_size.name.s);
1272                         status = smbcli_unlink(cli->tree, fname);
1273                         CHECK_STATUS(status, NT_STATUS_OK);
1274                         total_deleted++;
1275                         talloc_free(fname);
1276                 }
1277
1278                 if (i>0) {
1279                         io2.t2fnext.in.resume_key = result.list[i-1].ea_size.resume_key;
1280                         io2.t2fnext.in.last_name = result.list[i-1].ea_size.name.s;
1281                 }
1282         } while (NT_STATUS_IS_OK(status) && result.count != 0);
1283
1284         CHECK_STATUS(status, NT_STATUS_OK);
1285
1286         if (total_deleted != num_files) {
1287                 printf("error: deleted %d - expected to delete %d\n", 
1288                        total_deleted, num_files);
1289                 ret = false;
1290         }
1291
1292 done:
1293         smb_raw_exit(cli->session);
1294         smbcli_deltree(cli->tree, BASEDIR);
1295
1296         return ret;
1297 }
1298
1299
1300 static int ealist_cmp(union smb_search_data *r1, union smb_search_data *r2)
1301 {
1302         return strcmp(r1->ea_list.name.s, r2->ea_list.name.s);
1303 }
1304
1305 /* 
1306    testing of the rather strange ea_list level
1307 */
1308 static bool test_ea_list(struct torture_context *tctx, 
1309                                                  struct smbcli_state *cli)
1310 {
1311         int  fnum;
1312         bool ret = true;
1313         NTSTATUS status;
1314         union smb_search_first io;
1315         union smb_search_next nxt;
1316         struct multiple_result result;
1317         union smb_setfileinfo setfile;
1318
1319         torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
1320
1321         printf("Testing RAW_SEARCH_EA_LIST level\n");
1322
1323         if (!torture_setting_bool(tctx, "search_ea_support", true) ||
1324             !torture_setting_bool(tctx, "ea_support", true)) {
1325                 printf("..skipped per target configuration.\n");
1326                 return true;
1327         }
1328
1329         fnum = smbcli_open(cli->tree, BASEDIR "\\file1.txt", O_CREAT|O_RDWR, DENY_NONE);
1330         smbcli_close(cli->tree, fnum);
1331
1332         fnum = smbcli_open(cli->tree, BASEDIR "\\file2.txt", O_CREAT|O_RDWR, DENY_NONE);
1333         smbcli_close(cli->tree, fnum);
1334
1335         fnum = smbcli_open(cli->tree, BASEDIR "\\file3.txt", O_CREAT|O_RDWR, DENY_NONE);
1336         smbcli_close(cli->tree, fnum);
1337
1338         setfile.generic.level = RAW_SFILEINFO_EA_SET;
1339         setfile.generic.in.file.path = BASEDIR "\\file2.txt";
1340         setfile.ea_set.in.num_eas = 2;
1341         setfile.ea_set.in.eas = talloc_array(tctx, struct ea_struct, 2);
1342         setfile.ea_set.in.eas[0].flags = 0;
1343         setfile.ea_set.in.eas[0].name.s = "EA ONE";
1344         setfile.ea_set.in.eas[0].value = data_blob_string_const("VALUE 1");
1345         setfile.ea_set.in.eas[1].flags = 0;
1346         setfile.ea_set.in.eas[1].name.s = "SECOND EA";
1347         setfile.ea_set.in.eas[1].value = data_blob_string_const("Value Two");
1348
1349         status = smb_raw_setpathinfo(cli->tree, &setfile);
1350         CHECK_STATUS(status, NT_STATUS_OK);
1351
1352         setfile.generic.in.file.path = BASEDIR "\\file3.txt";
1353         status = smb_raw_setpathinfo(cli->tree, &setfile);
1354         CHECK_STATUS(status, NT_STATUS_OK);
1355         
1356         ZERO_STRUCT(result);
1357         result.tctx = tctx;
1358
1359         io.t2ffirst.level = RAW_SEARCH_TRANS2;
1360         io.t2ffirst.data_level = RAW_SEARCH_DATA_EA_LIST;
1361         io.t2ffirst.in.search_attrib = 0;
1362         io.t2ffirst.in.max_count = 2;
1363         io.t2ffirst.in.flags = FLAG_TRANS2_FIND_REQUIRE_RESUME;
1364         io.t2ffirst.in.storage_type = 0;
1365         io.t2ffirst.in.pattern = BASEDIR "\\*";
1366         io.t2ffirst.in.num_names = 2;
1367         io.t2ffirst.in.ea_names = talloc_array(tctx, struct ea_name, 2);
1368         io.t2ffirst.in.ea_names[0].name.s = "SECOND EA";
1369         io.t2ffirst.in.ea_names[1].name.s = "THIRD EA";
1370
1371         status = smb_raw_search_first(cli->tree, tctx,
1372                                       &io, &result, multiple_search_callback);
1373         CHECK_STATUS(status, NT_STATUS_OK);
1374         CHECK_VALUE(result.count, 2);
1375
1376         nxt.t2fnext.level = RAW_SEARCH_TRANS2;
1377         nxt.t2fnext.data_level = RAW_SEARCH_DATA_EA_LIST;
1378         nxt.t2fnext.in.handle = io.t2ffirst.out.handle;
1379         nxt.t2fnext.in.max_count = 2;
1380         nxt.t2fnext.in.resume_key = result.list[1].ea_list.resume_key;
1381         nxt.t2fnext.in.flags = FLAG_TRANS2_FIND_REQUIRE_RESUME | FLAG_TRANS2_FIND_CONTINUE;
1382         nxt.t2fnext.in.last_name = result.list[1].ea_list.name.s;
1383         nxt.t2fnext.in.num_names = 2;
1384         nxt.t2fnext.in.ea_names = talloc_array(tctx, struct ea_name, 2);
1385         nxt.t2fnext.in.ea_names[0].name.s = "SECOND EA";
1386         nxt.t2fnext.in.ea_names[1].name.s = "THIRD EA";
1387
1388         status = smb_raw_search_next(cli->tree, tctx,
1389                                      &nxt, &result, multiple_search_callback);
1390         CHECK_STATUS(status, NT_STATUS_OK);
1391
1392         /* we have to sort the result as different servers can return directories
1393            in different orders */
1394         TYPESAFE_QSORT(result.list, result.count, ealist_cmp);
1395
1396         CHECK_VALUE(result.count, 3);
1397         CHECK_VALUE(result.list[0].ea_list.eas.num_eas, 2);
1398         CHECK_STRING(result.list[0].ea_list.name.s, "file1.txt");
1399         CHECK_STRING(result.list[0].ea_list.eas.eas[0].name.s, "SECOND EA");
1400         CHECK_VALUE(result.list[0].ea_list.eas.eas[0].value.length, 0);
1401         CHECK_STRING(result.list[0].ea_list.eas.eas[1].name.s, "THIRD EA");
1402         CHECK_VALUE(result.list[0].ea_list.eas.eas[1].value.length, 0);
1403
1404         CHECK_STRING(result.list[1].ea_list.name.s, "file2.txt");
1405         CHECK_STRING(result.list[1].ea_list.eas.eas[0].name.s, "SECOND EA");
1406         CHECK_VALUE(result.list[1].ea_list.eas.eas[0].value.length, 9);
1407         CHECK_STRING((const char *)result.list[1].ea_list.eas.eas[0].value.data, "Value Two");
1408         CHECK_STRING(result.list[1].ea_list.eas.eas[1].name.s, "THIRD EA");
1409         CHECK_VALUE(result.list[1].ea_list.eas.eas[1].value.length, 0);
1410
1411         CHECK_STRING(result.list[2].ea_list.name.s, "file3.txt");
1412         CHECK_STRING(result.list[2].ea_list.eas.eas[0].name.s, "SECOND EA");
1413         CHECK_VALUE(result.list[2].ea_list.eas.eas[0].value.length, 9);
1414         CHECK_STRING((const char *)result.list[2].ea_list.eas.eas[0].value.data, "Value Two");
1415         CHECK_STRING(result.list[2].ea_list.eas.eas[1].name.s, "THIRD EA");
1416         CHECK_VALUE(result.list[2].ea_list.eas.eas[1].value.length, 0);
1417
1418         smb_raw_exit(cli->session);
1419         smbcli_deltree(cli->tree, BASEDIR);
1420
1421         return ret;
1422 }
1423
1424 /*
1425  Test the behavior of max count parameter in TRANS2_FIND_FIRST2 and
1426  TRANS2_FIND_NEXT2 queries
1427 */
1428 static bool test_max_count(struct torture_context *tctx,
1429                            struct smbcli_state *cli)
1430 {
1431         const int num_files = 2;
1432         int i, fnum;
1433         char *fname;
1434         bool ret = true;
1435         NTSTATUS status;
1436         struct multiple_result result;
1437         union smb_search_first io;
1438         union smb_search_next io2;
1439
1440         torture_assert(tctx, torture_setup_dir(cli, BASEDIR), "Failed to setup up test directory: " BASEDIR);
1441
1442         torture_comment(tctx, "Creating %d files\n", num_files);
1443
1444         for (i=num_files-1;i>=0;i--) {
1445                 fname = talloc_asprintf(cli, BASEDIR "\\t%03d-%d.txt", i, i);
1446                 fnum = smbcli_open(cli->tree, fname, O_CREAT|O_RDWR, DENY_NONE);
1447                 if (fnum == -1) {
1448                         torture_comment(tctx,
1449                                 "Failed to create %s - %s\n",
1450                                 fname, smbcli_errstr(cli->tree));
1451                         ret = false;
1452                         goto done;
1453                 }
1454                 talloc_free(fname);
1455                 smbcli_close(cli->tree, fnum);
1456         }
1457
1458         torture_comment(tctx, "Set max_count parameter to 0. "
1459                         "This should return 1 entry\n");
1460         ZERO_STRUCT(result);
1461         result.tctx = talloc_new(tctx);
1462
1463         io.t2ffirst.level = RAW_SEARCH_TRANS2;
1464         io.t2ffirst.data_level = RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO;
1465         io.t2ffirst.in.search_attrib = 0;
1466         io.t2ffirst.in.max_count = 0;
1467         io.t2ffirst.in.flags = 0;
1468         io.t2ffirst.in.storage_type = 0;
1469         io.t2ffirst.in.pattern = BASEDIR "\\*.*";
1470
1471         status = smb_raw_search_first(cli->tree, tctx,
1472                                       &io, &result, multiple_search_callback);
1473         CHECK_STATUS(status, NT_STATUS_OK);
1474         CHECK_VALUE(result.count, 1);
1475
1476         torture_comment(tctx, "Set max_count to 1. This should also "
1477                         "return 1 entry\n");
1478         io2.t2fnext.level = RAW_SEARCH_TRANS2;
1479         io2.t2fnext.data_level = RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO;
1480         io2.t2fnext.in.handle = io.t2ffirst.out.handle;
1481         io2.t2fnext.in.max_count = 1;
1482         io2.t2fnext.in.resume_key = 0;
1483         io2.t2fnext.in.flags = 0;
1484         io2.t2fnext.in.last_name =
1485                 result.list[result.count-1].both_directory_info.name.s;
1486
1487         status = smb_raw_search_next(cli->tree, tctx,
1488                                      &io2, &result, multiple_search_callback);
1489         CHECK_STATUS(status, NT_STATUS_OK);
1490         CHECK_VALUE(result.count, 2);
1491 done:
1492         smb_raw_exit(cli->session);
1493         smbcli_deltree(cli->tree, BASEDIR);
1494
1495         return ret;
1496 }
1497
1498 /* 
1499    basic testing of all RAW_SEARCH_* calls using a single file
1500 */
1501 struct torture_suite *torture_raw_search(TALLOC_CTX *mem_ctx)
1502 {
1503         struct torture_suite *suite = torture_suite_create(mem_ctx, "search");
1504
1505         torture_suite_add_1smb_test(suite, "one file search", test_one_file);
1506         torture_suite_add_1smb_test(suite, "many files", test_many_files);
1507         torture_suite_add_1smb_test(suite, "sorted", test_sorted);
1508         torture_suite_add_1smb_test(suite, "modify search", test_modify_search);
1509         torture_suite_add_1smb_test(suite, "many dirs", test_many_dirs);
1510         torture_suite_add_1smb_test(suite, "os2 delete", test_os2_delete);
1511         torture_suite_add_1smb_test(suite, "ea list", test_ea_list);
1512         torture_suite_add_1smb_test(suite, "max count", test_max_count);
1513
1514         return suite;
1515 }