torture/smb2: Adding SMB2 Directory enumeration torture tests.
[metze/samba/wip.git] / source4 / torture / smb2 / dir.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    SMB2 dir list test suite
5
6    Copyright (C) Andrew Tridgell 2005
7    Copyright (C) Zachary Loafman 2009
8    Copyright (C) Aravind Srinivasan 2009
9
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 3 of the License, or
13    (at your option) any later version.
14
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19
20    You should have received a copy of the GNU General Public License
21    along with this program.  If not, see <http://www.gnu.org/licenses/>.
22 */
23
24 #include "includes.h"
25 #include "libcli/smb2/smb2.h"
26 #include "libcli/smb2/smb2_calls.h"
27 #include "libcli/smb_composite/smb_composite.h"
28 #include "libcli/raw/libcliraw.h"
29 #include "libcli/raw/raw_proto.h"
30 #include "libcli/libcli.h"
31
32 #include "torture/torture.h"
33 #include "torture/smb2/proto.h"
34 #include "torture/util.h"
35
36 #include "system/filesys.h"
37
38 #define CHECK_STATUS(status, correct) do { \
39         if (!NT_STATUS_EQUAL(status, correct)) { \
40                 torture_result(tctx, TORTURE_FAIL, __location__": \
41                        Incorrect status %s - should be %s", \
42                        nt_errstr(status), nt_errstr(correct)); \
43                 ret = false; \
44                 goto done; \
45         }} while (0)
46
47 #define CHECK_VALUE(v, correct) torture_assert_int_equal(tctx, (v), \
48                                 (correct), "incorrect value");
49
50 #define DNAME   "smb2_dir"
51 #define NFILES  100
52
53 struct file_elem {
54         char *name;
55         bool found;
56 };
57
58 static NTSTATUS populate_tree(struct torture_context *tctx,
59                               TALLOC_CTX *mem_ctx,
60                               struct smb2_tree *tree,
61                               struct file_elem *files,
62                               int nfiles,
63                               struct smb2_handle *h_out)
64 {
65         struct smb2_create create;
66         NTSTATUS status;
67         bool ret;
68         int i;
69
70         smb2_deltree(tree, DNAME);
71
72         ZERO_STRUCT(create);
73         create.in.desired_access = SEC_RIGHTS_DIR_ALL;
74         create.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
75         create.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
76         create.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
77                                  NTCREATEX_SHARE_ACCESS_WRITE |
78                                  NTCREATEX_SHARE_ACCESS_DELETE;
79         create.in.create_disposition = NTCREATEX_DISP_CREATE;
80         create.in.fname = DNAME;
81
82         status = smb2_create(tree, mem_ctx, &create);
83         CHECK_STATUS(status, NT_STATUS_OK);
84         *h_out = create.out.file.handle;
85
86         ZERO_STRUCT(create);
87         create.in.desired_access = SEC_RIGHTS_FILE_ALL;
88         create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
89         create.in.create_disposition = NTCREATEX_DISP_CREATE;
90
91         for (i = 0; i < nfiles; i++) {
92                 files[i].name = generate_random_str(tctx, 8);
93                 create.in.fname = talloc_asprintf(mem_ctx, "%s\\%s",
94                     DNAME, files[i].name);
95                 status = smb2_create(tree, mem_ctx, &create);
96                 CHECK_STATUS(status, NT_STATUS_OK);
97                 smb2_util_close(tree, create.out.file.handle);
98         }
99  done:
100         return status;
101 }
102
103 /*
104   test find continue
105 */
106
107 static bool test_find(struct torture_context *tctx,
108                       struct smb2_tree *tree)
109 {
110         TALLOC_CTX *mem_ctx = talloc_new(tctx);
111         struct smb2_handle h;
112         struct smb2_find f;
113         union smb_search_data *d;
114         struct file_elem files[NFILES] = {};
115         NTSTATUS status;
116         bool ret = true;
117         uint_t count;
118         int i, j, file_count = 0;
119
120         status = populate_tree(tctx, mem_ctx, tree, files, NFILES, &h);
121
122         ZERO_STRUCT(f);
123         f.in.file.handle        = h;
124         f.in.pattern            = "*";
125         f.in.continue_flags     = SMB2_CONTINUE_FLAG_SINGLE;
126         f.in.max_response_size  = 0x100;
127         f.in.level              = SMB2_FIND_BOTH_DIRECTORY_INFO;
128
129         do {
130                 status = smb2_find_level(tree, tree, &f, &count, &d);
131                 if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES))
132                         break;
133                 CHECK_STATUS(status, NT_STATUS_OK);
134
135                 for (i = 0; i < count; i++) {
136                         bool expected;
137                         const char *found = d[i].both_directory_info.name.s;
138
139                         if (!strcmp(found, ".") || !strcmp(found, ".."))
140                                 continue;
141
142                         expected = false;
143                         for (j = 0; j < NFILES; j++) {
144                                 if (!strcmp(files[j].name, found)) {
145                                         files[j].found = true;
146                                         expected = true;
147                                         break;
148                                 }
149                         }
150
151                         if (expected)
152                                 continue;
153
154                         torture_result(tctx, TORTURE_FAIL,
155                             "(%s): didn't expect %s\n",
156                             __location__, found);
157                         ret = false;
158                         goto done;
159                 }
160
161                 file_count = file_count + i;
162                 f.in.continue_flags = 0;
163                 f.in.max_response_size  = 4096;
164         } while (count != 0);
165
166         CHECK_VALUE(file_count, NFILES + 2);
167
168         for (i = 0; i < NFILES; i++) {
169                 if (files[j].found)
170                         continue;
171
172                 torture_result(tctx, TORTURE_FAIL,
173                     "(%s): expected to find %s, but didn't\n",
174                     __location__, files[j].name);
175                 ret = false;
176                 goto done;
177         }
178
179  done:
180         smb2_deltree(tree, DNAME);
181         talloc_free(mem_ctx);
182
183         return ret;
184 }
185
186 /*
187   test fixed enumeration
188 */
189
190 static bool test_fixed(struct torture_context *tctx,
191                        struct smb2_tree *tree)
192 {
193         TALLOC_CTX *mem_ctx = talloc_new(tctx);
194         struct smb2_create create;
195         struct smb2_handle h, h2;
196         struct smb2_find f;
197         union smb_search_data *d;
198         struct file_elem files[NFILES] = {};
199         NTSTATUS status;
200         bool ret = true;
201         uint_t count;
202         int i;
203
204         status = populate_tree(tctx, mem_ctx, tree, files, NFILES, &h);
205
206         ZERO_STRUCT(create);
207         create.in.desired_access = SEC_RIGHTS_DIR_ALL;
208         create.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
209         create.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
210         create.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
211                                  NTCREATEX_SHARE_ACCESS_WRITE |
212                                  NTCREATEX_SHARE_ACCESS_DELETE;
213         create.in.create_disposition = NTCREATEX_DISP_OPEN;
214         create.in.fname = DNAME;
215
216         status = smb2_create(tree, mem_ctx, &create);
217         CHECK_STATUS(status, NT_STATUS_OK);
218         h2 = create.out.file.handle;
219
220         ZERO_STRUCT(f);
221         f.in.file.handle        = h;
222         f.in.pattern            = "*";
223         f.in.continue_flags     = SMB2_CONTINUE_FLAG_SINGLE;
224         f.in.max_response_size  = 0x100;
225         f.in.level              = SMB2_FIND_BOTH_DIRECTORY_INFO;
226
227         /* Start enumeration on h, then delete all from h2 */
228         status = smb2_find_level(tree, tree, &f, &count, &d);
229         CHECK_STATUS(status, NT_STATUS_OK);
230
231         f.in.file.handle        = h2;
232
233         do {
234                 status = smb2_find_level(tree, tree, &f, &count, &d);
235                 if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES))
236                         break;
237                 CHECK_STATUS(status, NT_STATUS_OK);
238
239                 for (i = 0; i < count; i++) {
240                         const char *found = d[i].both_directory_info.name.s;
241                         char *path = talloc_asprintf(mem_ctx, "%s\\%s",
242                             DNAME, found);
243
244                         if (!strcmp(found, ".") || !strcmp(found, ".."))
245                                 continue;
246
247                         status = smb2_util_unlink(tree, path);
248                         CHECK_STATUS(status, NT_STATUS_OK);
249
250                         talloc_free(path);
251                 }
252
253                 f.in.continue_flags = 0;
254                 f.in.max_response_size  = 4096;
255         } while (count != 0);
256
257         /* Now finish h enumeration. */
258         f.in.file.handle = h;
259
260         do {
261                 status = smb2_find_level(tree, tree, &f, &count, &d);
262                 if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES))
263                         break;
264                 CHECK_STATUS(status, NT_STATUS_OK);
265
266                 for (i = 0; i < count; i++) {
267                         const char *found = d[i].both_directory_info.name.s;
268
269                         if (!strcmp(found, ".") || !strcmp(found, ".."))
270                                 continue;
271
272                         torture_result(tctx, TORTURE_FAIL,
273                             "(%s): didn't expect %s\n",
274                             __location__, found);
275                         ret = false;
276                         goto done;
277                 }
278
279                 f.in.continue_flags = 0;
280                 f.in.max_response_size  = 4096;
281         } while (count != 0);
282
283  done:
284         smb2_util_close(tree, h);
285         smb2_util_close(tree, h2);
286         smb2_deltree(tree, DNAME);
287         talloc_free(mem_ctx);
288
289         return ret;
290 }
291
292 static struct {
293         const char *name;
294         uint8_t level;
295         enum smb_search_data_level data_level;
296         int name_offset;
297         int resume_key_offset;
298         uint32_t capability_mask;
299         NTSTATUS status;
300         union smb_search_data data;
301 } levels[] = {
302         {"SMB2_FIND_DIRECTORY_INFO",
303          SMB2_FIND_DIRECTORY_INFO, RAW_SEARCH_DATA_DIRECTORY_INFO,
304          offsetof(union smb_search_data, directory_info.name.s),
305          offsetof(union smb_search_data, directory_info.file_index),
306         },
307         {"SMB2_FIND_FULL_DIRECTORY_INFO",
308          SMB2_FIND_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_FULL_DIRECTORY_INFO,
309          offsetof(union smb_search_data, full_directory_info.name.s),
310          offsetof(union smb_search_data, full_directory_info.file_index),
311         },
312         {"SMB2_FIND_NAME_INFO",
313          SMB2_FIND_NAME_INFO, RAW_SEARCH_DATA_NAME_INFO,
314          offsetof(union smb_search_data, name_info.name.s),
315          offsetof(union smb_search_data, name_info.file_index),
316         },
317         {"SMB2_FIND_BOTH_DIRECTORY_INFO",
318          SMB2_FIND_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO,
319          offsetof(union smb_search_data, both_directory_info.name.s),
320          offsetof(union smb_search_data, both_directory_info.file_index),
321         },
322         {"SMB2_FIND_ID_FULL_DIRECTORY_INFO",
323          SMB2_FIND_ID_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO,
324          offsetof(union smb_search_data, id_full_directory_info.name.s),
325          offsetof(union smb_search_data, id_full_directory_info.file_index),
326         },
327         {"SMB2_FIND_ID_BOTH_DIRECTORY_INFO",
328          SMB2_FIND_ID_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO,
329          offsetof(union smb_search_data, id_both_directory_info.name.s),
330          offsetof(union smb_search_data, id_both_directory_info.file_index),
331         }
332 };
333
334 /*
335   extract the name from a smb_data structure and level
336 */
337 static const char *extract_name(union smb_search_data *data,
338                                 uint8_t level,
339                                 enum smb_search_data_level data_level)
340 {
341         int i;
342         for (i=0;i<ARRAY_SIZE(levels);i++) {
343                 if (level == levels[i].level &&
344                     data_level == levels[i].data_level) {
345                         return *(const char **)(levels[i].name_offset + (char *)data);
346                 }
347         }
348         return NULL;
349 }
350
351 /* find a level in the table by name */
352 static union smb_search_data *find(const char *name)
353 {
354         int i;
355         for (i=0;i<ARRAY_SIZE(levels);i++) {
356                 if (NT_STATUS_IS_OK(levels[i].status) &&
357                     strcmp(levels[i].name, name) == 0) {
358                         return &levels[i].data;
359                 }
360         }
361         return NULL;
362 }
363
364 static bool fill_level_data(TALLOC_CTX *mem_ctx,
365                             union smb_search_data *data,
366                             union smb_search_data *d,
367                             uint_t count,
368                             uint8_t level,
369                             enum smb_search_data_level data_level)
370 {
371         int i;
372         const char *sname = NULL;
373         for (i=0; i < count ; i++) {
374                 sname = extract_name(&d[i], level, data_level);
375                 if (sname == NULL)
376                         return false;
377                 if (!strcmp(sname, ".") || !strcmp(sname, ".."))
378                         continue;
379                 *data = d[i];
380         }
381         return true;
382 }
383
384
385 NTSTATUS torture_single_file_search(struct smb2_tree *tree,
386                                     TALLOC_CTX *mem_ctx,
387                                     const char *pattern,
388                                     uint8_t level,
389                                     enum smb_search_data_level data_level,
390                                     int index,
391                                     union smb_search_data *d,
392                                     uint_t *count,
393                                     struct smb2_handle *h)
394 {
395         struct smb2_find f;
396         NTSTATUS status;
397
398         ZERO_STRUCT(f);
399         f.in.file.handle        = *h;
400         f.in.pattern            = pattern;
401         f.in.continue_flags     = SMB2_CONTINUE_FLAG_RESTART;
402         f.in.max_response_size  = 0x100;
403         f.in.level              = level;
404
405         status = smb2_find_level(tree, tree, &f, count, &d);
406         if (NT_STATUS_IS_OK(status))
407                 fill_level_data(mem_ctx, &levels[index].data, d, *count, level,
408                                 data_level);
409         return status;
410 }
411
412 /*
413    basic testing of all File Information Classes using a single file
414 */
415 static bool test_one_file(struct torture_context *tctx,
416                           struct smb2_tree *tree)
417 {
418         TALLOC_CTX *mem_ctx = talloc_new(tctx);
419         bool ret = true;
420         const char *fname =  "torture_search.txt";
421         NTSTATUS status;
422         int i;
423         uint_t count;
424         union smb_fileinfo all_info2, alt_info, internal_info;
425         union smb_search_data *s;
426         union smb_search_data d;
427         struct smb2_handle h, h2;
428
429         status = torture_smb2_testdir(tree, DNAME, &h);
430         CHECK_STATUS(status, NT_STATUS_OK);
431
432         status = smb2_create_complex_file(tree, DNAME "\\torture_search.txt",
433                                           &h2);
434         CHECK_STATUS(status, NT_STATUS_OK);
435
436         /* call all the File Information Classes */
437         for (i=0;i<ARRAY_SIZE(levels);i++) {
438                 torture_comment(tctx, "testing %s %d\n", levels[i].name,
439                                 levels[i].level);
440
441                 levels[i].status = torture_single_file_search(tree, mem_ctx,
442                                    fname, levels[i].level, levels[i].data_level,
443                                    i, &d, &count, &h);
444                 CHECK_STATUS(levels[i].status, NT_STATUS_OK);
445         }
446
447         /* get the all_info file into to check against */
448         all_info2.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
449         all_info2.generic.in.file.handle = h2;
450         status = smb2_getinfo_file(tree, tctx, &all_info2);
451         torture_assert_ntstatus_ok(tctx, status,
452                                    "RAW_FILEINFO_ALL_INFO failed");
453
454         alt_info.generic.level = RAW_FILEINFO_ALT_NAME_INFORMATION;
455         alt_info.generic.in.file.handle = h2;
456         status = smb2_getinfo_file(tree, tctx, &alt_info);
457         torture_assert_ntstatus_ok(tctx, status,
458                                    "RAW_FILEINFO_ALT_NAME_INFO failed");
459
460         internal_info.generic.level = RAW_FILEINFO_INTERNAL_INFORMATION;
461         internal_info.generic.in.file.handle = h2;
462         status = smb2_getinfo_file(tree, tctx, &internal_info);
463         torture_assert_ntstatus_ok(tctx, status,
464                                    "RAW_FILEINFO_INTERNAL_INFORMATION failed");
465
466 #define CHECK_VAL(name, sname1, field1, v, sname2, field2) do { \
467         s = find(name); \
468         if (s) { \
469                 if ((s->sname1.field1) != (v.sname2.out.field2)) { \
470                         printf("(%s) %s/%s [0x%x] != %s/%s [0x%x]\n", \
471                                 __location__, \
472                                 #sname1, #field1, (int)s->sname1.field1, \
473                                 #sname2, #field2, (int)v.sname2.out.field2); \
474                         ret = false; \
475                 } \
476         }} while (0)
477
478 #define CHECK_TIME(name, sname1, field1, v, sname2, field2) do { \
479         s = find(name); \
480         if (s) { \
481                 if (s->sname1.field1 != \
482                     (~1 & nt_time_to_unix(v.sname2.out.field2))) { \
483                         printf("(%s) %s/%s [%s] != %s/%s [%s]\n", \
484                                 __location__, \
485                                 #sname1, #field1, \
486                                 timestring(tctx, s->sname1.field1), \
487                                 #sname2, #field2, \
488                                 nt_time_string(tctx, v.sname2.out.field2)); \
489                         ret = false; \
490                 } \
491         }} while (0)
492
493 #define CHECK_NTTIME(name, sname1, field1, v, sname2, field2) do { \
494         s = find(name); \
495         if (s) { \
496                 if (s->sname1.field1 != v.sname2.out.field2) { \
497                         printf("(%s) %s/%s [%s] != %s/%s [%s]\n", \
498                                 __location__, \
499                                 #sname1, #field1, \
500                                 nt_time_string(tctx, s->sname1.field1), \
501                                 #sname2, #field2, \
502                                 nt_time_string(tctx, v.sname2.out.field2)); \
503                         ret = false; \
504                 } \
505         }} while (0)
506
507 #define CHECK_STR(name, sname1, field1, v, sname2, field2) do { \
508         s = find(name); \
509         if (s) { \
510                 if (!s->sname1.field1 || \
511                     strcmp(s->sname1.field1, v.sname2.out.field2.s)) { \
512                         printf("(%s) %s/%s [%s] != %s/%s [%s]\n", \
513                                 __location__, \
514                                 #sname1, #field1, s->sname1.field1, \
515                                 #sname2, #field2, v.sname2.out.field2.s); \
516                         ret = false; \
517                 } \
518         }} while (0)
519
520 #define CHECK_WSTR(name, sname1, field1, v, sname2, field2, flags) do { \
521         s = find(name); \
522         if (s) { \
523                 if (!s->sname1.field1.s || \
524                     strcmp(s->sname1.field1.s, v.sname2.out.field2.s)) { \
525                         printf("(%s) %s/%s [%s] != %s/%s [%s]\n", \
526                                 __location__, \
527                                 #sname1, #field1, s->sname1.field1.s, \
528                                 #sname2, #field2, v.sname2.out.field2.s); \
529                         ret = false; \
530                 } \
531         }} while (0)
532
533 #define CHECK_NAME(name, sname1, field1, fname, flags) do { \
534         s = find(name); \
535         if (s) { \
536                 if (!s->sname1.field1.s || \
537                     strcmp(s->sname1.field1.s, fname)) { \
538                         printf("(%s) %s/%s [%s] != %s\n", \
539                                 __location__, \
540                                 #sname1, #field1, s->sname1.field1.s, \
541                                 fname); \
542                         ret = false; \
543                 } \
544         }} while (0)
545
546 #define CHECK_UNIX_NAME(name, sname1, field1, fname, flags) do { \
547         s = find(name); \
548         if (s) { \
549                 if (!s->sname1.field1 || \
550                     strcmp(s->sname1.field1, fname)) { \
551                         printf("(%s) %s/%s [%s] != %s\n", \
552                                 __location__, \
553                                 #sname1, #field1, s->sname1.field1, \
554                                 fname); \
555                         ret = false; \
556                 } \
557         }} while (0)
558
559         /* check that all the results are as expected */
560         CHECK_VAL("SMB2_FIND_DIRECTORY_INFO",            directory_info,         attrib, all_info2, all_info2, attrib);
561         CHECK_VAL("SMB2_FIND_FULL_DIRECTORY_INFO",       full_directory_info,    attrib, all_info2, all_info2, attrib);
562         CHECK_VAL("SMB2_FIND_BOTH_DIRECTORY_INFO",       both_directory_info,    attrib, all_info2, all_info2, attrib);
563         CHECK_VAL("SMB2_FIND_ID_FULL_DIRECTORY_INFO",    id_full_directory_info, attrib, all_info2, all_info2, attrib);
564         CHECK_VAL("SMB2_FIND_ID_BOTH_DIRECTORY_INFO",    id_both_directory_info, attrib, all_info2, all_info2, attrib);
565
566         CHECK_NTTIME("SMB2_FIND_DIRECTORY_INFO",         directory_info,         write_time, all_info2, all_info2, write_time);
567         CHECK_NTTIME("SMB2_FIND_FULL_DIRECTORY_INFO",    full_directory_info,    write_time, all_info2, all_info2, write_time);
568         CHECK_NTTIME("SMB2_FIND_BOTH_DIRECTORY_INFO",    both_directory_info,    write_time, all_info2, all_info2, write_time);
569         CHECK_NTTIME("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info, write_time, all_info2, all_info2, write_time);
570         CHECK_NTTIME("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info, write_time, all_info2, all_info2, write_time);
571
572         CHECK_NTTIME("SMB2_FIND_DIRECTORY_INFO",         directory_info,         create_time, all_info2, all_info2, create_time);
573         CHECK_NTTIME("SMB2_FIND_FULL_DIRECTORY_INFO",    full_directory_info,    create_time, all_info2, all_info2, create_time);
574         CHECK_NTTIME("SMB2_FIND_BOTH_DIRECTORY_INFO",    both_directory_info,    create_time, all_info2, all_info2, create_time);
575         CHECK_NTTIME("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info, create_time, all_info2, all_info2, create_time);
576         CHECK_NTTIME("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info, create_time, all_info2, all_info2, create_time);
577         CHECK_NTTIME("SMB2_FIND_DIRECTORY_INFO",         directory_info,         access_time, all_info2, all_info2, access_time);
578         CHECK_NTTIME("SMB2_FIND_FULL_DIRECTORY_INFO",    full_directory_info,    access_time, all_info2, all_info2, access_time);
579         CHECK_NTTIME("SMB2_FIND_BOTH_DIRECTORY_INFO",    both_directory_info,    access_time, all_info2, all_info2, access_time);
580         CHECK_NTTIME("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info, access_time, all_info2, all_info2, access_time);
581         CHECK_NTTIME("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info, access_time, all_info2, all_info2, access_time);
582
583         CHECK_NTTIME("SMB2_FIND_DIRECTORY_INFO",         directory_info,         change_time, all_info2, all_info2, change_time);
584         CHECK_NTTIME("SMB2_FIND_FULL_DIRECTORY_INFO",    full_directory_info,    change_time, all_info2, all_info2, change_time);
585         CHECK_NTTIME("SMB2_FIND_BOTH_DIRECTORY_INFO",    both_directory_info,    change_time, all_info2, all_info2, change_time);
586         CHECK_NTTIME("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info, change_time, all_info2, all_info2, change_time);
587         CHECK_NTTIME("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info, change_time, all_info2, all_info2, change_time);
588
589         CHECK_VAL("SMB2_FIND_DIRECTORY_INFO",            directory_info,         size, all_info2, all_info2, size);
590         CHECK_VAL("SMB2_FIND_FULL_DIRECTORY_INFO",       full_directory_info,    size, all_info2, all_info2, size);
591         CHECK_VAL("SMB2_FIND_BOTH_DIRECTORY_INFO",       both_directory_info,    size, all_info2, all_info2, size);
592         CHECK_VAL("SMB2_FIND_ID_FULL_DIRECTORY_INFO",    id_full_directory_info, size, all_info2, all_info2, size);
593         CHECK_VAL("SMB2_FIND_ID_BOTH_DIRECTORY_INFO",    id_both_directory_info, size, all_info2, all_info2, size);
594
595         CHECK_VAL("SMB2_FIND_DIRECTORY_INFO",            directory_info,         alloc_size, all_info2, all_info2, alloc_size);
596         CHECK_VAL("SMB2_FIND_FULL_DIRECTORY_INFO",       full_directory_info,    alloc_size, all_info2, all_info2, alloc_size);
597         CHECK_VAL("SMB2_FIND_BOTH_DIRECTORY_INFO",       both_directory_info,    alloc_size, all_info2, all_info2, alloc_size);
598         CHECK_VAL("SMB2_FIND_ID_FULL_DIRECTORY_INFO",    id_full_directory_info, alloc_size, all_info2, all_info2, alloc_size);
599         CHECK_VAL("SMB2_FIND_ID_BOTH_DIRECTORY_INFO",    id_both_directory_info, alloc_size, all_info2, all_info2, alloc_size);
600
601         CHECK_VAL("SMB2_FIND_FULL_DIRECTORY_INFO",       full_directory_info,    ea_size, all_info2, all_info2, ea_size);
602         CHECK_VAL("SMB2_FIND_BOTH_DIRECTORY_INFO",       both_directory_info,    ea_size, all_info2, all_info2, ea_size);
603         CHECK_VAL("SMB2_FIND_ID_FULL_DIRECTORY_INFO",    id_full_directory_info, ea_size, all_info2, all_info2, ea_size);
604         CHECK_VAL("SMB2_FIND_ID_BOTH_DIRECTORY_INFO",    id_both_directory_info, ea_size, all_info2, all_info2, ea_size);
605
606         CHECK_WSTR("SMB2_FIND_BOTH_DIRECTORY_INFO",      both_directory_info,    short_name, alt_info, alt_name_info, fname, STR_UNICODE);
607
608         CHECK_NAME("SMB2_FIND_DIRECTORY_INFO",           directory_info,         name, fname, STR_TERMINATE_ASCII);
609         CHECK_NAME("SMB2_FIND_FULL_DIRECTORY_INFO",      full_directory_info,    name, fname, STR_TERMINATE_ASCII);
610         CHECK_NAME("SMB2_FIND_NAME_INFO",                name_info,              name, fname, STR_TERMINATE_ASCII);
611         CHECK_NAME("SMB2_FIND_BOTH_DIRECTORY_INFO",      both_directory_info,    name, fname, STR_TERMINATE_ASCII);
612         CHECK_NAME("SMB2_FIND_ID_FULL_DIRECTORY_INFO",   id_full_directory_info, name, fname, STR_TERMINATE_ASCII);
613         CHECK_NAME("SMB2_FIND_ID_BOTH_DIRECTORY_INFO",   id_both_directory_info, name, fname, STR_TERMINATE_ASCII);
614         CHECK_VAL("SMB2_FIND_ID_FULL_DIRECTORY_INFO",    id_full_directory_info, file_id, internal_info, internal_information, file_id);
615
616         CHECK_VAL("SMB2_FIND_ID_BOTH_DIRECTORY_INFO",    id_both_directory_info, file_id, internal_info, internal_information, file_id);
617
618 done:
619         smb2_util_close(tree, h);
620         smb2_util_unlink(tree, fname);
621         talloc_free(mem_ctx);
622
623         return ret;
624 }
625
626
627 struct multiple_result {
628         TALLOC_CTX *tctx;
629         int count;
630         union smb_search_data *list;
631 };
632
633 bool fill_result(void *private_data,
634                  union smb_search_data *file,
635                  int count,
636                  uint8_t level,
637                  enum smb_search_data_level data_level)
638 {
639         int i;
640         const char *sname;
641         struct multiple_result *data = (struct multiple_result *)private_data;
642
643         for (i=0; i<count; i++) {
644                 sname = extract_name(&file[i], level, data_level);
645                 if (!strcmp(sname, ".") || !(strcmp(sname, "..")))
646                         continue;
647                 data->count++;
648                 data->list = talloc_realloc(data->tctx,
649                                             data->list,
650                                             union smb_search_data,
651                                             data->count);
652                 data->list[data->count-1] = file[i];
653         }
654         return true;
655 }
656
657 enum continue_type {CONT_SINGLE, CONT_INDEX, CONT_RESTART};
658
659 static NTSTATUS multiple_smb2_search(struct smb2_tree *tree,
660                                      TALLOC_CTX *tctx,
661                                      const char *pattern,
662                                      uint8_t level,
663                                      enum smb_search_data_level data_level,
664                                      enum continue_type cont_type,
665                                      void *data,
666                                      struct smb2_handle *h)
667 {
668         struct smb2_find f;
669         bool ret = true;
670         uint_t count = 0;
671         union smb_search_data *d;
672         NTSTATUS status;
673         struct multiple_result *result = (struct multiple_result *)data;
674
675         ZERO_STRUCT(f);
676         f.in.file.handle        = *h;
677         f.in.pattern            = pattern;
678         f.in.max_response_size  = 0x1000;
679         f.in.level              = level;
680
681         /* The search should start from the beginning everytime */
682         f.in.continue_flags = SMB2_CONTINUE_FLAG_RESTART;
683
684         do {
685                 status = smb2_find_level(tree, tree, &f, &count, &d);
686                 if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES))
687                         break;
688                 CHECK_STATUS(status, NT_STATUS_OK);
689                 if (!fill_result(result, d, count, level, data_level)) {
690                         return NT_STATUS_UNSUCCESSFUL;
691                 }
692
693                 /*
694                  * After the first iteration is complete set the CONTINUE
695                  * FLAGS appropriately
696                  */
697                 switch (cont_type) {
698                         case CONT_INDEX:
699                                 f.in.continue_flags = SMB2_CONTINUE_FLAG_INDEX;
700                                 break;
701                         case CONT_SINGLE:
702                                 f.in.continue_flags = SMB2_CONTINUE_FLAG_SINGLE;
703                                 break;
704                         case CONT_RESTART:
705                         default:
706                                 /* we should prevent staying in the loop forever */
707                                 f.in.continue_flags = 0;
708                                 break;
709                 }
710         } while (count != 0);
711 done:
712         return status;
713 }
714
715
716 static enum smb_search_data_level compare_data_level;
717 uint8_t level_sort;
718
719 static int search_compare(union smb_search_data *d1,
720                           union smb_search_data *d2)
721 {
722         const char *s1, *s2;
723
724         s1 = extract_name(d1, level_sort, compare_data_level);
725         s2 = extract_name(d2, level_sort, compare_data_level);
726         return strcmp_safe(s1, s2);
727 }
728
729 /*
730    basic testing of search calls using many files
731 */
732 static bool test_many_files(struct torture_context *tctx,
733                             struct smb2_tree *tree)
734 {
735         TALLOC_CTX *mem_ctx = talloc_new(tctx);
736         const int num_files = 700;
737         int i, t;
738         char *fname;
739         bool ret = true;
740         NTSTATUS status;
741         struct multiple_result result;
742         struct smb2_create create;
743         struct smb2_handle h;
744         struct {
745                 const char *name;
746                 const char *cont_name;
747                 uint8_t level;
748                 enum smb_search_data_level data_level;
749                 enum continue_type cont_type;
750         } search_types[] = {
751                 {"SMB2_FIND_BOTH_DIRECTORY_INFO",    "SINGLE",  SMB2_FIND_BOTH_DIRECTORY_INFO,    RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO,    CONT_SINGLE},
752                 {"SMB2_FIND_BOTH_DIRECTORY_INFO",    "INDEX",   SMB2_FIND_BOTH_DIRECTORY_INFO,    RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO,    CONT_INDEX},
753                 {"SMB2_FIND_BOTH_DIRECTORY_INFO",    "RESTART", SMB2_FIND_BOTH_DIRECTORY_INFO,    RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO,    CONT_RESTART},
754                 {"SMB2_FIND_DIRECTORY_INFO",         "SINGLE",  SMB2_FIND_DIRECTORY_INFO,         RAW_SEARCH_DATA_DIRECTORY_INFO,         CONT_SINGLE},
755                 {"SMB2_FIND_DIRECTORY_INFO",         "INDEX",   SMB2_FIND_DIRECTORY_INFO,         RAW_SEARCH_DATA_DIRECTORY_INFO,         CONT_INDEX},
756                 {"SMB2_FIND_DIRECTORY_INFO",         "RESTART", SMB2_FIND_DIRECTORY_INFO,         RAW_SEARCH_DATA_DIRECTORY_INFO,         CONT_RESTART},
757                 {"SMB2_FIND_FULL_DIRECTORY_INFO",    "SINGLE",  SMB2_FIND_FULL_DIRECTORY_INFO,    RAW_SEARCH_DATA_FULL_DIRECTORY_INFO,    CONT_SINGLE},
758                 {"SMB2_FIND_FULL_DIRECTORY_INFO",    "INDEX",   SMB2_FIND_FULL_DIRECTORY_INFO,    RAW_SEARCH_DATA_FULL_DIRECTORY_INFO,    CONT_INDEX},
759                 {"SMB2_FIND_FULL_DIRECTORY_INFO",    "RESTART", SMB2_FIND_FULL_DIRECTORY_INFO,    RAW_SEARCH_DATA_FULL_DIRECTORY_INFO,    CONT_RESTART},
760                 {"SMB2_FIND_ID_FULL_DIRECTORY_INFO", "SINGLE",  SMB2_FIND_ID_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO, CONT_SINGLE},
761                 {"SMB2_FIND_ID_FULL_DIRECTORY_INFO", "INDEX",   SMB2_FIND_ID_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO, CONT_INDEX},
762                 {"SMB2_FIND_ID_FULL_DIRECTORY_INFO", "RESTART", SMB2_FIND_ID_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO, CONT_RESTART},
763                 {"SMB2_FIND_ID_BOTH_DIRECTORY_INFO", "SINGLE",  SMB2_FIND_ID_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO, CONT_SINGLE},
764                 {"SMB2_FIND_ID_BOTH_DIRECTORY_INFO", "INDEX",   SMB2_FIND_ID_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO, CONT_INDEX},
765                 {"SMB2_FIND_ID_BOTH_DIRECTORY_INFO", "RESTART", SMB2_FIND_ID_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO, CONT_RESTART}
766         };
767
768         smb2_deltree(tree, DNAME);
769         status = torture_smb2_testdir(tree, DNAME, &h);
770         CHECK_STATUS(status, NT_STATUS_OK);
771
772         torture_comment(tctx, "Testing with %d files\n", num_files);
773         ZERO_STRUCT(create);
774         create.in.desired_access = SEC_RIGHTS_FILE_ALL;
775         create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
776         create.in.create_disposition = NTCREATEX_DISP_CREATE;
777
778         for (i=num_files-1;i>=0;i--) {
779                 fname = talloc_asprintf(mem_ctx, DNAME "\\t%03d-%d.txt", i, i);
780                 create.in.fname = talloc_asprintf(mem_ctx, "%s", fname);
781                 status = smb2_create(tree, mem_ctx, &create);
782                 CHECK_STATUS(status, NT_STATUS_OK);
783                 smb2_util_close(tree, create.out.file.handle);
784                 talloc_free(fname);
785         }
786
787         for (t=0;t<ARRAY_SIZE(search_types);t++) {
788                 ZERO_STRUCT(result);
789                 result.tctx = talloc_new(tctx);
790
791                 torture_comment(tctx,
792                                 "Continue %s via %s\n", search_types[t].name,
793                                 search_types[t].cont_name);
794                 status = multiple_smb2_search(tree, tctx, "*",
795                                               search_types[t].level,
796                                               search_types[t].data_level,
797                                               search_types[t].cont_type,
798                                               &result, &h);
799
800                 CHECK_VALUE(result.count, num_files);
801
802                 compare_data_level = search_types[t].data_level;
803                 level_sort = search_types[t].level;
804
805                 qsort(result.list, result.count, sizeof(result.list[0]),
806                       QSORT_CAST  search_compare);
807
808                 for (i=0;i<result.count;i++) {
809                         const char *s;
810                         enum smb_search_level level;
811                         level = RAW_SEARCH_SMB2;
812                         s = extract_name(&result.list[i],
813                                          search_types[t].level,
814                                          compare_data_level);
815                         fname = talloc_asprintf(mem_ctx, "t%03d-%d.txt", i, i);
816                         torture_assert_str_equal(tctx, fname, s,
817                                                  "Incorrect name");
818                         talloc_free(fname);
819                 }
820                 talloc_free(result.tctx);
821         }
822
823 done:
824         smb2_util_close(tree, h);
825         smb2_deltree(tree, DNAME);
826         talloc_free(mem_ctx);
827
828         return ret;
829 }
830
831 /*
832   check a individual file result
833 */
834 static bool check_result(struct multiple_result *result,
835                          const char *name,
836                          bool exist,
837                          uint32_t attrib)
838 {
839         int i;
840         for (i=0;i<result->count;i++) {
841                 if (strcmp(name,
842                            result->list[i].both_directory_info.name.s) == 0) {
843                         break;
844                 }
845         }
846         if (i == result->count) {
847                 if (exist) {
848                         printf("failed: '%s' should exist with attribute %s\n",
849                                name, attrib_string(result->list, attrib));
850                         return false;
851                 }
852                 return true;
853         }
854
855         if (!exist) {
856                 printf("failed: '%s' should NOT exist (has attribute %s)\n",
857                        name, attrib_string(result->list,
858                        result->list[i].both_directory_info.attrib));
859                 return false;
860         }
861
862         if ((result->list[i].both_directory_info.attrib&0xFFF) != attrib) {
863                 printf("failed: '%s' should have attribute 0x%x (has 0x%x)\n",
864                        name,
865                        attrib, result->list[i].both_directory_info.attrib);
866                 return false;
867         }
868         return true;
869 }
870
871 /*
872    test what happens when the directory is modified during a search
873 */
874 static bool test_modify_search(struct torture_context *tctx,
875                                struct smb2_tree *tree)
876 {
877         int num_files = 700;
878         struct multiple_result result;
879         union smb_setfileinfo sfinfo;
880         TALLOC_CTX *mem_ctx = talloc_new(tctx);
881         struct smb2_create create;
882         struct smb2_handle h;
883         struct smb2_find f;
884         union smb_search_data *d;
885         struct file_elem files[700] = {};
886         NTSTATUS status;
887         bool ret = true;
888         int i;
889         uint_t count;
890
891         smb2_deltree(tree, DNAME);
892
893         status = torture_smb2_testdir(tree, DNAME, &h);
894         CHECK_STATUS(status, NT_STATUS_OK);
895
896         printf("Creating %d files\n", num_files);
897
898         ZERO_STRUCT(create);
899         create.in.desired_access = SEC_RIGHTS_FILE_ALL;
900         create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
901         create.in.create_disposition = NTCREATEX_DISP_CREATE;
902
903         for (i = num_files-1; i >= 0; i--) {
904                 files[i].name = talloc_asprintf(mem_ctx, "t%03d-%d.txt", i, i);
905                 create.in.fname = talloc_asprintf(mem_ctx, "%s\\%s",
906                                                   DNAME, files[i].name);
907                 status = smb2_create(tree, mem_ctx, &create);
908                 CHECK_STATUS(status, NT_STATUS_OK);
909                 smb2_util_close(tree, create.out.file.handle);
910         }
911
912         printf("pulling the first two files\n");
913         ZERO_STRUCT(result);
914         result.tctx = talloc_new(tctx);
915
916         ZERO_STRUCT(f);
917         f.in.file.handle        = h;
918         f.in.pattern            = "*";
919         f.in.continue_flags     = SMB2_CONTINUE_FLAG_SINGLE;
920         f.in.max_response_size  = 0x100;
921         f.in.level              = SMB2_FIND_BOTH_DIRECTORY_INFO;
922
923         do {
924                 status = smb2_find_level(tree, tree, &f, &count, &d);
925                 if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES))
926                         break;
927                 CHECK_STATUS(status, NT_STATUS_OK);
928                 if (!fill_result(&result, d, count, f.in.level,
929                                  RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO)) {
930                         ret = false;
931                         goto done;
932                 }
933         }while(result.count < 2);
934
935         printf("Changing attributes and deleting\n");
936
937         ZERO_STRUCT(create);
938         create.in.desired_access = SEC_RIGHTS_FILE_ALL;
939         create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
940         create.in.create_disposition = NTCREATEX_DISP_CREATE;
941
942         files[num_files].name = talloc_asprintf(mem_ctx, "T003-03.txt.2");
943         create.in.fname = talloc_asprintf(mem_ctx, "%s\\%s", DNAME,
944                                           files[num_files].name);
945         status = smb2_create(tree, mem_ctx, &create);
946         CHECK_STATUS(status, NT_STATUS_OK);
947         smb2_util_close(tree, create.out.file.handle);
948
949         ZERO_STRUCT(create);
950         create.in.desired_access = SEC_RIGHTS_FILE_ALL;
951         create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
952         create.in.create_disposition = NTCREATEX_DISP_CREATE;
953
954         files[num_files + 1].name = talloc_asprintf(mem_ctx, "T013-13.txt.2");
955         create.in.fname = talloc_asprintf(mem_ctx, "%s\\%s", DNAME,
956                                           files[num_files + 1].name);
957         status = smb2_create(tree, mem_ctx, &create);
958         CHECK_STATUS(status, NT_STATUS_OK);
959         smb2_util_close(tree, create.out.file.handle);
960
961         files[num_files + 2].name = talloc_asprintf(mem_ctx, "T013-13.txt.3");
962         status = smb2_create_complex_file(tree, DNAME "\\T013-13.txt.3", &h);
963         CHECK_STATUS(status, NT_STATUS_OK);
964
965         smb2_util_unlink(tree, DNAME "\\T014-14.txt");
966         smb2_util_setatr(tree, DNAME "\\T015-15.txt", FILE_ATTRIBUTE_HIDDEN);
967         smb2_util_setatr(tree, DNAME "\\T016-16.txt", FILE_ATTRIBUTE_NORMAL);
968         smb2_util_setatr(tree, DNAME "\\T017-17.txt", FILE_ATTRIBUTE_SYSTEM);
969         smb2_util_setatr(tree, DNAME "\\T018-18.txt", 0);
970         smb2_util_setatr(tree, DNAME "\\T039-39.txt", FILE_ATTRIBUTE_HIDDEN);
971         smb2_util_setatr(tree, DNAME "\\T000-0.txt", FILE_ATTRIBUTE_HIDDEN);
972         sfinfo.generic.level = RAW_SFILEINFO_DISPOSITION_INFORMATION;
973         sfinfo.generic.in.file.path = DNAME "\\T013-13.txt.3";
974         sfinfo.disposition_info.in.delete_on_close = 1;
975         status = smb2_composite_setpathinfo(tree, &sfinfo);
976         CHECK_STATUS(status, NT_STATUS_OK);
977
978         /* Reset the numfiles to include the new files and start the
979          * search from the beginning */
980         num_files = num_files + 2;
981         f.in.pattern = "*";
982         f.in.continue_flags = SMB2_CONTINUE_FLAG_RESTART;
983         result.count = 0;
984
985         do {
986                 status = smb2_find_level(tree, tree, &f, &count, &d);
987                 if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES))
988                         break;
989                 CHECK_STATUS(status, NT_STATUS_OK);
990                 if (!fill_result(&result, d, count, f.in.level,
991                                  RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO)) {
992                         ret = false;
993                         goto done;
994                 }
995                 f.in.continue_flags = 0;
996                 f.in.max_response_size  = 4096;
997         } while (count != 0);
998
999
1000         ret &= check_result(&result, "t039-39.txt", true, FILE_ATTRIBUTE_HIDDEN);
1001         ret &= check_result(&result, "t000-0.txt", true, FILE_ATTRIBUTE_HIDDEN);
1002         ret &= check_result(&result, "t014-14.txt", false, 0);
1003         ret &= check_result(&result, "t015-15.txt", true, FILE_ATTRIBUTE_HIDDEN);
1004         ret &= check_result(&result, "t016-16.txt", true, FILE_ATTRIBUTE_NORMAL);
1005         ret &= check_result(&result, "t017-17.txt", true, FILE_ATTRIBUTE_SYSTEM);
1006         ret &= check_result(&result, "t018-18.txt", true, FILE_ATTRIBUTE_ARCHIVE);
1007         ret &= check_result(&result, "t019-19.txt", true, FILE_ATTRIBUTE_ARCHIVE);
1008         ret &= check_result(&result, "T013-13.txt.2", true, FILE_ATTRIBUTE_ARCHIVE);
1009         ret &= check_result(&result, "T003-3.txt.2", false, 0);
1010         ret &= check_result(&result, "T013-13.txt.3", true, FILE_ATTRIBUTE_NORMAL);
1011
1012         if (!ret) {
1013                 for (i=0;i<result.count;i++) {
1014                         printf("%s %s (0x%x)\n",
1015                                result.list[i].both_directory_info.name.s,
1016                                attrib_string(tctx,
1017                                result.list[i].both_directory_info.attrib),
1018                                result.list[i].both_directory_info.attrib);
1019                 }
1020         }
1021  done:
1022         smb2_util_close(tree, h);
1023         smb2_deltree(tree, DNAME);
1024         talloc_free(mem_ctx);
1025
1026         return ret;
1027 }
1028
1029 /*
1030    testing if directories always come back sorted
1031 */
1032 static bool test_sorted(struct torture_context *tctx,
1033                         struct smb2_tree *tree)
1034 {
1035         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1036         const int num_files = 700;
1037         int i;
1038         struct file_elem files[700] = {};
1039         bool ret = true;
1040         NTSTATUS status;
1041         struct multiple_result result;
1042         struct smb2_handle h;
1043
1044         printf("Testing if directories always come back sorted\n");
1045         status = populate_tree(tctx, mem_ctx, tree, files, num_files, &h);
1046         CHECK_STATUS(status, NT_STATUS_OK);
1047
1048         ZERO_STRUCT(result);
1049         result.tctx = tctx;
1050
1051         status = multiple_smb2_search(tree, tctx, "*",
1052                                       SMB2_FIND_BOTH_DIRECTORY_INFO,
1053                                       RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO,
1054                                       SMB2_CONTINUE_FLAG_SINGLE,
1055                                       &result, &h);
1056
1057         CHECK_VALUE(result.count, num_files);
1058
1059         for (i=0;i<num_files-1;i++) {
1060                 const char *name1, *name2;
1061                 name1 = result.list[i].both_directory_info.name.s;
1062                 name2 = result.list[i+1].both_directory_info.name.s;
1063                 if (strcasecmp_m(name1, name2) > 0) {
1064                         printf("non-alphabetical order at entry %d  '%s' '%s'"
1065                                "\n", i, name1, name2);
1066                         torture_comment(tctx,
1067                         "Server does not produce sorted directory listings"
1068                         "(not an error)\n");
1069                         goto done;
1070                 }
1071         }
1072         talloc_free(result.list);
1073 done:
1074         smb2_util_close(tree, h);
1075         smb2_deltree(tree, DNAME);
1076         talloc_free(mem_ctx);
1077
1078         return ret;
1079 }
1080
1081 /* test the behavior of file_index field in the SMB2_FIND struct */
1082
1083 static bool test_file_index(struct torture_context *tctx,
1084                             struct smb2_tree *tree)
1085 {
1086         TALLOC_CTX *mem_ctx = talloc_new(mem_ctx);
1087         const int num_files = 100;
1088         int resume_index = 4;
1089         int i;
1090         char *fname;
1091         bool ret = true;
1092         NTSTATUS status;
1093         struct multiple_result result;
1094         struct smb2_create create;
1095         struct smb2_find f;
1096         struct smb2_handle h;
1097         union smb_search_data *d;
1098         int count;
1099
1100         smb2_deltree(tree, DNAME);
1101
1102         status = torture_smb2_testdir(tree, DNAME, &h);
1103         CHECK_STATUS(status, NT_STATUS_OK);
1104
1105         printf("Testing the behavior of file_index flag\n");
1106
1107         ZERO_STRUCT(create);
1108         create.in.desired_access = SEC_RIGHTS_FILE_ALL;
1109         create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1110         create.in.create_disposition = NTCREATEX_DISP_CREATE;
1111         for (i = num_files-1; i >= 0; i--) {
1112                 fname = talloc_asprintf(mem_ctx, DNAME "\\file%u.txt", i);
1113                 create.in.fname = fname;
1114                 status = smb2_create(tree, mem_ctx, &create);
1115                 CHECK_STATUS(status, NT_STATUS_OK);
1116                 talloc_free(fname);
1117                 smb2_util_close(tree, create.out.file.handle);
1118         }
1119
1120         ZERO_STRUCT(result);
1121         result.tctx = tctx;
1122
1123         ZERO_STRUCT(f);
1124         f.in.file.handle        = h;
1125         f.in.pattern            = "*";
1126         f.in.continue_flags     = SMB2_CONTINUE_FLAG_SINGLE;
1127         f.in.max_response_size  = 0x1000;
1128         f.in.level              = SMB2_FIND_FULL_DIRECTORY_INFO;
1129
1130         do {
1131                 status = smb2_find_level(tree, tree, &f, &count, &d);
1132                 if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES))
1133                         break;
1134                 CHECK_STATUS(status, NT_STATUS_OK);
1135                 if (!fill_result(&result, d, count, f.in.level,
1136                                  RAW_SEARCH_DATA_FULL_DIRECTORY_INFO)) {
1137                         ret = false;
1138                         goto done;
1139                 }
1140         } while(result.count < 10);
1141
1142         if (result.list[0].full_directory_info.file_index == 0) {
1143                 torture_comment(tctx,
1144                                 "Talking to a server that doesn't provide a "
1145                                 "file index.\nWindows servers using NTFS do "
1146                                 "not provide a file_index. Skipping test\n");
1147                 goto done;
1148         } else {
1149                 /* We are not talking to a Windows based server.  Windows
1150                  * servers using NTFS do not provide a file_index.  Windows
1151                  * server using FAT do provide a file index, however in both
1152                  * cases they do not honor a file index on a resume request.
1153                  * See MS-FSCC <62> and MS-SMB2 <54> for more information. */
1154
1155                 /* Set the file_index flag to point to the fifth file from the
1156                  * previous enumeration and try to start the subsequent
1157                  * searches from that point */
1158                 f.in.file_index =
1159                     result.list[resume_index].full_directory_info.file_index;
1160                 f.in.continue_flags = SMB2_CONTINUE_FLAG_INDEX;
1161
1162                 /* get the name of the next expected file */
1163                 fname = talloc_asprintf(mem_ctx, DNAME "\\%s",
1164                         result.list[resume_index].full_directory_info.name.s);
1165
1166                 ZERO_STRUCT(result);
1167                 result.tctx = tctx;
1168                 status = smb2_find_level(tree, tree, &f, &count, &d);
1169                 if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES))
1170                         goto done;
1171                 CHECK_STATUS(status, NT_STATUS_OK);
1172                 if (!fill_result(&result, d, count, f.in.level,
1173                                  RAW_SEARCH_DATA_FULL_DIRECTORY_INFO)) {
1174                         ret = false;
1175                         goto done;
1176                 }
1177                 if (strcmp(fname,
1178                         result.list[0].full_directory_info.name.s)) {
1179                         printf("next expected file: %s but the server "
1180                                "returned %s\n", fname,
1181                                result.list[0].full_directory_info.name.s);
1182                         torture_comment(tctx,
1183                                         "Not an error. Resuming using a file "
1184                                         "index is an optional feature of the "
1185                                         "protocol.");
1186                         goto done;
1187                 }
1188         }
1189 done:
1190         smb2_util_close(tree, h);
1191         smb2_deltree(tree, DNAME);
1192         talloc_free(mem_ctx);
1193
1194         return ret;
1195 }
1196
1197 /*
1198  * Tests directory enumeration in a directory containing >1000 files with
1199  * names of varying lengths.
1200  */
1201 static bool test_large_files(struct torture_context *tctx,
1202                              struct smb2_tree *tree)
1203 {
1204         TALLOC_CTX *mem_ctx = talloc_new(mem_ctx);
1205         const int num_files = 2000;
1206         int i, j = 1, retry_count = 0;
1207         struct file_elem files[2000] = {};
1208         bool ret = true;
1209         NTSTATUS status;
1210         struct smb2_create create;
1211         struct smb2_find f;
1212         struct smb2_handle h;
1213         union smb_search_data *d;
1214         int count, file_count = 0;
1215
1216         torture_comment(tctx,
1217         "Testing directory enumeration in a directory with >1000 files\n");
1218
1219         smb2_deltree(tree, DNAME);
1220
1221         ZERO_STRUCT(create);
1222         create.in.desired_access = SEC_RIGHTS_DIR_ALL;
1223         create.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1224         create.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
1225         create.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1226                                  NTCREATEX_SHARE_ACCESS_WRITE |
1227                                  NTCREATEX_SHARE_ACCESS_DELETE;
1228         create.in.create_disposition = NTCREATEX_DISP_CREATE;
1229         create.in.fname = DNAME;
1230
1231         status = smb2_create(tree, mem_ctx, &create);
1232         CHECK_STATUS(status, NT_STATUS_OK);
1233         h = create.out.file.handle;
1234
1235         ZERO_STRUCT(create);
1236         create.in.desired_access = SEC_RIGHTS_FILE_ALL;
1237         create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1238         create.in.create_disposition = NTCREATEX_DISP_CREATE;
1239
1240         for (i = 0; i < num_files; i++) {
1241                 files[i].name = generate_random_str(tctx, j);
1242                 create.in.fname = talloc_asprintf(mem_ctx, "%s\\%s",
1243                     DNAME, files[i].name);
1244                 status = smb2_create(tree, mem_ctx, &create);
1245                 CHECK_STATUS(status, NT_STATUS_OK);
1246                 smb2_util_close(tree, create.out.file.handle);
1247                 retry_count = 0;
1248                 if (i%9 == 0)
1249                         j = j + 1;
1250         }
1251
1252         ZERO_STRUCT(f);
1253         f.in.file.handle        = h;
1254         f.in.pattern            = "*";
1255         f.in.max_response_size  = 0x100;
1256         f.in.level              = SMB2_FIND_BOTH_DIRECTORY_INFO;
1257
1258         do {
1259                 status = smb2_find_level(tree, tree, &f, &count, &d);
1260                 if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES))
1261                         break;
1262                 CHECK_STATUS(status, NT_STATUS_OK);
1263
1264                 for (i = 0; i < count; i++) {
1265                         bool expected;
1266                         const char *found = d[i].both_directory_info.name.s;
1267
1268                         if (!strcmp(found, ".") || !strcmp(found, ".."))
1269                                 continue;
1270
1271                         expected = false;
1272                         for (j = 0; j < 2000; j++) {
1273                                 if (!strcmp(files[j].name, found)) {
1274                                         files[j].found = true;
1275                                         expected = true;
1276                                         break;
1277                                 }
1278                         }
1279
1280                         if (expected)
1281                                 continue;
1282
1283                         torture_result(tctx, TORTURE_FAIL,
1284                             "(%s): didn't expect %s\n",
1285                             __location__, found);
1286                         ret = false;
1287                         goto done;
1288                 }
1289                 file_count = file_count + i;
1290                 f.in.continue_flags = 0;
1291                 f.in.max_response_size  = 4096;
1292         } while (count != 0);
1293
1294         CHECK_VALUE(file_count, num_files + 2);
1295
1296         for (i = 0; i < num_files; i++) {
1297                 if (files[j].found)
1298                         continue;
1299
1300                 torture_result(tctx, TORTURE_FAIL,
1301                     "(%s): expected to find %s, but didn't\n",
1302                     __location__, files[j].name);
1303                 ret = false;
1304                 goto done;
1305         }
1306 done:
1307         smb2_util_close(tree, h);
1308         smb2_deltree(tree, DNAME);
1309         talloc_free(mem_ctx);
1310
1311         return ret;
1312 }
1313
1314 struct torture_suite *torture_smb2_dir_init(void)
1315 {
1316         struct torture_suite *suite =
1317             torture_suite_create(talloc_autofree_context(), "DIR");
1318
1319         torture_suite_add_1smb2_test(suite, "FIND", test_find);
1320         torture_suite_add_1smb2_test(suite, "FIXED", test_fixed);
1321         torture_suite_add_1smb2_test(suite, "ONE", test_one_file);
1322         torture_suite_add_1smb2_test(suite, "MANY", test_many_files);
1323         torture_suite_add_1smb2_test(suite, "MODIFY", test_modify_search);
1324         torture_suite_add_1smb2_test(suite, "SORTED", test_sorted);
1325         torture_suite_add_1smb2_test(suite, "FILE-INDEX", test_file_index);
1326         torture_suite_add_1smb2_test(suite, "LARGE-FILES", test_large_files);
1327         suite->description = talloc_strdup(suite, "SMB2-DIR tests");
1328
1329         return suite;
1330 }