libcli: Make cli_smb2_close_fnum async
[Samba.git] / source4 / torture / smb2 / dir.c
blob0e409f2b70af44201beedb65e3c4808285c0e348
1 /*
2 Unix SMB/CIFS implementation.
4 SMB2 dir list test suite
6 Copyright (C) Andrew Tridgell 2005
7 Copyright (C) Zachary Loafman 2009
8 Copyright (C) Aravind Srinivasan 2009
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.
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.
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/>.
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"
32 #include "torture/torture.h"
33 #include "torture/smb2/proto.h"
34 #include "torture/util.h"
36 #include "system/filesys.h"
37 #include "lib/util/tsort.h"
39 #define DNAME "smb2_dir"
40 #define NFILES 100
42 struct file_elem {
43 char *name;
44 NTTIME create_time;
45 bool found;
48 static NTSTATUS populate_tree(struct torture_context *tctx,
49 TALLOC_CTX *mem_ctx,
50 struct smb2_tree *tree,
51 struct file_elem *files,
52 int nfiles,
53 struct smb2_handle *h_out)
55 struct smb2_create create;
56 char **strs = NULL;
57 NTSTATUS status;
58 bool ret;
59 int i;
61 smb2_deltree(tree, DNAME);
63 ZERO_STRUCT(create);
64 create.in.desired_access = SEC_RIGHTS_DIR_ALL;
65 create.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
66 create.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
67 create.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
68 NTCREATEX_SHARE_ACCESS_WRITE |
69 NTCREATEX_SHARE_ACCESS_DELETE;
70 create.in.create_disposition = NTCREATEX_DISP_CREATE;
71 create.in.fname = DNAME;
73 status = smb2_create(tree, mem_ctx, &create);
74 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
75 *h_out = create.out.file.handle;
77 ZERO_STRUCT(create);
78 create.in.desired_access = SEC_RIGHTS_FILE_ALL;
79 create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
80 create.in.create_disposition = NTCREATEX_DISP_CREATE;
82 strs = generate_unique_strs(mem_ctx, 8, nfiles);
83 if (strs == NULL) {
84 status = NT_STATUS_OBJECT_NAME_COLLISION;
85 goto done;
87 for (i = 0; i < nfiles; i++) {
88 files[i].name = strs[i];
89 create.in.fname = talloc_asprintf(mem_ctx, "%s\\%s",
90 DNAME, files[i].name);
91 status = smb2_create(tree, mem_ctx, &create);
92 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
93 files[i].create_time = create.out.create_time;
94 smb2_util_close(tree, create.out.file.handle);
96 done:
97 return status;
101 test find continue
104 static bool test_find(struct torture_context *tctx,
105 struct smb2_tree *tree)
107 TALLOC_CTX *mem_ctx = talloc_new(tctx);
108 struct smb2_handle h;
109 struct smb2_find f;
110 union smb_search_data *d;
111 struct file_elem files[NFILES] = {};
112 NTSTATUS status;
113 bool ret = true;
114 unsigned int count;
115 int i, j, file_count = 0;
117 status = populate_tree(tctx, mem_ctx, tree, files, NFILES, &h);
119 ZERO_STRUCT(f);
120 f.in.file.handle = h;
121 f.in.pattern = "*";
122 f.in.continue_flags = SMB2_CONTINUE_FLAG_SINGLE;
123 f.in.max_response_size = 0x100;
124 f.in.level = SMB2_FIND_BOTH_DIRECTORY_INFO;
126 do {
127 status = smb2_find_level(tree, tree, &f, &count, &d);
128 if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES))
129 break;
130 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
132 for (i = 0; i < count; i++) {
133 bool expected;
134 const char *found = d[i].both_directory_info.name.s;
135 NTTIME ctime = d[i].both_directory_info.create_time;
137 if (!strcmp(found, ".") || !strcmp(found, ".."))
138 continue;
140 expected = false;
141 for (j = 0; j < NFILES; j++) {
142 if (strcmp(files[j].name, found) != 0) {
143 continue;
146 if (files[j].create_time != ctime) {
147 torture_result(tctx, TORTURE_FAIL,
148 "(%s): create_time mismatch for %s"
149 "\n", __location__, found);
150 ret = false;
151 goto done;
153 files[j].found = true;
154 expected = true;
155 break;
158 if (expected)
159 continue;
161 torture_result(tctx, TORTURE_FAIL,
162 "(%s): didn't expect %s\n",
163 __location__, found);
164 ret = false;
165 goto done;
168 file_count = file_count + i;
169 f.in.continue_flags = 0;
170 f.in.max_response_size = 4096;
171 } while (count != 0);
173 torture_assert_int_equal_goto(tctx, file_count, NFILES + 2, ret, done,
174 "");
176 for (i = 0; i < NFILES; i++) {
177 if (files[j].found)
178 continue;
180 torture_result(tctx, TORTURE_FAIL,
181 "(%s): expected to find %s, but didn't\n",
182 __location__, files[j].name);
183 ret = false;
184 goto done;
187 done:
188 smb2_deltree(tree, DNAME);
189 talloc_free(mem_ctx);
191 return ret;
195 test fixed enumeration
198 static bool test_fixed(struct torture_context *tctx,
199 struct smb2_tree *tree)
201 TALLOC_CTX *mem_ctx = talloc_new(tctx);
202 struct smb2_create create;
203 struct smb2_handle h, h2;
204 struct smb2_find f;
205 union smb_search_data *d;
206 struct file_elem files[NFILES] = {};
207 NTSTATUS status;
208 bool ret = true;
209 unsigned int count;
210 int i;
212 status = populate_tree(tctx, mem_ctx, tree, files, NFILES, &h);
214 ZERO_STRUCT(create);
215 create.in.desired_access = SEC_RIGHTS_DIR_ALL;
216 create.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
217 create.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
218 create.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
219 NTCREATEX_SHARE_ACCESS_WRITE |
220 NTCREATEX_SHARE_ACCESS_DELETE;
221 create.in.create_disposition = NTCREATEX_DISP_OPEN;
222 create.in.fname = DNAME;
224 status = smb2_create(tree, mem_ctx, &create);
225 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
226 h2 = create.out.file.handle;
228 ZERO_STRUCT(f);
229 f.in.file.handle = h;
230 f.in.pattern = "*";
231 f.in.continue_flags = SMB2_CONTINUE_FLAG_SINGLE;
232 f.in.max_response_size = 0x100;
233 f.in.level = SMB2_FIND_BOTH_DIRECTORY_INFO;
235 /* Start enumeration on h, then delete all from h2 */
236 status = smb2_find_level(tree, tree, &f, &count, &d);
237 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
239 f.in.file.handle = h2;
241 do {
242 status = smb2_find_level(tree, tree, &f, &count, &d);
243 if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES))
244 break;
245 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
247 for (i = 0; i < count; i++) {
248 const char *found = d[i].both_directory_info.name.s;
249 char *path = talloc_asprintf(mem_ctx, "%s\\%s",
250 DNAME, found);
252 if (!strcmp(found, ".") || !strcmp(found, ".."))
253 continue;
255 status = smb2_util_unlink(tree, path);
256 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
257 "");
259 talloc_free(path);
262 f.in.continue_flags = 0;
263 f.in.max_response_size = 4096;
264 } while (count != 0);
266 /* Now finish h enumeration. */
267 f.in.file.handle = h;
269 do {
270 status = smb2_find_level(tree, tree, &f, &count, &d);
271 if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES))
272 break;
273 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
275 for (i = 0; i < count; i++) {
276 const char *found = d[i].both_directory_info.name.s;
278 if (!strcmp(found, ".") || !strcmp(found, ".."))
279 continue;
281 torture_result(tctx, TORTURE_FAIL,
282 "(%s): didn't expect %s (count=%u)\n",
283 __location__, found, count);
284 ret = false;
285 goto done;
288 f.in.continue_flags = 0;
289 f.in.max_response_size = 4096;
290 } while (count != 0);
292 done:
293 smb2_util_close(tree, h);
294 smb2_util_close(tree, h2);
295 smb2_deltree(tree, DNAME);
296 talloc_free(mem_ctx);
298 return ret;
301 static struct {
302 const char *name;
303 uint8_t level;
304 enum smb_search_data_level data_level;
305 int name_offset;
306 int resume_key_offset;
307 uint32_t capability_mask;
308 NTSTATUS status;
309 union smb_search_data data;
310 } levels[] = {
311 {"SMB2_FIND_DIRECTORY_INFO",
312 SMB2_FIND_DIRECTORY_INFO, RAW_SEARCH_DATA_DIRECTORY_INFO,
313 offsetof(union smb_search_data, directory_info.name.s),
314 offsetof(union smb_search_data, directory_info.file_index),
316 {"SMB2_FIND_FULL_DIRECTORY_INFO",
317 SMB2_FIND_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_FULL_DIRECTORY_INFO,
318 offsetof(union smb_search_data, full_directory_info.name.s),
319 offsetof(union smb_search_data, full_directory_info.file_index),
321 {"SMB2_FIND_NAME_INFO",
322 SMB2_FIND_NAME_INFO, RAW_SEARCH_DATA_NAME_INFO,
323 offsetof(union smb_search_data, name_info.name.s),
324 offsetof(union smb_search_data, name_info.file_index),
326 {"SMB2_FIND_BOTH_DIRECTORY_INFO",
327 SMB2_FIND_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO,
328 offsetof(union smb_search_data, both_directory_info.name.s),
329 offsetof(union smb_search_data, both_directory_info.file_index),
331 {"SMB2_FIND_ID_FULL_DIRECTORY_INFO",
332 SMB2_FIND_ID_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO,
333 offsetof(union smb_search_data, id_full_directory_info.name.s),
334 offsetof(union smb_search_data, id_full_directory_info.file_index),
336 {"SMB2_FIND_ID_BOTH_DIRECTORY_INFO",
337 SMB2_FIND_ID_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO,
338 offsetof(union smb_search_data, id_both_directory_info.name.s),
339 offsetof(union smb_search_data, id_both_directory_info.file_index),
344 extract the name from a smb_data structure and level
346 static const char *extract_name(union smb_search_data *data,
347 uint8_t level,
348 enum smb_search_data_level data_level)
350 int i;
351 for (i=0;i<ARRAY_SIZE(levels);i++) {
352 if (level == levels[i].level &&
353 data_level == levels[i].data_level) {
354 return *(const char **)(levels[i].name_offset + (char *)data);
357 return NULL;
360 /* find a level in the table by name */
361 static union smb_search_data *find(const char *name)
363 int i;
364 for (i=0;i<ARRAY_SIZE(levels);i++) {
365 if (NT_STATUS_IS_OK(levels[i].status) &&
366 strcmp(levels[i].name, name) == 0) {
367 return &levels[i].data;
370 return NULL;
373 static bool fill_level_data(TALLOC_CTX *mem_ctx,
374 union smb_search_data *data,
375 union smb_search_data *d,
376 unsigned int count,
377 uint8_t level,
378 enum smb_search_data_level data_level)
380 int i;
381 const char *sname = NULL;
382 for (i=0; i < count ; i++) {
383 sname = extract_name(&d[i], level, data_level);
384 if (sname == NULL)
385 return false;
386 if (!strcmp(sname, ".") || !strcmp(sname, ".."))
387 continue;
388 *data = d[i];
390 return true;
394 NTSTATUS torture_single_file_search(struct smb2_tree *tree,
395 TALLOC_CTX *mem_ctx,
396 const char *pattern,
397 uint8_t level,
398 enum smb_search_data_level data_level,
399 int idx,
400 union smb_search_data *d,
401 unsigned int *count,
402 struct smb2_handle *h)
404 struct smb2_find f;
405 NTSTATUS status;
407 ZERO_STRUCT(f);
408 f.in.file.handle = *h;
409 f.in.pattern = pattern;
410 f.in.continue_flags = SMB2_CONTINUE_FLAG_RESTART;
411 f.in.max_response_size = 0x100;
412 f.in.level = level;
414 status = smb2_find_level(tree, tree, &f, count, &d);
415 if (NT_STATUS_IS_OK(status))
416 fill_level_data(mem_ctx, &levels[idx].data, d, *count, level,
417 data_level);
418 return status;
422 basic testing of all File Information Classes using a single file
424 static bool test_one_file(struct torture_context *tctx,
425 struct smb2_tree *tree)
427 TALLOC_CTX *mem_ctx = talloc_new(tctx);
428 bool ret = true;
429 const char *fname = "torture_search.txt";
430 NTSTATUS status;
431 int i;
432 unsigned int count;
433 union smb_fileinfo all_info2, alt_info, internal_info;
434 union smb_search_data *s;
435 union smb_search_data d;
436 struct smb2_handle h, h2;
438 status = torture_smb2_testdir(tree, DNAME, &h);
439 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
441 status = smb2_create_complex_file(tree, DNAME "\\torture_search.txt",
442 &h2);
443 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
445 /* call all the File Information Classes */
446 for (i=0;i<ARRAY_SIZE(levels);i++) {
447 torture_comment(tctx, "Testing %s %d\n", levels[i].name,
448 levels[i].level);
450 levels[i].status = torture_single_file_search(tree, mem_ctx,
451 fname, levels[i].level, levels[i].data_level,
452 i, &d, &count, &h);
453 torture_assert_ntstatus_ok_goto(tctx, levels[i].status, ret,
454 done, "");
457 /* get the all_info file into to check against */
458 all_info2.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
459 all_info2.generic.in.file.handle = h2;
460 status = smb2_getinfo_file(tree, tctx, &all_info2);
461 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
462 "RAW_FILEINFO_ALL_INFO failed");
464 alt_info.generic.level = RAW_FILEINFO_ALT_NAME_INFORMATION;
465 alt_info.generic.in.file.handle = h2;
466 status = smb2_getinfo_file(tree, tctx, &alt_info);
467 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
468 "RAW_FILEINFO_ALT_NAME_INFO failed");
470 internal_info.generic.level = RAW_FILEINFO_INTERNAL_INFORMATION;
471 internal_info.generic.in.file.handle = h2;
472 status = smb2_getinfo_file(tree, tctx, &internal_info);
473 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
474 "RAW_FILEINFO_INTERNAL_INFORMATION "
475 "failed");
477 #define CHECK_VAL(name, sname1, field1, v, sname2, field2) do { \
478 s = find(name); \
479 if (s) { \
480 if ((s->sname1.field1) != (v.sname2.out.field2)) { \
481 torture_result(tctx, TORTURE_FAIL, \
482 "(%s) %s/%s [0x%x] != %s/%s [0x%x]\n", \
483 __location__, \
484 #sname1, #field1, (int)s->sname1.field1, \
485 #sname2, #field2, (int)v.sname2.out.field2); \
486 ret = false; \
488 }} while (0)
490 #define CHECK_TIME(name, sname1, field1, v, sname2, field2) do { \
491 s = find(name); \
492 if (s) { \
493 if (s->sname1.field1 != \
494 (~1 & nt_time_to_unix(v.sname2.out.field2))) { \
495 torture_result(tctx, TORTURE_FAIL, \
496 "(%s) %s/%s [%s] != %s/%s [%s]\n", \
497 __location__, \
498 #sname1, #field1, \
499 timestring(tctx, s->sname1.field1), \
500 #sname2, #field2, \
501 nt_time_string(tctx, v.sname2.out.field2)); \
502 ret = false; \
504 }} while (0)
506 #define CHECK_NTTIME(name, sname1, field1, v, sname2, field2) do { \
507 s = find(name); \
508 if (s) { \
509 if (s->sname1.field1 != v.sname2.out.field2) { \
510 torture_result(tctx, TORTURE_FAIL, \
511 "(%s) %s/%s [%s] != %s/%s [%s]\n", \
512 __location__, \
513 #sname1, #field1, \
514 nt_time_string(tctx, s->sname1.field1), \
515 #sname2, #field2, \
516 nt_time_string(tctx, v.sname2.out.field2)); \
517 ret = false; \
519 }} while (0)
521 #define CHECK_STR(name, sname1, field1, v, sname2, field2) do { \
522 s = find(name); \
523 if (s) { \
524 if (!s->sname1.field1 || \
525 strcmp(s->sname1.field1, v.sname2.out.field2.s)) { \
526 torture_result(tctx, TORTURE_FAIL, \
527 "(%s) %s/%s [%s] != %s/%s [%s]\n", \
528 __location__, \
529 #sname1, #field1, s->sname1.field1, \
530 #sname2, #field2, v.sname2.out.field2.s); \
531 ret = false; \
533 }} while (0)
535 #define CHECK_WSTR(name, sname1, field1, v, sname2, field2, flags) do { \
536 s = find(name); \
537 if (s) { \
538 if (!s->sname1.field1.s || \
539 strcmp(s->sname1.field1.s, v.sname2.out.field2.s)) { \
540 torture_result(tctx, TORTURE_FAIL, \
541 "(%s) %s/%s [%s] != %s/%s [%s]\n", \
542 __location__, \
543 #sname1, #field1, s->sname1.field1.s, \
544 #sname2, #field2, v.sname2.out.field2.s); \
545 ret = false; \
547 }} while (0)
549 #define CHECK_NAME(name, sname1, field1, fname, flags) do { \
550 s = find(name); \
551 if (s) { \
552 if (!s->sname1.field1.s || \
553 strcmp(s->sname1.field1.s, fname)) { \
554 torture_result(tctx, TORTURE_FAIL, \
555 "(%s) %s/%s [%s] != %s\n", \
556 __location__, \
557 #sname1, #field1, s->sname1.field1.s, fname); \
558 ret = false; \
560 }} while (0)
562 #define CHECK_UNIX_NAME(name, sname1, field1, fname, flags) do { \
563 s = find(name); \
564 if (s) { \
565 if (!s->sname1.field1 || \
566 strcmp(s->sname1.field1, fname)) { \
567 torture_result(tctx, TORTURE_FAIL, \
568 "(%s) %s/%s [%s] != %s\n", \
569 __location__, \
570 #sname1, #field1, s->sname1.field1, fname); \
571 ret = false; \
573 }} while (0)
575 /* check that all the results are as expected */
576 CHECK_VAL("SMB2_FIND_DIRECTORY_INFO", directory_info, attrib, all_info2, all_info2, attrib);
577 CHECK_VAL("SMB2_FIND_FULL_DIRECTORY_INFO", full_directory_info, attrib, all_info2, all_info2, attrib);
578 CHECK_VAL("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info, attrib, all_info2, all_info2, attrib);
579 CHECK_VAL("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info, attrib, all_info2, all_info2, attrib);
580 CHECK_VAL("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info, attrib, all_info2, all_info2, attrib);
582 CHECK_NTTIME("SMB2_FIND_DIRECTORY_INFO", directory_info, write_time, all_info2, all_info2, write_time);
583 CHECK_NTTIME("SMB2_FIND_FULL_DIRECTORY_INFO", full_directory_info, write_time, all_info2, all_info2, write_time);
584 CHECK_NTTIME("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info, write_time, all_info2, all_info2, write_time);
585 CHECK_NTTIME("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info, write_time, all_info2, all_info2, write_time);
586 CHECK_NTTIME("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info, write_time, all_info2, all_info2, write_time);
588 CHECK_NTTIME("SMB2_FIND_DIRECTORY_INFO", directory_info, create_time, all_info2, all_info2, create_time);
589 CHECK_NTTIME("SMB2_FIND_FULL_DIRECTORY_INFO", full_directory_info, create_time, all_info2, all_info2, create_time);
590 CHECK_NTTIME("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info, create_time, all_info2, all_info2, create_time);
591 CHECK_NTTIME("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info, create_time, all_info2, all_info2, create_time);
592 CHECK_NTTIME("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info, create_time, all_info2, all_info2, create_time);
594 CHECK_NTTIME("SMB2_FIND_DIRECTORY_INFO", directory_info, access_time, all_info2, all_info2, access_time);
595 CHECK_NTTIME("SMB2_FIND_FULL_DIRECTORY_INFO", full_directory_info, access_time, all_info2, all_info2, access_time);
596 CHECK_NTTIME("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info, access_time, all_info2, all_info2, access_time);
597 CHECK_NTTIME("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info, access_time, all_info2, all_info2, access_time);
598 CHECK_NTTIME("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info, access_time, all_info2, all_info2, access_time);
600 CHECK_NTTIME("SMB2_FIND_DIRECTORY_INFO", directory_info, change_time, all_info2, all_info2, change_time);
601 CHECK_NTTIME("SMB2_FIND_FULL_DIRECTORY_INFO", full_directory_info, change_time, all_info2, all_info2, change_time);
602 CHECK_NTTIME("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info, change_time, all_info2, all_info2, change_time);
603 CHECK_NTTIME("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info, change_time, all_info2, all_info2, change_time);
604 CHECK_NTTIME("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info, change_time, all_info2, all_info2, change_time);
606 CHECK_VAL("SMB2_FIND_DIRECTORY_INFO", directory_info, size, all_info2, all_info2, size);
607 CHECK_VAL("SMB2_FIND_FULL_DIRECTORY_INFO", full_directory_info, size, all_info2, all_info2, size);
608 CHECK_VAL("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info, size, all_info2, all_info2, size);
609 CHECK_VAL("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info, size, all_info2, all_info2, size);
610 CHECK_VAL("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info, size, all_info2, all_info2, size);
612 CHECK_VAL("SMB2_FIND_DIRECTORY_INFO", directory_info, alloc_size, all_info2, all_info2, alloc_size);
613 CHECK_VAL("SMB2_FIND_FULL_DIRECTORY_INFO", full_directory_info, alloc_size, all_info2, all_info2, alloc_size);
614 CHECK_VAL("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info, alloc_size, all_info2, all_info2, alloc_size);
615 CHECK_VAL("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info, alloc_size, all_info2, all_info2, alloc_size);
616 CHECK_VAL("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info, alloc_size, all_info2, all_info2, alloc_size);
618 CHECK_VAL("SMB2_FIND_FULL_DIRECTORY_INFO", full_directory_info, ea_size, all_info2, all_info2, ea_size);
619 CHECK_VAL("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info, ea_size, all_info2, all_info2, ea_size);
620 CHECK_VAL("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info, ea_size, all_info2, all_info2, ea_size);
621 CHECK_VAL("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info, ea_size, all_info2, all_info2, ea_size);
623 CHECK_NAME("SMB2_FIND_DIRECTORY_INFO", directory_info, name, fname, STR_TERMINATE_ASCII);
624 CHECK_NAME("SMB2_FIND_FULL_DIRECTORY_INFO", full_directory_info, name, fname, STR_TERMINATE_ASCII);
625 CHECK_NAME("SMB2_FIND_NAME_INFO", name_info, name, fname, STR_TERMINATE_ASCII);
626 CHECK_NAME("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info, name, fname, STR_TERMINATE_ASCII);
627 CHECK_NAME("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info, name, fname, STR_TERMINATE_ASCII);
628 CHECK_NAME("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info, name, fname, STR_TERMINATE_ASCII);
630 CHECK_WSTR("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info, short_name, alt_info, alt_name_info, fname, STR_UNICODE);
632 CHECK_VAL("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info, file_id, internal_info, internal_information, file_id);
633 CHECK_VAL("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info, file_id, internal_info, internal_information, file_id);
635 done:
636 smb2_util_close(tree, h);
637 smb2_util_unlink(tree, fname);
638 talloc_free(mem_ctx);
640 return ret;
644 struct multiple_result {
645 TALLOC_CTX *tctx;
646 int count;
647 union smb_search_data *list;
650 bool fill_result(void *private_data,
651 union smb_search_data *file,
652 int count,
653 uint8_t level,
654 enum smb_search_data_level data_level)
656 int i;
657 const char *sname;
658 struct multiple_result *data = (struct multiple_result *)private_data;
660 for (i=0; i<count; i++) {
661 sname = extract_name(&file[i], level, data_level);
662 if (!strcmp(sname, ".") || !(strcmp(sname, "..")))
663 continue;
664 data->count++;
665 data->list = talloc_realloc(data->tctx,
666 data->list,
667 union smb_search_data,
668 data->count);
669 data->list[data->count-1] = file[i];
671 return true;
674 enum continue_type {CONT_SINGLE, CONT_INDEX, CONT_RESTART};
676 static NTSTATUS multiple_smb2_search(struct smb2_tree *tree,
677 TALLOC_CTX *tctx,
678 const char *pattern,
679 uint8_t level,
680 enum smb_search_data_level data_level,
681 enum continue_type cont_type,
682 void *data,
683 struct smb2_handle *h)
685 struct smb2_find f;
686 bool ret = true;
687 unsigned int count = 0;
688 union smb_search_data *d;
689 NTSTATUS status;
690 struct multiple_result *result = (struct multiple_result *)data;
692 ZERO_STRUCT(f);
693 f.in.file.handle = *h;
694 f.in.pattern = pattern;
695 f.in.max_response_size = 0x1000;
696 f.in.level = level;
698 /* The search should start from the beginning everytime */
699 f.in.continue_flags = SMB2_CONTINUE_FLAG_RESTART;
701 do {
702 status = smb2_find_level(tree, tree, &f, &count, &d);
703 if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES))
704 break;
705 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
706 if (!fill_result(result, d, count, level, data_level)) {
707 return NT_STATUS_UNSUCCESSFUL;
711 * After the first iteration is complete set the CONTINUE
712 * FLAGS appropriately
714 switch (cont_type) {
715 case CONT_INDEX:
716 f.in.continue_flags = SMB2_CONTINUE_FLAG_INDEX;
717 break;
718 case CONT_SINGLE:
719 f.in.continue_flags = SMB2_CONTINUE_FLAG_SINGLE;
720 break;
721 case CONT_RESTART:
722 default:
723 /* we should prevent staying in the loop
724 * forever */
725 f.in.continue_flags = 0;
726 break;
728 } while (count != 0);
729 done:
730 return status;
734 static enum smb_search_data_level compare_data_level;
735 uint8_t level_sort;
737 static int search_compare(union smb_search_data *d1,
738 union smb_search_data *d2)
740 const char *s1, *s2;
742 s1 = extract_name(d1, level_sort, compare_data_level);
743 s2 = extract_name(d2, level_sort, compare_data_level);
744 return strcmp_safe(s1, s2);
748 basic testing of search calls using many files
750 static bool test_many_files(struct torture_context *tctx,
751 struct smb2_tree *tree)
753 TALLOC_CTX *mem_ctx = talloc_new(tctx);
754 const int num_files = 700;
755 int i, t;
756 char *fname;
757 bool ret = true;
758 NTSTATUS status;
759 struct multiple_result result;
760 struct smb2_create create;
761 struct smb2_handle h;
762 struct {
763 const char *name;
764 const char *cont_name;
765 uint8_t level;
766 enum smb_search_data_level data_level;
767 enum continue_type cont_type;
768 } search_types[] = {
769 {"SMB2_FIND_BOTH_DIRECTORY_INFO", "SINGLE", SMB2_FIND_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO, CONT_SINGLE},
770 {"SMB2_FIND_BOTH_DIRECTORY_INFO", "INDEX", SMB2_FIND_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO, CONT_INDEX},
771 {"SMB2_FIND_BOTH_DIRECTORY_INFO", "RESTART", SMB2_FIND_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO, CONT_RESTART},
772 {"SMB2_FIND_DIRECTORY_INFO", "SINGLE", SMB2_FIND_DIRECTORY_INFO, RAW_SEARCH_DATA_DIRECTORY_INFO, CONT_SINGLE},
773 {"SMB2_FIND_DIRECTORY_INFO", "INDEX", SMB2_FIND_DIRECTORY_INFO, RAW_SEARCH_DATA_DIRECTORY_INFO, CONT_INDEX},
774 {"SMB2_FIND_DIRECTORY_INFO", "RESTART", SMB2_FIND_DIRECTORY_INFO, RAW_SEARCH_DATA_DIRECTORY_INFO, CONT_RESTART},
775 {"SMB2_FIND_FULL_DIRECTORY_INFO", "SINGLE", SMB2_FIND_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_FULL_DIRECTORY_INFO, CONT_SINGLE},
776 {"SMB2_FIND_FULL_DIRECTORY_INFO", "INDEX", SMB2_FIND_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_FULL_DIRECTORY_INFO, CONT_INDEX},
777 {"SMB2_FIND_FULL_DIRECTORY_INFO", "RESTART", SMB2_FIND_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_FULL_DIRECTORY_INFO, CONT_RESTART},
778 {"SMB2_FIND_ID_FULL_DIRECTORY_INFO", "SINGLE", SMB2_FIND_ID_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO, CONT_SINGLE},
779 {"SMB2_FIND_ID_FULL_DIRECTORY_INFO", "INDEX", SMB2_FIND_ID_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO, CONT_INDEX},
780 {"SMB2_FIND_ID_FULL_DIRECTORY_INFO", "RESTART", SMB2_FIND_ID_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO, CONT_RESTART},
781 {"SMB2_FIND_ID_BOTH_DIRECTORY_INFO", "SINGLE", SMB2_FIND_ID_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO, CONT_SINGLE},
782 {"SMB2_FIND_ID_BOTH_DIRECTORY_INFO", "INDEX", SMB2_FIND_ID_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO, CONT_INDEX},
783 {"SMB2_FIND_ID_BOTH_DIRECTORY_INFO", "RESTART", SMB2_FIND_ID_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO, CONT_RESTART}
786 smb2_deltree(tree, DNAME);
787 status = torture_smb2_testdir(tree, DNAME, &h);
788 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
790 torture_comment(tctx, "Testing with %d files\n", num_files);
791 ZERO_STRUCT(create);
792 create.in.desired_access = SEC_RIGHTS_FILE_ALL;
793 create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
794 create.in.create_disposition = NTCREATEX_DISP_CREATE;
796 for (i=num_files-1;i>=0;i--) {
797 fname = talloc_asprintf(mem_ctx, DNAME "\\t%03d-%d.txt", i, i);
798 create.in.fname = talloc_asprintf(mem_ctx, "%s", fname);
799 status = smb2_create(tree, mem_ctx, &create);
800 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
801 smb2_util_close(tree, create.out.file.handle);
802 talloc_free(fname);
805 for (t=0;t<ARRAY_SIZE(search_types);t++) {
806 ZERO_STRUCT(result);
807 result.tctx = talloc_new(tctx);
809 torture_comment(tctx,
810 "Continue %s via %s\n", search_types[t].name,
811 search_types[t].cont_name);
812 status = multiple_smb2_search(tree, tctx, "*",
813 search_types[t].level,
814 search_types[t].data_level,
815 search_types[t].cont_type,
816 &result, &h);
818 torture_assert_int_equal_goto(tctx, result.count, num_files,
819 ret, done, "");
821 compare_data_level = search_types[t].data_level;
822 level_sort = search_types[t].level;
824 TYPESAFE_QSORT(result.list, result.count, search_compare);
826 for (i=0;i<result.count;i++) {
827 const char *s;
828 enum smb_search_level level;
829 level = RAW_SEARCH_SMB2;
830 s = extract_name(&result.list[i],
831 search_types[t].level,
832 compare_data_level);
833 fname = talloc_asprintf(mem_ctx, "t%03d-%d.txt", i, i);
834 torture_assert_str_equal_goto(tctx, s, fname, ret,
835 done, "Incorrect name");
836 talloc_free(fname);
838 talloc_free(result.tctx);
841 done:
842 smb2_util_close(tree, h);
843 smb2_deltree(tree, DNAME);
844 talloc_free(mem_ctx);
846 return ret;
850 check an individual file result
852 static bool check_result(struct torture_context *tctx,
853 struct multiple_result *result,
854 const char *name,
855 bool exist,
856 uint32_t attrib)
858 int i;
859 for (i=0;i<result->count;i++) {
860 if (strcmp(name,
861 result->list[i].both_directory_info.name.s) == 0) {
862 break;
865 if (i == result->count) {
866 if (exist) {
867 torture_result(tctx, TORTURE_FAIL,
868 "failed: '%s' should exist with attribute %s\n",
869 name, attrib_string(result->list, attrib));
870 return false;
872 return true;
875 if (!exist) {
876 torture_result(tctx, TORTURE_FAIL,
877 "failed: '%s' should NOT exist (has attribute %s)\n",
878 name, attrib_string(result->list,
879 result->list[i].both_directory_info.attrib));
880 return false;
883 if ((result->list[i].both_directory_info.attrib&0xFFF) != attrib) {
884 torture_result(tctx, TORTURE_FAIL,
885 "failed: '%s' should have attribute 0x%x (has 0x%x)\n",
886 name, attrib, result->list[i].both_directory_info.attrib);
887 return false;
889 return true;
893 test what happens when the directory is modified during a search
895 static bool test_modify_search(struct torture_context *tctx,
896 struct smb2_tree *tree)
898 int num_files = 700;
899 struct multiple_result result;
900 union smb_setfileinfo sfinfo;
901 TALLOC_CTX *mem_ctx = talloc_new(tctx);
902 struct smb2_create create;
903 struct smb2_handle h;
904 struct smb2_find f;
905 union smb_search_data *d;
906 struct file_elem files[702] = {};
907 NTSTATUS status;
908 bool ret = true;
909 int i;
910 unsigned int count;
912 smb2_deltree(tree, DNAME);
914 status = torture_smb2_testdir(tree, DNAME, &h);
915 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
917 torture_comment(tctx, "Creating %d files\n", num_files);
919 ZERO_STRUCT(create);
920 create.in.desired_access = SEC_RIGHTS_FILE_ALL;
921 create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
922 create.in.create_disposition = NTCREATEX_DISP_CREATE;
924 for (i = num_files-1; i >= 0; i--) {
925 files[i].name = talloc_asprintf(mem_ctx, "t%03d-%d.txt", i, i);
926 create.in.fname = talloc_asprintf(mem_ctx, "%s\\%s",
927 DNAME, files[i].name);
928 status = smb2_create(tree, mem_ctx, &create);
929 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
930 smb2_util_close(tree, create.out.file.handle);
933 torture_comment(tctx, "pulling the first two files\n");
934 ZERO_STRUCT(result);
935 result.tctx = talloc_new(tctx);
937 ZERO_STRUCT(f);
938 f.in.file.handle = h;
939 f.in.pattern = "*";
940 f.in.continue_flags = SMB2_CONTINUE_FLAG_SINGLE;
941 f.in.max_response_size = 0x100;
942 f.in.level = SMB2_FIND_BOTH_DIRECTORY_INFO;
944 do {
945 status = smb2_find_level(tree, tree, &f, &count, &d);
946 if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES))
947 break;
948 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
949 if (!fill_result(&result, d, count, f.in.level,
950 RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO)) {
951 ret = false;
952 goto done;
954 } while (result.count < 2);
956 torture_comment(tctx, "Changing attributes and deleting\n");
958 ZERO_STRUCT(create);
959 create.in.desired_access = SEC_RIGHTS_FILE_ALL;
960 create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
961 create.in.create_disposition = NTCREATEX_DISP_CREATE;
963 files[num_files].name = talloc_asprintf(mem_ctx, "T003-03.txt.2");
964 create.in.fname = talloc_asprintf(mem_ctx, "%s\\%s", DNAME,
965 files[num_files].name);
966 status = smb2_create(tree, mem_ctx, &create);
967 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
968 smb2_util_close(tree, create.out.file.handle);
970 ZERO_STRUCT(create);
971 create.in.desired_access = SEC_RIGHTS_FILE_ALL;
972 create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
973 create.in.create_disposition = NTCREATEX_DISP_CREATE;
975 files[num_files + 1].name = talloc_asprintf(mem_ctx, "T013-13.txt.2");
976 create.in.fname = talloc_asprintf(mem_ctx, "%s\\%s", DNAME,
977 files[num_files + 1].name);
978 status = smb2_create(tree, mem_ctx, &create);
979 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
980 smb2_util_close(tree, create.out.file.handle);
982 files[num_files + 2].name = talloc_asprintf(mem_ctx, "T013-13.txt.3");
983 status = smb2_create_complex_file(tree, DNAME "\\T013-13.txt.3", &h);
984 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
986 smb2_util_unlink(tree, DNAME "\\T014-14.txt");
987 smb2_util_setatr(tree, DNAME "\\T015-15.txt", FILE_ATTRIBUTE_HIDDEN);
988 smb2_util_setatr(tree, DNAME "\\T016-16.txt", FILE_ATTRIBUTE_NORMAL);
989 smb2_util_setatr(tree, DNAME "\\T017-17.txt", FILE_ATTRIBUTE_SYSTEM);
990 smb2_util_setatr(tree, DNAME "\\T018-18.txt", 0);
991 smb2_util_setatr(tree, DNAME "\\T039-39.txt", FILE_ATTRIBUTE_HIDDEN);
992 smb2_util_setatr(tree, DNAME "\\T000-0.txt", FILE_ATTRIBUTE_HIDDEN);
993 sfinfo.generic.level = RAW_SFILEINFO_DISPOSITION_INFORMATION;
994 sfinfo.generic.in.file.path = DNAME "\\T013-13.txt.3";
995 sfinfo.disposition_info.in.delete_on_close = 1;
996 status = smb2_composite_setpathinfo(tree, &sfinfo);
997 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
999 /* Reset the numfiles to include the new files and start the
1000 * search from the beginning */
1001 num_files = num_files + 2;
1002 f.in.pattern = "*";
1003 f.in.continue_flags = SMB2_CONTINUE_FLAG_RESTART;
1004 result.count = 0;
1006 do {
1007 status = smb2_find_level(tree, tree, &f, &count, &d);
1008 if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES))
1009 break;
1010 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
1011 if (!fill_result(&result, d, count, f.in.level,
1012 RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO)) {
1013 ret = false;
1014 goto done;
1016 f.in.continue_flags = 0;
1017 f.in.max_response_size = 4096;
1018 } while (count != 0);
1021 ret &= check_result(tctx, &result, "t039-39.txt", true, FILE_ATTRIBUTE_HIDDEN);
1022 ret &= check_result(tctx, &result, "t000-0.txt", true, FILE_ATTRIBUTE_HIDDEN);
1023 ret &= check_result(tctx, &result, "t014-14.txt", false, 0);
1024 ret &= check_result(tctx, &result, "t015-15.txt", true, FILE_ATTRIBUTE_HIDDEN);
1025 ret &= check_result(tctx, &result, "t016-16.txt", true, FILE_ATTRIBUTE_NORMAL);
1026 ret &= check_result(tctx, &result, "t017-17.txt", true, FILE_ATTRIBUTE_SYSTEM);
1027 ret &= check_result(tctx, &result, "t018-18.txt", true, FILE_ATTRIBUTE_ARCHIVE);
1028 ret &= check_result(tctx, &result, "t019-19.txt", true, FILE_ATTRIBUTE_ARCHIVE);
1029 ret &= check_result(tctx, &result, "T013-13.txt.2", true, FILE_ATTRIBUTE_ARCHIVE);
1030 ret &= check_result(tctx, &result, "T003-3.txt.2", false, 0);
1031 ret &= check_result(tctx, &result, "T013-13.txt.3", true, FILE_ATTRIBUTE_NORMAL);
1033 if (!ret) {
1034 for (i=0;i<result.count;i++) {
1035 torture_warning(tctx, "%s %s (0x%x)\n",
1036 result.list[i].both_directory_info.name.s,
1037 attrib_string(tctx,
1038 result.list[i].both_directory_info.attrib),
1039 result.list[i].both_directory_info.attrib);
1042 done:
1043 smb2_util_close(tree, h);
1044 smb2_deltree(tree, DNAME);
1045 talloc_free(mem_ctx);
1047 return ret;
1051 testing if directories always come back sorted
1053 static bool test_sorted(struct torture_context *tctx,
1054 struct smb2_tree *tree)
1056 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1057 const int num_files = 700;
1058 int i;
1059 struct file_elem files[700] = {};
1060 bool ret = true;
1061 NTSTATUS status;
1062 struct multiple_result result;
1063 struct smb2_handle h;
1065 torture_comment(tctx, "Testing if directories always come back "
1066 "sorted\n");
1067 status = populate_tree(tctx, mem_ctx, tree, files, num_files, &h);
1068 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
1070 ZERO_STRUCT(result);
1071 result.tctx = tctx;
1073 status = multiple_smb2_search(tree, tctx, "*",
1074 SMB2_FIND_BOTH_DIRECTORY_INFO,
1075 RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO,
1076 SMB2_CONTINUE_FLAG_SINGLE,
1077 &result, &h);
1079 torture_assert_int_equal_goto(tctx, result.count, num_files, ret, done,
1080 "");
1082 for (i=0;i<num_files-1;i++) {
1083 const char *name1, *name2;
1084 name1 = result.list[i].both_directory_info.name.s;
1085 name2 = result.list[i+1].both_directory_info.name.s;
1086 if (strcasecmp_m(name1, name2) > 0) {
1087 torture_comment(tctx, "non-alphabetical order at entry "
1088 "%d '%s' '%s'\n", i, name1, name2);
1089 torture_comment(tctx,
1090 "Server does not produce sorted directory listings"
1091 "(not an error)\n");
1092 goto done;
1095 talloc_free(result.list);
1096 done:
1097 smb2_util_close(tree, h);
1098 smb2_deltree(tree, DNAME);
1099 talloc_free(mem_ctx);
1101 return ret;
1104 /* test the behavior of file_index field in the SMB2_FIND struct */
1105 static bool test_file_index(struct torture_context *tctx,
1106 struct smb2_tree *tree)
1108 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1109 const int num_files = 100;
1110 int resume_index = 4;
1111 int i;
1112 char *fname;
1113 bool ret = true;
1114 NTSTATUS status;
1115 struct multiple_result result;
1116 struct smb2_create create;
1117 struct smb2_find f;
1118 struct smb2_handle h;
1119 union smb_search_data *d;
1120 unsigned count;
1122 smb2_deltree(tree, DNAME);
1124 status = torture_smb2_testdir(tree, DNAME, &h);
1125 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
1127 torture_comment(tctx, "Testing the behavior of file_index flag\n");
1129 ZERO_STRUCT(create);
1130 create.in.desired_access = SEC_RIGHTS_FILE_ALL;
1131 create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1132 create.in.create_disposition = NTCREATEX_DISP_CREATE;
1133 for (i = num_files-1; i >= 0; i--) {
1134 fname = talloc_asprintf(mem_ctx, DNAME "\\file%u.txt", i);
1135 create.in.fname = fname;
1136 status = smb2_create(tree, mem_ctx, &create);
1137 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
1138 talloc_free(fname);
1139 smb2_util_close(tree, create.out.file.handle);
1142 ZERO_STRUCT(result);
1143 result.tctx = tctx;
1145 ZERO_STRUCT(f);
1146 f.in.file.handle = h;
1147 f.in.pattern = "*";
1148 f.in.continue_flags = SMB2_CONTINUE_FLAG_SINGLE;
1149 f.in.max_response_size = 0x1000;
1150 f.in.level = SMB2_FIND_FULL_DIRECTORY_INFO;
1152 do {
1153 status = smb2_find_level(tree, tree, &f, &count, &d);
1154 if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES))
1155 break;
1156 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
1157 if (!fill_result(&result, d, count, f.in.level,
1158 RAW_SEARCH_DATA_FULL_DIRECTORY_INFO)) {
1159 ret = false;
1160 goto done;
1162 } while(result.count < 10);
1164 if (result.list[0].full_directory_info.file_index == 0) {
1165 torture_skip_goto(tctx, done,
1166 "Talking to a server that doesn't provide a "
1167 "file index.\nWindows servers using NTFS do "
1168 "not provide a file_index. Skipping test\n");
1169 } else {
1170 /* We are not talking to a Windows based server. Windows
1171 * servers using NTFS do not provide a file_index. Windows
1172 * servers using FAT do provide a file index, however in both
1173 * cases they do not honor a file index on a resume request.
1174 * See MS-FSCC <62> and MS-SMB2 <54> for more information. */
1176 /* Set the file_index flag to point to the fifth file from the
1177 * previous enumeration and try to start the subsequent
1178 * searches from that point */
1179 f.in.file_index =
1180 result.list[resume_index].full_directory_info.file_index;
1181 f.in.continue_flags = SMB2_CONTINUE_FLAG_INDEX;
1183 /* get the name of the next expected file */
1184 fname = talloc_asprintf(mem_ctx, DNAME "\\%s",
1185 result.list[resume_index].full_directory_info.name.s);
1187 ZERO_STRUCT(result);
1188 result.tctx = tctx;
1189 status = smb2_find_level(tree, tree, &f, &count, &d);
1190 if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES))
1191 goto done;
1192 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
1193 if (!fill_result(&result, d, count, f.in.level,
1194 RAW_SEARCH_DATA_FULL_DIRECTORY_INFO)) {
1195 ret = false;
1196 goto done;
1198 if (strcmp(fname,
1199 result.list[0].full_directory_info.name.s)) {
1200 torture_comment(tctx, "Next expected file: %s but the "
1201 "server returned %s\n", fname,
1202 result.list[0].full_directory_info.name.s);
1203 torture_comment(tctx,
1204 "Not an error. Resuming using a file "
1205 "index is an optional feature of the "
1206 "protocol.\n");
1207 goto done;
1210 done:
1211 smb2_util_close(tree, h);
1212 smb2_deltree(tree, DNAME);
1213 talloc_free(mem_ctx);
1215 return ret;
1219 * Tests directory enumeration in a directory containing >1000 files with
1220 * names of varying lengths.
1222 static bool test_large_files(struct torture_context *tctx,
1223 struct smb2_tree *tree)
1225 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1226 const int num_files = 2000;
1227 int max_len = 200;
1228 /* These should be evenly divisible */
1229 int num_at_len = num_files / max_len;
1230 struct file_elem files[2000] = {};
1231 size_t len = 1;
1232 bool ret = true;
1233 NTSTATUS status;
1234 struct smb2_create create;
1235 struct smb2_find f;
1236 struct smb2_handle h;
1237 union smb_search_data *d;
1238 int i, j, file_count = 0;
1239 char **strs = NULL;
1240 unsigned count;
1242 torture_comment(tctx,
1243 "Testing directory enumeration in a directory with >1000 files\n");
1245 smb2_deltree(tree, DNAME);
1247 ZERO_STRUCT(create);
1248 create.in.desired_access = SEC_RIGHTS_DIR_ALL;
1249 create.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1250 create.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
1251 create.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1252 NTCREATEX_SHARE_ACCESS_WRITE |
1253 NTCREATEX_SHARE_ACCESS_DELETE;
1254 create.in.create_disposition = NTCREATEX_DISP_CREATE;
1255 create.in.fname = DNAME;
1257 status = smb2_create(tree, mem_ctx, &create);
1258 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
1259 h = create.out.file.handle;
1261 ZERO_STRUCT(create);
1262 create.in.desired_access = SEC_RIGHTS_FILE_ALL;
1263 create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1264 create.in.create_disposition = NTCREATEX_DISP_CREATE;
1266 for (i = 0; i < num_files; i++) {
1267 if (i % num_at_len == 0) {
1268 strs = generate_unique_strs(mem_ctx, len, num_at_len);
1269 len++;
1271 files[i].name = strs[i % num_at_len];
1272 create.in.fname = talloc_asprintf(mem_ctx, "%s\\%s",
1273 DNAME, files[i].name);
1274 status = smb2_create(tree, mem_ctx, &create);
1275 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
1276 smb2_util_close(tree, create.out.file.handle);
1279 ZERO_STRUCT(f);
1280 f.in.file.handle = h;
1281 f.in.pattern = "*";
1282 f.in.max_response_size = 0x100;
1283 f.in.level = SMB2_FIND_BOTH_DIRECTORY_INFO;
1285 do {
1286 status = smb2_find_level(tree, tree, &f, &count, &d);
1287 if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES))
1288 break;
1289 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
1291 for (i = 0; i < count; i++) {
1292 bool expected;
1293 const char *found = d[i].both_directory_info.name.s;
1295 if (!strcmp(found, ".") || !strcmp(found, ".."))
1296 continue;
1298 expected = false;
1299 for (j = 0; j < 2000; j++) {
1300 if (!strcmp(files[j].name, found)) {
1301 files[j].found = true;
1302 expected = true;
1303 break;
1307 if (expected)
1308 continue;
1310 torture_result(tctx, TORTURE_FAIL,
1311 "(%s): didn't expect %s\n",
1312 __location__, found);
1313 ret = false;
1314 goto done;
1316 file_count = file_count + i;
1317 f.in.continue_flags = 0;
1318 f.in.max_response_size = 4096;
1319 } while (count != 0);
1321 torture_assert_int_equal_goto(tctx, file_count, num_files + 2, ret,
1322 done, "");
1324 for (i = 0; i < num_files; i++) {
1325 if (files[j].found)
1326 continue;
1328 torture_result(tctx, TORTURE_FAIL,
1329 "(%s): expected to find %s, but didn't\n",
1330 __location__, files[j].name);
1331 ret = false;
1332 goto done;
1334 done:
1335 smb2_util_close(tree, h);
1336 smb2_deltree(tree, DNAME);
1337 talloc_free(mem_ctx);
1339 return ret;
1342 struct torture_suite *torture_smb2_dir_init(void)
1344 struct torture_suite *suite =
1345 torture_suite_create(talloc_autofree_context(), "dir");
1347 torture_suite_add_1smb2_test(suite, "find", test_find);
1348 torture_suite_add_1smb2_test(suite, "fixed", test_fixed);
1349 torture_suite_add_1smb2_test(suite, "one", test_one_file);
1350 torture_suite_add_1smb2_test(suite, "many", test_many_files);
1351 torture_suite_add_1smb2_test(suite, "modify", test_modify_search);
1352 torture_suite_add_1smb2_test(suite, "sorted", test_sorted);
1353 torture_suite_add_1smb2_test(suite, "file-index", test_file_index);
1354 torture_suite_add_1smb2_test(suite, "large-files", test_large_files);
1355 suite->description = talloc_strdup(suite, "SMB2-DIR tests");
1357 return suite;