subunit: Support formatting compatible with upstream subunit, for consistency.
[Samba/gebeck_regimport.git] / source4 / torture / smb2 / dir.c
blobdbd1a19a45a20848a24c906e10a50f81ecfbfa7c
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 bool found;
47 static NTSTATUS populate_tree(struct torture_context *tctx,
48 TALLOC_CTX *mem_ctx,
49 struct smb2_tree *tree,
50 struct file_elem *files,
51 int nfiles,
52 struct smb2_handle *h_out)
54 struct smb2_create create;
55 char **strs = NULL;
56 NTSTATUS status;
57 bool ret;
58 int i;
60 smb2_deltree(tree, DNAME);
62 ZERO_STRUCT(create);
63 create.in.desired_access = SEC_RIGHTS_DIR_ALL;
64 create.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
65 create.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
66 create.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
67 NTCREATEX_SHARE_ACCESS_WRITE |
68 NTCREATEX_SHARE_ACCESS_DELETE;
69 create.in.create_disposition = NTCREATEX_DISP_CREATE;
70 create.in.fname = DNAME;
72 status = smb2_create(tree, mem_ctx, &create);
73 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
74 *h_out = create.out.file.handle;
76 ZERO_STRUCT(create);
77 create.in.desired_access = SEC_RIGHTS_FILE_ALL;
78 create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
79 create.in.create_disposition = NTCREATEX_DISP_CREATE;
81 strs = generate_unique_strs(mem_ctx, 8, nfiles);
82 if (strs == NULL) {
83 status = NT_STATUS_OBJECT_NAME_COLLISION;
84 goto done;
86 for (i = 0; i < nfiles; i++) {
87 files[i].name = strs[i];
88 create.in.fname = talloc_asprintf(mem_ctx, "%s\\%s",
89 DNAME, files[i].name);
90 status = smb2_create(tree, mem_ctx, &create);
91 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
92 smb2_util_close(tree, create.out.file.handle);
94 done:
95 return status;
99 test find continue
102 static bool test_find(struct torture_context *tctx,
103 struct smb2_tree *tree)
105 TALLOC_CTX *mem_ctx = talloc_new(tctx);
106 struct smb2_handle h;
107 struct smb2_find f;
108 union smb_search_data *d;
109 struct file_elem files[NFILES] = {};
110 NTSTATUS status;
111 bool ret = true;
112 unsigned int count;
113 int i, j, file_count = 0;
115 status = populate_tree(tctx, mem_ctx, tree, files, NFILES, &h);
117 ZERO_STRUCT(f);
118 f.in.file.handle = h;
119 f.in.pattern = "*";
120 f.in.continue_flags = SMB2_CONTINUE_FLAG_SINGLE;
121 f.in.max_response_size = 0x100;
122 f.in.level = SMB2_FIND_BOTH_DIRECTORY_INFO;
124 do {
125 status = smb2_find_level(tree, tree, &f, &count, &d);
126 if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES))
127 break;
128 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
130 for (i = 0; i < count; i++) {
131 bool expected;
132 const char *found = d[i].both_directory_info.name.s;
134 if (!strcmp(found, ".") || !strcmp(found, ".."))
135 continue;
137 expected = false;
138 for (j = 0; j < NFILES; j++) {
139 if (!strcmp(files[j].name, found)) {
140 files[j].found = true;
141 expected = true;
142 break;
146 if (expected)
147 continue;
149 torture_result(tctx, TORTURE_FAIL,
150 "(%s): didn't expect %s\n",
151 __location__, found);
152 ret = false;
153 goto done;
156 file_count = file_count + i;
157 f.in.continue_flags = 0;
158 f.in.max_response_size = 4096;
159 } while (count != 0);
161 torture_assert_int_equal_goto(tctx, file_count, NFILES + 2, ret, done,
162 "");
164 for (i = 0; i < NFILES; i++) {
165 if (files[j].found)
166 continue;
168 torture_result(tctx, TORTURE_FAIL,
169 "(%s): expected to find %s, but didn't\n",
170 __location__, files[j].name);
171 ret = false;
172 goto done;
175 done:
176 smb2_deltree(tree, DNAME);
177 talloc_free(mem_ctx);
179 return ret;
183 test fixed enumeration
186 static bool test_fixed(struct torture_context *tctx,
187 struct smb2_tree *tree)
189 TALLOC_CTX *mem_ctx = talloc_new(tctx);
190 struct smb2_create create;
191 struct smb2_handle h, h2;
192 struct smb2_find f;
193 union smb_search_data *d;
194 struct file_elem files[NFILES] = {};
195 NTSTATUS status;
196 bool ret = true;
197 unsigned int count;
198 int i;
200 status = populate_tree(tctx, mem_ctx, tree, files, NFILES, &h);
202 ZERO_STRUCT(create);
203 create.in.desired_access = SEC_RIGHTS_DIR_ALL;
204 create.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
205 create.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
206 create.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
207 NTCREATEX_SHARE_ACCESS_WRITE |
208 NTCREATEX_SHARE_ACCESS_DELETE;
209 create.in.create_disposition = NTCREATEX_DISP_OPEN;
210 create.in.fname = DNAME;
212 status = smb2_create(tree, mem_ctx, &create);
213 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
214 h2 = create.out.file.handle;
216 ZERO_STRUCT(f);
217 f.in.file.handle = h;
218 f.in.pattern = "*";
219 f.in.continue_flags = SMB2_CONTINUE_FLAG_SINGLE;
220 f.in.max_response_size = 0x100;
221 f.in.level = SMB2_FIND_BOTH_DIRECTORY_INFO;
223 /* Start enumeration on h, then delete all from h2 */
224 status = smb2_find_level(tree, tree, &f, &count, &d);
225 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
227 f.in.file.handle = h2;
229 do {
230 status = smb2_find_level(tree, tree, &f, &count, &d);
231 if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES))
232 break;
233 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
235 for (i = 0; i < count; i++) {
236 const char *found = d[i].both_directory_info.name.s;
237 char *path = talloc_asprintf(mem_ctx, "%s\\%s",
238 DNAME, found);
240 if (!strcmp(found, ".") || !strcmp(found, ".."))
241 continue;
243 status = smb2_util_unlink(tree, path);
244 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
245 "");
247 talloc_free(path);
250 f.in.continue_flags = 0;
251 f.in.max_response_size = 4096;
252 } while (count != 0);
254 /* Now finish h enumeration. */
255 f.in.file.handle = h;
257 do {
258 status = smb2_find_level(tree, tree, &f, &count, &d);
259 if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES))
260 break;
261 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
263 for (i = 0; i < count; i++) {
264 const char *found = d[i].both_directory_info.name.s;
266 if (!strcmp(found, ".") || !strcmp(found, ".."))
267 continue;
269 torture_result(tctx, TORTURE_FAIL,
270 "(%s): didn't expect %s (count=%u)\n",
271 __location__, found, count);
272 ret = false;
273 goto done;
276 f.in.continue_flags = 0;
277 f.in.max_response_size = 4096;
278 } while (count != 0);
280 done:
281 smb2_util_close(tree, h);
282 smb2_util_close(tree, h2);
283 smb2_deltree(tree, DNAME);
284 talloc_free(mem_ctx);
286 return ret;
289 static struct {
290 const char *name;
291 uint8_t level;
292 enum smb_search_data_level data_level;
293 int name_offset;
294 int resume_key_offset;
295 uint32_t capability_mask;
296 NTSTATUS status;
297 union smb_search_data data;
298 } levels[] = {
299 {"SMB2_FIND_DIRECTORY_INFO",
300 SMB2_FIND_DIRECTORY_INFO, RAW_SEARCH_DATA_DIRECTORY_INFO,
301 offsetof(union smb_search_data, directory_info.name.s),
302 offsetof(union smb_search_data, directory_info.file_index),
304 {"SMB2_FIND_FULL_DIRECTORY_INFO",
305 SMB2_FIND_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_FULL_DIRECTORY_INFO,
306 offsetof(union smb_search_data, full_directory_info.name.s),
307 offsetof(union smb_search_data, full_directory_info.file_index),
309 {"SMB2_FIND_NAME_INFO",
310 SMB2_FIND_NAME_INFO, RAW_SEARCH_DATA_NAME_INFO,
311 offsetof(union smb_search_data, name_info.name.s),
312 offsetof(union smb_search_data, name_info.file_index),
314 {"SMB2_FIND_BOTH_DIRECTORY_INFO",
315 SMB2_FIND_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO,
316 offsetof(union smb_search_data, both_directory_info.name.s),
317 offsetof(union smb_search_data, both_directory_info.file_index),
319 {"SMB2_FIND_ID_FULL_DIRECTORY_INFO",
320 SMB2_FIND_ID_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO,
321 offsetof(union smb_search_data, id_full_directory_info.name.s),
322 offsetof(union smb_search_data, id_full_directory_info.file_index),
324 {"SMB2_FIND_ID_BOTH_DIRECTORY_INFO",
325 SMB2_FIND_ID_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO,
326 offsetof(union smb_search_data, id_both_directory_info.name.s),
327 offsetof(union smb_search_data, id_both_directory_info.file_index),
332 extract the name from a smb_data structure and level
334 static const char *extract_name(union smb_search_data *data,
335 uint8_t level,
336 enum smb_search_data_level data_level)
338 int i;
339 for (i=0;i<ARRAY_SIZE(levels);i++) {
340 if (level == levels[i].level &&
341 data_level == levels[i].data_level) {
342 return *(const char **)(levels[i].name_offset + (char *)data);
345 return NULL;
348 /* find a level in the table by name */
349 static union smb_search_data *find(const char *name)
351 int i;
352 for (i=0;i<ARRAY_SIZE(levels);i++) {
353 if (NT_STATUS_IS_OK(levels[i].status) &&
354 strcmp(levels[i].name, name) == 0) {
355 return &levels[i].data;
358 return NULL;
361 static bool fill_level_data(TALLOC_CTX *mem_ctx,
362 union smb_search_data *data,
363 union smb_search_data *d,
364 unsigned int count,
365 uint8_t level,
366 enum smb_search_data_level data_level)
368 int i;
369 const char *sname = NULL;
370 for (i=0; i < count ; i++) {
371 sname = extract_name(&d[i], level, data_level);
372 if (sname == NULL)
373 return false;
374 if (!strcmp(sname, ".") || !strcmp(sname, ".."))
375 continue;
376 *data = d[i];
378 return true;
382 NTSTATUS torture_single_file_search(struct smb2_tree *tree,
383 TALLOC_CTX *mem_ctx,
384 const char *pattern,
385 uint8_t level,
386 enum smb_search_data_level data_level,
387 int idx,
388 union smb_search_data *d,
389 unsigned int *count,
390 struct smb2_handle *h)
392 struct smb2_find f;
393 NTSTATUS status;
395 ZERO_STRUCT(f);
396 f.in.file.handle = *h;
397 f.in.pattern = pattern;
398 f.in.continue_flags = SMB2_CONTINUE_FLAG_RESTART;
399 f.in.max_response_size = 0x100;
400 f.in.level = level;
402 status = smb2_find_level(tree, tree, &f, count, &d);
403 if (NT_STATUS_IS_OK(status))
404 fill_level_data(mem_ctx, &levels[idx].data, d, *count, level,
405 data_level);
406 return status;
410 basic testing of all File Information Classes using a single file
412 static bool test_one_file(struct torture_context *tctx,
413 struct smb2_tree *tree)
415 TALLOC_CTX *mem_ctx = talloc_new(tctx);
416 bool ret = true;
417 const char *fname = "torture_search.txt";
418 NTSTATUS status;
419 int i;
420 unsigned int count;
421 union smb_fileinfo all_info2, alt_info, internal_info;
422 union smb_search_data *s;
423 union smb_search_data d;
424 struct smb2_handle h, h2;
426 status = torture_smb2_testdir(tree, DNAME, &h);
427 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
429 status = smb2_create_complex_file(tree, DNAME "\\torture_search.txt",
430 &h2);
431 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
433 /* call all the File Information Classes */
434 for (i=0;i<ARRAY_SIZE(levels);i++) {
435 torture_comment(tctx, "Testing %s %d\n", levels[i].name,
436 levels[i].level);
438 levels[i].status = torture_single_file_search(tree, mem_ctx,
439 fname, levels[i].level, levels[i].data_level,
440 i, &d, &count, &h);
441 torture_assert_ntstatus_ok_goto(tctx, levels[i].status, ret,
442 done, "");
445 /* get the all_info file into to check against */
446 all_info2.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
447 all_info2.generic.in.file.handle = h2;
448 status = smb2_getinfo_file(tree, tctx, &all_info2);
449 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
450 "RAW_FILEINFO_ALL_INFO failed");
452 alt_info.generic.level = RAW_FILEINFO_ALT_NAME_INFORMATION;
453 alt_info.generic.in.file.handle = h2;
454 status = smb2_getinfo_file(tree, tctx, &alt_info);
455 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
456 "RAW_FILEINFO_ALT_NAME_INFO failed");
458 internal_info.generic.level = RAW_FILEINFO_INTERNAL_INFORMATION;
459 internal_info.generic.in.file.handle = h2;
460 status = smb2_getinfo_file(tree, tctx, &internal_info);
461 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
462 "RAW_FILEINFO_INTERNAL_INFORMATION "
463 "failed");
465 #define CHECK_VAL(name, sname1, field1, v, sname2, field2) do { \
466 s = find(name); \
467 if (s) { \
468 if ((s->sname1.field1) != (v.sname2.out.field2)) { \
469 torture_result(tctx, TORTURE_FAIL, \
470 "(%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; \
476 }} while (0)
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 torture_result(tctx, TORTURE_FAIL, \
484 "(%s) %s/%s [%s] != %s/%s [%s]\n", \
485 __location__, \
486 #sname1, #field1, \
487 timestring(tctx, s->sname1.field1), \
488 #sname2, #field2, \
489 nt_time_string(tctx, v.sname2.out.field2)); \
490 ret = false; \
492 }} while (0)
494 #define CHECK_NTTIME(name, sname1, field1, v, sname2, field2) do { \
495 s = find(name); \
496 if (s) { \
497 if (s->sname1.field1 != v.sname2.out.field2) { \
498 torture_result(tctx, TORTURE_FAIL, \
499 "(%s) %s/%s [%s] != %s/%s [%s]\n", \
500 __location__, \
501 #sname1, #field1, \
502 nt_time_string(tctx, s->sname1.field1), \
503 #sname2, #field2, \
504 nt_time_string(tctx, v.sname2.out.field2)); \
505 ret = false; \
507 }} while (0)
509 #define CHECK_STR(name, sname1, field1, v, sname2, field2) do { \
510 s = find(name); \
511 if (s) { \
512 if (!s->sname1.field1 || \
513 strcmp(s->sname1.field1, v.sname2.out.field2.s)) { \
514 torture_result(tctx, TORTURE_FAIL, \
515 "(%s) %s/%s [%s] != %s/%s [%s]\n", \
516 __location__, \
517 #sname1, #field1, s->sname1.field1, \
518 #sname2, #field2, v.sname2.out.field2.s); \
519 ret = false; \
521 }} while (0)
523 #define CHECK_WSTR(name, sname1, field1, v, sname2, field2, flags) do { \
524 s = find(name); \
525 if (s) { \
526 if (!s->sname1.field1.s || \
527 strcmp(s->sname1.field1.s, v.sname2.out.field2.s)) { \
528 torture_result(tctx, TORTURE_FAIL, \
529 "(%s) %s/%s [%s] != %s/%s [%s]\n", \
530 __location__, \
531 #sname1, #field1, s->sname1.field1.s, \
532 #sname2, #field2, v.sname2.out.field2.s); \
533 ret = false; \
535 }} while (0)
537 #define CHECK_NAME(name, sname1, field1, fname, flags) do { \
538 s = find(name); \
539 if (s) { \
540 if (!s->sname1.field1.s || \
541 strcmp(s->sname1.field1.s, fname)) { \
542 torture_result(tctx, TORTURE_FAIL, \
543 "(%s) %s/%s [%s] != %s\n", \
544 __location__, \
545 #sname1, #field1, s->sname1.field1.s, fname); \
546 ret = false; \
548 }} while (0)
550 #define CHECK_UNIX_NAME(name, sname1, field1, fname, flags) do { \
551 s = find(name); \
552 if (s) { \
553 if (!s->sname1.field1 || \
554 strcmp(s->sname1.field1, fname)) { \
555 torture_result(tctx, TORTURE_FAIL, \
556 "(%s) %s/%s [%s] != %s\n", \
557 __location__, \
558 #sname1, #field1, s->sname1.field1, fname); \
559 ret = false; \
561 }} while (0)
563 /* check that all the results are as expected */
564 CHECK_VAL("SMB2_FIND_DIRECTORY_INFO", directory_info, attrib, all_info2, all_info2, attrib);
565 CHECK_VAL("SMB2_FIND_FULL_DIRECTORY_INFO", full_directory_info, attrib, all_info2, all_info2, attrib);
566 CHECK_VAL("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info, attrib, all_info2, all_info2, attrib);
567 CHECK_VAL("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info, attrib, all_info2, all_info2, attrib);
568 CHECK_VAL("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info, attrib, all_info2, all_info2, attrib);
570 CHECK_NTTIME("SMB2_FIND_DIRECTORY_INFO", directory_info, write_time, all_info2, all_info2, write_time);
571 CHECK_NTTIME("SMB2_FIND_FULL_DIRECTORY_INFO", full_directory_info, write_time, all_info2, all_info2, write_time);
572 CHECK_NTTIME("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info, write_time, all_info2, all_info2, write_time);
573 CHECK_NTTIME("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info, write_time, all_info2, all_info2, write_time);
574 CHECK_NTTIME("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info, write_time, all_info2, all_info2, write_time);
576 CHECK_NTTIME("SMB2_FIND_DIRECTORY_INFO", directory_info, create_time, all_info2, all_info2, create_time);
577 CHECK_NTTIME("SMB2_FIND_FULL_DIRECTORY_INFO", full_directory_info, create_time, all_info2, all_info2, create_time);
578 CHECK_NTTIME("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info, create_time, all_info2, all_info2, create_time);
579 CHECK_NTTIME("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info, create_time, all_info2, all_info2, create_time);
580 CHECK_NTTIME("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info, create_time, all_info2, all_info2, create_time);
582 CHECK_NTTIME("SMB2_FIND_DIRECTORY_INFO", directory_info, access_time, all_info2, all_info2, access_time);
583 CHECK_NTTIME("SMB2_FIND_FULL_DIRECTORY_INFO", full_directory_info, access_time, all_info2, all_info2, access_time);
584 CHECK_NTTIME("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info, access_time, all_info2, all_info2, access_time);
585 CHECK_NTTIME("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info, access_time, all_info2, all_info2, access_time);
586 CHECK_NTTIME("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info, access_time, all_info2, all_info2, access_time);
588 CHECK_NTTIME("SMB2_FIND_DIRECTORY_INFO", directory_info, change_time, all_info2, all_info2, change_time);
589 CHECK_NTTIME("SMB2_FIND_FULL_DIRECTORY_INFO", full_directory_info, change_time, all_info2, all_info2, change_time);
590 CHECK_NTTIME("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info, change_time, all_info2, all_info2, change_time);
591 CHECK_NTTIME("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info, change_time, all_info2, all_info2, change_time);
592 CHECK_NTTIME("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info, change_time, all_info2, all_info2, change_time);
594 CHECK_VAL("SMB2_FIND_DIRECTORY_INFO", directory_info, size, all_info2, all_info2, size);
595 CHECK_VAL("SMB2_FIND_FULL_DIRECTORY_INFO", full_directory_info, size, all_info2, all_info2, size);
596 CHECK_VAL("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info, size, all_info2, all_info2, size);
597 CHECK_VAL("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info, size, all_info2, all_info2, size);
598 CHECK_VAL("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info, size, all_info2, all_info2, size);
600 CHECK_VAL("SMB2_FIND_DIRECTORY_INFO", directory_info, alloc_size, all_info2, all_info2, alloc_size);
601 CHECK_VAL("SMB2_FIND_FULL_DIRECTORY_INFO", full_directory_info, alloc_size, all_info2, all_info2, alloc_size);
602 CHECK_VAL("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info, alloc_size, all_info2, all_info2, alloc_size);
603 CHECK_VAL("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info, alloc_size, all_info2, all_info2, alloc_size);
604 CHECK_VAL("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info, alloc_size, all_info2, all_info2, alloc_size);
606 CHECK_VAL("SMB2_FIND_FULL_DIRECTORY_INFO", full_directory_info, ea_size, all_info2, all_info2, ea_size);
607 CHECK_VAL("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info, ea_size, all_info2, all_info2, ea_size);
608 CHECK_VAL("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info, ea_size, all_info2, all_info2, ea_size);
609 CHECK_VAL("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info, ea_size, all_info2, all_info2, ea_size);
611 CHECK_NAME("SMB2_FIND_DIRECTORY_INFO", directory_info, name, fname, STR_TERMINATE_ASCII);
612 CHECK_NAME("SMB2_FIND_FULL_DIRECTORY_INFO", full_directory_info, name, fname, STR_TERMINATE_ASCII);
613 CHECK_NAME("SMB2_FIND_NAME_INFO", name_info, name, fname, STR_TERMINATE_ASCII);
614 CHECK_NAME("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info, name, fname, STR_TERMINATE_ASCII);
615 CHECK_NAME("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info, name, fname, STR_TERMINATE_ASCII);
616 CHECK_NAME("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info, name, fname, STR_TERMINATE_ASCII);
618 CHECK_WSTR("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info, short_name, alt_info, alt_name_info, fname, STR_UNICODE);
620 CHECK_VAL("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info, file_id, internal_info, internal_information, file_id);
621 CHECK_VAL("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info, file_id, internal_info, internal_information, file_id);
623 done:
624 smb2_util_close(tree, h);
625 smb2_util_unlink(tree, fname);
626 talloc_free(mem_ctx);
628 return ret;
632 struct multiple_result {
633 TALLOC_CTX *tctx;
634 int count;
635 union smb_search_data *list;
638 bool fill_result(void *private_data,
639 union smb_search_data *file,
640 int count,
641 uint8_t level,
642 enum smb_search_data_level data_level)
644 int i;
645 const char *sname;
646 struct multiple_result *data = (struct multiple_result *)private_data;
648 for (i=0; i<count; i++) {
649 sname = extract_name(&file[i], level, data_level);
650 if (!strcmp(sname, ".") || !(strcmp(sname, "..")))
651 continue;
652 data->count++;
653 data->list = talloc_realloc(data->tctx,
654 data->list,
655 union smb_search_data,
656 data->count);
657 data->list[data->count-1] = file[i];
659 return true;
662 enum continue_type {CONT_SINGLE, CONT_INDEX, CONT_RESTART};
664 static NTSTATUS multiple_smb2_search(struct smb2_tree *tree,
665 TALLOC_CTX *tctx,
666 const char *pattern,
667 uint8_t level,
668 enum smb_search_data_level data_level,
669 enum continue_type cont_type,
670 void *data,
671 struct smb2_handle *h)
673 struct smb2_find f;
674 bool ret = true;
675 unsigned int count = 0;
676 union smb_search_data *d;
677 NTSTATUS status;
678 struct multiple_result *result = (struct multiple_result *)data;
680 ZERO_STRUCT(f);
681 f.in.file.handle = *h;
682 f.in.pattern = pattern;
683 f.in.max_response_size = 0x1000;
684 f.in.level = level;
686 /* The search should start from the beginning everytime */
687 f.in.continue_flags = SMB2_CONTINUE_FLAG_RESTART;
689 do {
690 status = smb2_find_level(tree, tree, &f, &count, &d);
691 if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES))
692 break;
693 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
694 if (!fill_result(result, d, count, level, data_level)) {
695 return NT_STATUS_UNSUCCESSFUL;
699 * After the first iteration is complete set the CONTINUE
700 * FLAGS appropriately
702 switch (cont_type) {
703 case CONT_INDEX:
704 f.in.continue_flags = SMB2_CONTINUE_FLAG_INDEX;
705 break;
706 case CONT_SINGLE:
707 f.in.continue_flags = SMB2_CONTINUE_FLAG_SINGLE;
708 break;
709 case CONT_RESTART:
710 default:
711 /* we should prevent staying in the loop
712 * forever */
713 f.in.continue_flags = 0;
714 break;
716 } while (count != 0);
717 done:
718 return status;
722 static enum smb_search_data_level compare_data_level;
723 uint8_t level_sort;
725 static int search_compare(union smb_search_data *d1,
726 union smb_search_data *d2)
728 const char *s1, *s2;
730 s1 = extract_name(d1, level_sort, compare_data_level);
731 s2 = extract_name(d2, level_sort, compare_data_level);
732 return strcmp_safe(s1, s2);
736 basic testing of search calls using many files
738 static bool test_many_files(struct torture_context *tctx,
739 struct smb2_tree *tree)
741 TALLOC_CTX *mem_ctx = talloc_new(tctx);
742 const int num_files = 700;
743 int i, t;
744 char *fname;
745 bool ret = true;
746 NTSTATUS status;
747 struct multiple_result result;
748 struct smb2_create create;
749 struct smb2_handle h;
750 struct {
751 const char *name;
752 const char *cont_name;
753 uint8_t level;
754 enum smb_search_data_level data_level;
755 enum continue_type cont_type;
756 } search_types[] = {
757 {"SMB2_FIND_BOTH_DIRECTORY_INFO", "SINGLE", SMB2_FIND_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO, CONT_SINGLE},
758 {"SMB2_FIND_BOTH_DIRECTORY_INFO", "INDEX", SMB2_FIND_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO, CONT_INDEX},
759 {"SMB2_FIND_BOTH_DIRECTORY_INFO", "RESTART", SMB2_FIND_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO, CONT_RESTART},
760 {"SMB2_FIND_DIRECTORY_INFO", "SINGLE", SMB2_FIND_DIRECTORY_INFO, RAW_SEARCH_DATA_DIRECTORY_INFO, CONT_SINGLE},
761 {"SMB2_FIND_DIRECTORY_INFO", "INDEX", SMB2_FIND_DIRECTORY_INFO, RAW_SEARCH_DATA_DIRECTORY_INFO, CONT_INDEX},
762 {"SMB2_FIND_DIRECTORY_INFO", "RESTART", SMB2_FIND_DIRECTORY_INFO, RAW_SEARCH_DATA_DIRECTORY_INFO, CONT_RESTART},
763 {"SMB2_FIND_FULL_DIRECTORY_INFO", "SINGLE", SMB2_FIND_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_FULL_DIRECTORY_INFO, CONT_SINGLE},
764 {"SMB2_FIND_FULL_DIRECTORY_INFO", "INDEX", SMB2_FIND_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_FULL_DIRECTORY_INFO, CONT_INDEX},
765 {"SMB2_FIND_FULL_DIRECTORY_INFO", "RESTART", SMB2_FIND_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_FULL_DIRECTORY_INFO, CONT_RESTART},
766 {"SMB2_FIND_ID_FULL_DIRECTORY_INFO", "SINGLE", SMB2_FIND_ID_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO, CONT_SINGLE},
767 {"SMB2_FIND_ID_FULL_DIRECTORY_INFO", "INDEX", SMB2_FIND_ID_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO, CONT_INDEX},
768 {"SMB2_FIND_ID_FULL_DIRECTORY_INFO", "RESTART", SMB2_FIND_ID_FULL_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO, CONT_RESTART},
769 {"SMB2_FIND_ID_BOTH_DIRECTORY_INFO", "SINGLE", SMB2_FIND_ID_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO, CONT_SINGLE},
770 {"SMB2_FIND_ID_BOTH_DIRECTORY_INFO", "INDEX", SMB2_FIND_ID_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO, CONT_INDEX},
771 {"SMB2_FIND_ID_BOTH_DIRECTORY_INFO", "RESTART", SMB2_FIND_ID_BOTH_DIRECTORY_INFO, RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO, CONT_RESTART}
774 smb2_deltree(tree, DNAME);
775 status = torture_smb2_testdir(tree, DNAME, &h);
776 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
778 torture_comment(tctx, "Testing with %d files\n", num_files);
779 ZERO_STRUCT(create);
780 create.in.desired_access = SEC_RIGHTS_FILE_ALL;
781 create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
782 create.in.create_disposition = NTCREATEX_DISP_CREATE;
784 for (i=num_files-1;i>=0;i--) {
785 fname = talloc_asprintf(mem_ctx, DNAME "\\t%03d-%d.txt", i, i);
786 create.in.fname = talloc_asprintf(mem_ctx, "%s", fname);
787 status = smb2_create(tree, mem_ctx, &create);
788 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
789 smb2_util_close(tree, create.out.file.handle);
790 talloc_free(fname);
793 for (t=0;t<ARRAY_SIZE(search_types);t++) {
794 ZERO_STRUCT(result);
795 result.tctx = talloc_new(tctx);
797 torture_comment(tctx,
798 "Continue %s via %s\n", search_types[t].name,
799 search_types[t].cont_name);
800 status = multiple_smb2_search(tree, tctx, "*",
801 search_types[t].level,
802 search_types[t].data_level,
803 search_types[t].cont_type,
804 &result, &h);
806 torture_assert_int_equal_goto(tctx, result.count, num_files,
807 ret, done, "");
809 compare_data_level = search_types[t].data_level;
810 level_sort = search_types[t].level;
812 TYPESAFE_QSORT(result.list, result.count, search_compare);
814 for (i=0;i<result.count;i++) {
815 const char *s;
816 enum smb_search_level level;
817 level = RAW_SEARCH_SMB2;
818 s = extract_name(&result.list[i],
819 search_types[t].level,
820 compare_data_level);
821 fname = talloc_asprintf(mem_ctx, "t%03d-%d.txt", i, i);
822 torture_assert_str_equal_goto(tctx, s, fname, ret,
823 done, "Incorrect name");
824 talloc_free(fname);
826 talloc_free(result.tctx);
829 done:
830 smb2_util_close(tree, h);
831 smb2_deltree(tree, DNAME);
832 talloc_free(mem_ctx);
834 return ret;
838 check an individual file result
840 static bool check_result(struct torture_context *tctx,
841 struct multiple_result *result,
842 const char *name,
843 bool exist,
844 uint32_t attrib)
846 int i;
847 for (i=0;i<result->count;i++) {
848 if (strcmp(name,
849 result->list[i].both_directory_info.name.s) == 0) {
850 break;
853 if (i == result->count) {
854 if (exist) {
855 torture_result(tctx, TORTURE_FAIL,
856 "failed: '%s' should exist with attribute %s\n",
857 name, attrib_string(result->list, attrib));
858 return false;
860 return true;
863 if (!exist) {
864 torture_result(tctx, TORTURE_FAIL,
865 "failed: '%s' should NOT exist (has attribute %s)\n",
866 name, attrib_string(result->list,
867 result->list[i].both_directory_info.attrib));
868 return false;
871 if ((result->list[i].both_directory_info.attrib&0xFFF) != attrib) {
872 torture_result(tctx, TORTURE_FAIL,
873 "failed: '%s' should have attribute 0x%x (has 0x%x)\n",
874 name, attrib, result->list[i].both_directory_info.attrib);
875 return false;
877 return true;
881 test what happens when the directory is modified during a search
883 static bool test_modify_search(struct torture_context *tctx,
884 struct smb2_tree *tree)
886 int num_files = 700;
887 struct multiple_result result;
888 union smb_setfileinfo sfinfo;
889 TALLOC_CTX *mem_ctx = talloc_new(tctx);
890 struct smb2_create create;
891 struct smb2_handle h;
892 struct smb2_find f;
893 union smb_search_data *d;
894 struct file_elem files[700] = {};
895 NTSTATUS status;
896 bool ret = true;
897 int i;
898 unsigned int count;
900 smb2_deltree(tree, DNAME);
902 status = torture_smb2_testdir(tree, DNAME, &h);
903 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
905 torture_comment(tctx, "Creating %d files\n", num_files);
907 ZERO_STRUCT(create);
908 create.in.desired_access = SEC_RIGHTS_FILE_ALL;
909 create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
910 create.in.create_disposition = NTCREATEX_DISP_CREATE;
912 for (i = num_files-1; i >= 0; i--) {
913 files[i].name = talloc_asprintf(mem_ctx, "t%03d-%d.txt", i, i);
914 create.in.fname = talloc_asprintf(mem_ctx, "%s\\%s",
915 DNAME, files[i].name);
916 status = smb2_create(tree, mem_ctx, &create);
917 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
918 smb2_util_close(tree, create.out.file.handle);
921 torture_comment(tctx, "pulling the first two files\n");
922 ZERO_STRUCT(result);
923 result.tctx = talloc_new(tctx);
925 ZERO_STRUCT(f);
926 f.in.file.handle = h;
927 f.in.pattern = "*";
928 f.in.continue_flags = SMB2_CONTINUE_FLAG_SINGLE;
929 f.in.max_response_size = 0x100;
930 f.in.level = SMB2_FIND_BOTH_DIRECTORY_INFO;
932 do {
933 status = smb2_find_level(tree, tree, &f, &count, &d);
934 if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES))
935 break;
936 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
937 if (!fill_result(&result, d, count, f.in.level,
938 RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO)) {
939 ret = false;
940 goto done;
942 } while (result.count < 2);
944 torture_comment(tctx, "Changing attributes and deleting\n");
946 ZERO_STRUCT(create);
947 create.in.desired_access = SEC_RIGHTS_FILE_ALL;
948 create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
949 create.in.create_disposition = NTCREATEX_DISP_CREATE;
951 files[num_files].name = talloc_asprintf(mem_ctx, "T003-03.txt.2");
952 create.in.fname = talloc_asprintf(mem_ctx, "%s\\%s", DNAME,
953 files[num_files].name);
954 status = smb2_create(tree, mem_ctx, &create);
955 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
956 smb2_util_close(tree, create.out.file.handle);
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 + 1].name = talloc_asprintf(mem_ctx, "T013-13.txt.2");
964 create.in.fname = talloc_asprintf(mem_ctx, "%s\\%s", DNAME,
965 files[num_files + 1].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 files[num_files + 2].name = talloc_asprintf(mem_ctx, "T013-13.txt.3");
971 status = smb2_create_complex_file(tree, DNAME "\\T013-13.txt.3", &h);
972 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
974 smb2_util_unlink(tree, DNAME "\\T014-14.txt");
975 smb2_util_setatr(tree, DNAME "\\T015-15.txt", FILE_ATTRIBUTE_HIDDEN);
976 smb2_util_setatr(tree, DNAME "\\T016-16.txt", FILE_ATTRIBUTE_NORMAL);
977 smb2_util_setatr(tree, DNAME "\\T017-17.txt", FILE_ATTRIBUTE_SYSTEM);
978 smb2_util_setatr(tree, DNAME "\\T018-18.txt", 0);
979 smb2_util_setatr(tree, DNAME "\\T039-39.txt", FILE_ATTRIBUTE_HIDDEN);
980 smb2_util_setatr(tree, DNAME "\\T000-0.txt", FILE_ATTRIBUTE_HIDDEN);
981 sfinfo.generic.level = RAW_SFILEINFO_DISPOSITION_INFORMATION;
982 sfinfo.generic.in.file.path = DNAME "\\T013-13.txt.3";
983 sfinfo.disposition_info.in.delete_on_close = 1;
984 status = smb2_composite_setpathinfo(tree, &sfinfo);
985 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
987 /* Reset the numfiles to include the new files and start the
988 * search from the beginning */
989 num_files = num_files + 2;
990 f.in.pattern = "*";
991 f.in.continue_flags = SMB2_CONTINUE_FLAG_RESTART;
992 result.count = 0;
994 do {
995 status = smb2_find_level(tree, tree, &f, &count, &d);
996 if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES))
997 break;
998 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
999 if (!fill_result(&result, d, count, f.in.level,
1000 RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO)) {
1001 ret = false;
1002 goto done;
1004 f.in.continue_flags = 0;
1005 f.in.max_response_size = 4096;
1006 } while (count != 0);
1009 ret &= check_result(tctx, &result, "t039-39.txt", true, FILE_ATTRIBUTE_HIDDEN);
1010 ret &= check_result(tctx, &result, "t000-0.txt", true, FILE_ATTRIBUTE_HIDDEN);
1011 ret &= check_result(tctx, &result, "t014-14.txt", false, 0);
1012 ret &= check_result(tctx, &result, "t015-15.txt", true, FILE_ATTRIBUTE_HIDDEN);
1013 ret &= check_result(tctx, &result, "t016-16.txt", true, FILE_ATTRIBUTE_NORMAL);
1014 ret &= check_result(tctx, &result, "t017-17.txt", true, FILE_ATTRIBUTE_SYSTEM);
1015 ret &= check_result(tctx, &result, "t018-18.txt", true, FILE_ATTRIBUTE_ARCHIVE);
1016 ret &= check_result(tctx, &result, "t019-19.txt", true, FILE_ATTRIBUTE_ARCHIVE);
1017 ret &= check_result(tctx, &result, "T013-13.txt.2", true, FILE_ATTRIBUTE_ARCHIVE);
1018 ret &= check_result(tctx, &result, "T003-3.txt.2", false, 0);
1019 ret &= check_result(tctx, &result, "T013-13.txt.3", true, FILE_ATTRIBUTE_NORMAL);
1021 if (!ret) {
1022 for (i=0;i<result.count;i++) {
1023 torture_warning(tctx, "%s %s (0x%x)\n",
1024 result.list[i].both_directory_info.name.s,
1025 attrib_string(tctx,
1026 result.list[i].both_directory_info.attrib),
1027 result.list[i].both_directory_info.attrib);
1030 done:
1031 smb2_util_close(tree, h);
1032 smb2_deltree(tree, DNAME);
1033 talloc_free(mem_ctx);
1035 return ret;
1039 testing if directories always come back sorted
1041 static bool test_sorted(struct torture_context *tctx,
1042 struct smb2_tree *tree)
1044 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1045 const int num_files = 700;
1046 int i;
1047 struct file_elem files[700] = {};
1048 bool ret = true;
1049 NTSTATUS status;
1050 struct multiple_result result;
1051 struct smb2_handle h;
1053 torture_comment(tctx, "Testing if directories always come back "
1054 "sorted\n");
1055 status = populate_tree(tctx, mem_ctx, tree, files, num_files, &h);
1056 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
1058 ZERO_STRUCT(result);
1059 result.tctx = tctx;
1061 status = multiple_smb2_search(tree, tctx, "*",
1062 SMB2_FIND_BOTH_DIRECTORY_INFO,
1063 RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO,
1064 SMB2_CONTINUE_FLAG_SINGLE,
1065 &result, &h);
1067 torture_assert_int_equal_goto(tctx, result.count, num_files, ret, done,
1068 "");
1070 for (i=0;i<num_files-1;i++) {
1071 const char *name1, *name2;
1072 name1 = result.list[i].both_directory_info.name.s;
1073 name2 = result.list[i+1].both_directory_info.name.s;
1074 if (strcasecmp_m(name1, name2) > 0) {
1075 torture_comment(tctx, "non-alphabetical order at entry "
1076 "%d '%s' '%s'\n", i, name1, name2);
1077 torture_comment(tctx,
1078 "Server does not produce sorted directory listings"
1079 "(not an error)\n");
1080 goto done;
1083 talloc_free(result.list);
1084 done:
1085 smb2_util_close(tree, h);
1086 smb2_deltree(tree, DNAME);
1087 talloc_free(mem_ctx);
1089 return ret;
1092 /* test the behavior of file_index field in the SMB2_FIND struct */
1093 static bool test_file_index(struct torture_context *tctx,
1094 struct smb2_tree *tree)
1096 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1097 const int num_files = 100;
1098 int resume_index = 4;
1099 int i;
1100 char *fname;
1101 bool ret = true;
1102 NTSTATUS status;
1103 struct multiple_result result;
1104 struct smb2_create create;
1105 struct smb2_find f;
1106 struct smb2_handle h;
1107 union smb_search_data *d;
1108 unsigned count;
1110 smb2_deltree(tree, DNAME);
1112 status = torture_smb2_testdir(tree, DNAME, &h);
1113 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
1115 torture_comment(tctx, "Testing the behavior of file_index flag\n");
1117 ZERO_STRUCT(create);
1118 create.in.desired_access = SEC_RIGHTS_FILE_ALL;
1119 create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1120 create.in.create_disposition = NTCREATEX_DISP_CREATE;
1121 for (i = num_files-1; i >= 0; i--) {
1122 fname = talloc_asprintf(mem_ctx, DNAME "\\file%u.txt", i);
1123 create.in.fname = fname;
1124 status = smb2_create(tree, mem_ctx, &create);
1125 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
1126 talloc_free(fname);
1127 smb2_util_close(tree, create.out.file.handle);
1130 ZERO_STRUCT(result);
1131 result.tctx = tctx;
1133 ZERO_STRUCT(f);
1134 f.in.file.handle = h;
1135 f.in.pattern = "*";
1136 f.in.continue_flags = SMB2_CONTINUE_FLAG_SINGLE;
1137 f.in.max_response_size = 0x1000;
1138 f.in.level = SMB2_FIND_FULL_DIRECTORY_INFO;
1140 do {
1141 status = smb2_find_level(tree, tree, &f, &count, &d);
1142 if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES))
1143 break;
1144 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
1145 if (!fill_result(&result, d, count, f.in.level,
1146 RAW_SEARCH_DATA_FULL_DIRECTORY_INFO)) {
1147 ret = false;
1148 goto done;
1150 } while(result.count < 10);
1152 if (result.list[0].full_directory_info.file_index == 0) {
1153 torture_skip_goto(tctx, done,
1154 "Talking to a server that doesn't provide a "
1155 "file index.\nWindows servers using NTFS do "
1156 "not provide a file_index. Skipping test\n");
1157 } else {
1158 /* We are not talking to a Windows based server. Windows
1159 * servers using NTFS do not provide a file_index. Windows
1160 * servers using FAT do provide a file index, however in both
1161 * cases they do not honor a file index on a resume request.
1162 * See MS-FSCC <62> and MS-SMB2 <54> for more information. */
1164 /* Set the file_index flag to point to the fifth file from the
1165 * previous enumeration and try to start the subsequent
1166 * searches from that point */
1167 f.in.file_index =
1168 result.list[resume_index].full_directory_info.file_index;
1169 f.in.continue_flags = SMB2_CONTINUE_FLAG_INDEX;
1171 /* get the name of the next expected file */
1172 fname = talloc_asprintf(mem_ctx, DNAME "\\%s",
1173 result.list[resume_index].full_directory_info.name.s);
1175 ZERO_STRUCT(result);
1176 result.tctx = tctx;
1177 status = smb2_find_level(tree, tree, &f, &count, &d);
1178 if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES))
1179 goto done;
1180 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
1181 if (!fill_result(&result, d, count, f.in.level,
1182 RAW_SEARCH_DATA_FULL_DIRECTORY_INFO)) {
1183 ret = false;
1184 goto done;
1186 if (strcmp(fname,
1187 result.list[0].full_directory_info.name.s)) {
1188 torture_comment(tctx, "Next expected file: %s but the "
1189 "server returned %s\n", fname,
1190 result.list[0].full_directory_info.name.s);
1191 torture_comment(tctx,
1192 "Not an error. Resuming using a file "
1193 "index is an optional feature of the "
1194 "protocol.\n");
1195 goto done;
1198 done:
1199 smb2_util_close(tree, h);
1200 smb2_deltree(tree, DNAME);
1201 talloc_free(mem_ctx);
1203 return ret;
1207 * Tests directory enumeration in a directory containing >1000 files with
1208 * names of varying lengths.
1210 static bool test_large_files(struct torture_context *tctx,
1211 struct smb2_tree *tree)
1213 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1214 const int num_files = 2000;
1215 int max_len = 200;
1216 /* These should be evenly divisible */
1217 int num_at_len = num_files / max_len;
1218 struct file_elem files[2000] = {};
1219 size_t len = 1;
1220 bool ret = true;
1221 NTSTATUS status;
1222 struct smb2_create create;
1223 struct smb2_find f;
1224 struct smb2_handle h;
1225 union smb_search_data *d;
1226 int i, j, file_count = 0;
1227 char **strs = NULL;
1228 unsigned count;
1230 torture_comment(tctx,
1231 "Testing directory enumeration in a directory with >1000 files\n");
1233 smb2_deltree(tree, DNAME);
1235 ZERO_STRUCT(create);
1236 create.in.desired_access = SEC_RIGHTS_DIR_ALL;
1237 create.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
1238 create.in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
1239 create.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
1240 NTCREATEX_SHARE_ACCESS_WRITE |
1241 NTCREATEX_SHARE_ACCESS_DELETE;
1242 create.in.create_disposition = NTCREATEX_DISP_CREATE;
1243 create.in.fname = DNAME;
1245 status = smb2_create(tree, mem_ctx, &create);
1246 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
1247 h = create.out.file.handle;
1249 ZERO_STRUCT(create);
1250 create.in.desired_access = SEC_RIGHTS_FILE_ALL;
1251 create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
1252 create.in.create_disposition = NTCREATEX_DISP_CREATE;
1254 for (i = 0; i < num_files; i++) {
1255 if (i % num_at_len == 0) {
1256 strs = generate_unique_strs(mem_ctx, len, num_at_len);
1257 len++;
1259 files[i].name = strs[i % num_at_len];
1260 create.in.fname = talloc_asprintf(mem_ctx, "%s\\%s",
1261 DNAME, files[i].name);
1262 status = smb2_create(tree, mem_ctx, &create);
1263 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
1264 smb2_util_close(tree, create.out.file.handle);
1267 ZERO_STRUCT(f);
1268 f.in.file.handle = h;
1269 f.in.pattern = "*";
1270 f.in.max_response_size = 0x100;
1271 f.in.level = SMB2_FIND_BOTH_DIRECTORY_INFO;
1273 do {
1274 status = smb2_find_level(tree, tree, &f, &count, &d);
1275 if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES))
1276 break;
1277 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "");
1279 for (i = 0; i < count; i++) {
1280 bool expected;
1281 const char *found = d[i].both_directory_info.name.s;
1283 if (!strcmp(found, ".") || !strcmp(found, ".."))
1284 continue;
1286 expected = false;
1287 for (j = 0; j < 2000; j++) {
1288 if (!strcmp(files[j].name, found)) {
1289 files[j].found = true;
1290 expected = true;
1291 break;
1295 if (expected)
1296 continue;
1298 torture_result(tctx, TORTURE_FAIL,
1299 "(%s): didn't expect %s\n",
1300 __location__, found);
1301 ret = false;
1302 goto done;
1304 file_count = file_count + i;
1305 f.in.continue_flags = 0;
1306 f.in.max_response_size = 4096;
1307 } while (count != 0);
1309 torture_assert_int_equal_goto(tctx, file_count, num_files + 2, ret,
1310 done, "");
1312 for (i = 0; i < num_files; i++) {
1313 if (files[j].found)
1314 continue;
1316 torture_result(tctx, TORTURE_FAIL,
1317 "(%s): expected to find %s, but didn't\n",
1318 __location__, files[j].name);
1319 ret = false;
1320 goto done;
1322 done:
1323 smb2_util_close(tree, h);
1324 smb2_deltree(tree, DNAME);
1325 talloc_free(mem_ctx);
1327 return ret;
1330 struct torture_suite *torture_smb2_dir_init(void)
1332 struct torture_suite *suite =
1333 torture_suite_create(talloc_autofree_context(), "DIR");
1335 torture_suite_add_1smb2_test(suite, "FIND", test_find);
1336 torture_suite_add_1smb2_test(suite, "FIXED", test_fixed);
1337 torture_suite_add_1smb2_test(suite, "ONE", test_one_file);
1338 torture_suite_add_1smb2_test(suite, "MANY", test_many_files);
1339 torture_suite_add_1smb2_test(suite, "MODIFY", test_modify_search);
1340 torture_suite_add_1smb2_test(suite, "SORTED", test_sorted);
1341 torture_suite_add_1smb2_test(suite, "FILE-INDEX", test_file_index);
1342 torture_suite_add_1smb2_test(suite, "LARGE-FILES", test_large_files);
1343 suite->description = talloc_strdup(suite, "SMB2-DIR tests");
1345 return suite;