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/>.
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"
48 static NTSTATUS
populate_tree(struct torture_context
*tctx
,
50 struct smb2_tree
*tree
,
51 struct file_elem
*files
,
53 struct smb2_handle
*h_out
)
55 struct smb2_create create
;
61 smb2_deltree(tree
, DNAME
);
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
;
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
);
84 status
= NT_STATUS_OBJECT_NAME_COLLISION
;
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
);
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
;
110 union smb_search_data
*d
;
111 struct file_elem files
[NFILES
] = {};
115 int i
, j
, file_count
= 0;
117 status
= populate_tree(tctx
, mem_ctx
, tree
, files
, NFILES
, &h
);
120 f
.in
.file
.handle
= h
;
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
;
127 status
= smb2_find_level(tree
, tree
, &f
, &count
, &d
);
128 if (NT_STATUS_EQUAL(status
, STATUS_NO_MORE_FILES
))
130 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
, "");
132 for (i
= 0; i
< count
; i
++) {
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
, ".."))
141 for (j
= 0; j
< NFILES
; j
++) {
142 if (strcmp(files
[j
].name
, found
) != 0) {
146 if (files
[j
].create_time
!= ctime
) {
147 torture_result(tctx
, TORTURE_FAIL
,
148 "(%s): create_time mismatch for %s"
149 "\n", __location__
, found
);
153 files
[j
].found
= true;
161 torture_result(tctx
, TORTURE_FAIL
,
162 "(%s): didn't expect %s\n",
163 __location__
, found
);
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
,
176 for (i
= 0; i
< NFILES
; i
++) {
180 torture_result(tctx
, TORTURE_FAIL
,
181 "(%s): expected to find %s, but didn't\n",
182 __location__
, files
[j
].name
);
188 smb2_deltree(tree
, DNAME
);
189 talloc_free(mem_ctx
);
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
;
205 union smb_search_data
*d
;
206 struct file_elem files
[NFILES
] = {};
212 status
= populate_tree(tctx
, mem_ctx
, tree
, files
, NFILES
, &h
);
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
;
229 f
.in
.file
.handle
= h
;
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
;
242 status
= smb2_find_level(tree
, tree
, &f
, &count
, &d
);
243 if (NT_STATUS_EQUAL(status
, STATUS_NO_MORE_FILES
))
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",
252 if (!strcmp(found
, ".") || !strcmp(found
, ".."))
255 status
= smb2_util_unlink(tree
, path
);
256 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
,
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
;
270 status
= smb2_find_level(tree
, tree
, &f
, &count
, &d
);
271 if (NT_STATUS_EQUAL(status
, STATUS_NO_MORE_FILES
))
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
, ".."))
281 torture_result(tctx
, TORTURE_FAIL
,
282 "(%s): didn't expect %s (count=%u)\n",
283 __location__
, found
, count
);
288 f
.in
.continue_flags
= 0;
289 f
.in
.max_response_size
= 4096;
290 } while (count
!= 0);
293 smb2_util_close(tree
, h
);
294 smb2_util_close(tree
, h2
);
295 smb2_deltree(tree
, DNAME
);
296 talloc_free(mem_ctx
);
304 enum smb_search_data_level data_level
;
306 int resume_key_offset
;
307 uint32_t capability_mask
;
309 union smb_search_data data
;
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
,
348 enum smb_search_data_level data_level
)
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
);
360 /* find a level in the table by name */
361 static union smb_search_data
*find(const char *name
)
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
;
373 static bool fill_level_data(TALLOC_CTX
*mem_ctx
,
374 union smb_search_data
*data
,
375 union smb_search_data
*d
,
378 enum smb_search_data_level data_level
)
381 const char *sname
= NULL
;
382 for (i
=0; i
< count
; i
++) {
383 sname
= extract_name(&d
[i
], level
, data_level
);
386 if (!strcmp(sname
, ".") || !strcmp(sname
, ".."))
394 NTSTATUS
torture_single_file_search(struct smb2_tree
*tree
,
398 enum smb_search_data_level data_level
,
400 union smb_search_data
*d
,
402 struct smb2_handle
*h
)
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;
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
,
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
);
429 const char *fname
= "torture_search.txt";
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",
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
,
450 levels
[i
].status
= torture_single_file_search(tree
, mem_ctx
,
451 fname
, levels
[i
].level
, levels
[i
].data_level
,
453 torture_assert_ntstatus_ok_goto(tctx
, levels
[i
].status
, ret
,
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 "
477 #define CHECK_VAL(name, sname1, field1, v, sname2, field2) do { \
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", \
484 #sname1, #field1, (int)s->sname1.field1, \
485 #sname2, #field2, (int)v.sname2.out.field2); \
490 #define CHECK_TIME(name, sname1, field1, v, sname2, field2) do { \
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", \
499 timestring(tctx, s->sname1.field1), \
501 nt_time_string(tctx, v.sname2.out.field2)); \
506 #define CHECK_NTTIME(name, sname1, field1, v, sname2, field2) do { \
509 if (s->sname1.field1 != v.sname2.out.field2) { \
510 torture_result(tctx, TORTURE_FAIL, \
511 "(%s) %s/%s [%s] != %s/%s [%s]\n", \
514 nt_time_string(tctx, s->sname1.field1), \
516 nt_time_string(tctx, v.sname2.out.field2)); \
521 #define CHECK_STR(name, sname1, field1, v, sname2, field2) do { \
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", \
529 #sname1, #field1, s->sname1.field1, \
530 #sname2, #field2, v.sname2.out.field2.s); \
535 #define CHECK_WSTR(name, sname1, field1, v, sname2, field2, flags) do { \
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", \
543 #sname1, #field1, s->sname1.field1.s, \
544 #sname2, #field2, v.sname2.out.field2.s); \
549 #define CHECK_NAME(name, sname1, field1, fname, flags) do { \
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", \
557 #sname1, #field1, s->sname1.field1.s, fname); \
562 #define CHECK_UNIX_NAME(name, sname1, field1, fname, flags) do { \
565 if (!s->sname1.field1 || \
566 strcmp(s->sname1.field1, fname)) { \
567 torture_result(tctx, TORTURE_FAIL, \
568 "(%s) %s/%s [%s] != %s\n", \
570 #sname1, #field1, s->sname1.field1, fname); \
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
);
636 smb2_util_close(tree
, h
);
637 smb2_util_unlink(tree
, fname
);
638 talloc_free(mem_ctx
);
644 struct multiple_result
{
647 union smb_search_data
*list
;
650 bool fill_result(void *private_data
,
651 union smb_search_data
*file
,
654 enum smb_search_data_level data_level
)
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
, "..")))
665 data
->list
= talloc_realloc(data
->tctx
,
667 union smb_search_data
,
669 data
->list
[data
->count
-1] = file
[i
];
674 enum continue_type
{CONT_SINGLE
, CONT_INDEX
, CONT_RESTART
};
676 static NTSTATUS
multiple_smb2_search(struct smb2_tree
*tree
,
680 enum smb_search_data_level data_level
,
681 enum continue_type cont_type
,
683 struct smb2_handle
*h
)
687 unsigned int count
= 0;
688 union smb_search_data
*d
;
690 struct multiple_result
*result
= (struct multiple_result
*)data
;
693 f
.in
.file
.handle
= *h
;
694 f
.in
.pattern
= pattern
;
695 f
.in
.max_response_size
= 0x1000;
698 /* The search should start from the beginning everytime */
699 f
.in
.continue_flags
= SMB2_CONTINUE_FLAG_RESTART
;
702 status
= smb2_find_level(tree
, tree
, &f
, &count
, &d
);
703 if (NT_STATUS_EQUAL(status
, STATUS_NO_MORE_FILES
))
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
716 f
.in
.continue_flags
= SMB2_CONTINUE_FLAG_INDEX
;
719 f
.in
.continue_flags
= SMB2_CONTINUE_FLAG_SINGLE
;
723 /* we should prevent staying in the loop
725 f
.in
.continue_flags
= 0;
728 } while (count
!= 0);
734 static enum smb_search_data_level compare_data_level
;
737 static int search_compare(union smb_search_data
*d1
,
738 union smb_search_data
*d2
)
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;
759 struct multiple_result result
;
760 struct smb2_create create
;
761 struct smb2_handle h
;
764 const char *cont_name
;
766 enum smb_search_data_level data_level
;
767 enum continue_type cont_type
;
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
);
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
);
805 for (t
=0;t
<ARRAY_SIZE(search_types
);t
++) {
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
,
818 torture_assert_int_equal_goto(tctx
, result
.count
, num_files
,
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
++) {
828 enum smb_search_level level
;
829 level
= RAW_SEARCH_SMB2
;
830 s
= extract_name(&result
.list
[i
],
831 search_types
[t
].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");
838 talloc_free(result
.tctx
);
842 smb2_util_close(tree
, h
);
843 smb2_deltree(tree
, DNAME
);
844 talloc_free(mem_ctx
);
850 check an individual file result
852 static bool check_result(struct torture_context
*tctx
,
853 struct multiple_result
*result
,
859 for (i
=0;i
<result
->count
;i
++) {
861 result
->list
[i
].both_directory_info
.name
.s
) == 0) {
865 if (i
== result
->count
) {
867 torture_result(tctx
, TORTURE_FAIL
,
868 "failed: '%s' should exist with attribute %s\n",
869 name
, attrib_string(result
->list
, attrib
));
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
));
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
);
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 struct multiple_result result
;
899 union smb_setfileinfo sfinfo
;
900 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
901 struct smb2_create create
;
902 struct smb2_handle h
;
904 union smb_search_data
*d
;
905 struct file_elem files
[703] = {};
906 int num_files
= ARRAY_SIZE(files
)-3;
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
);
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");
935 result
.tctx
= talloc_new(tctx
);
938 f
.in
.file
.handle
= h
;
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
;
945 status
= smb2_find_level(tree
, tree
, &f
, &count
, &d
);
946 if (NT_STATUS_EQUAL(status
, STATUS_NO_MORE_FILES
))
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
)) {
954 } while (result
.count
< 2);
956 torture_comment(tctx
, "Changing attributes and deleting\n");
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
);
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;
1003 f
.in
.continue_flags
= SMB2_CONTINUE_FLAG_RESTART
;
1007 status
= smb2_find_level(tree
, tree
, &f
, &count
, &d
);
1008 if (NT_STATUS_EQUAL(status
, STATUS_NO_MORE_FILES
))
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
)) {
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
);
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
,
1038 result
.list
[i
].both_directory_info
.attrib
),
1039 result
.list
[i
].both_directory_info
.attrib
);
1043 smb2_util_close(tree
, h
);
1044 smb2_deltree(tree
, DNAME
);
1045 talloc_free(mem_ctx
);
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;
1059 struct file_elem files
[700] = {};
1062 struct multiple_result result
;
1063 struct smb2_handle h
;
1065 torture_comment(tctx
, "Testing if directories always come back "
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
);
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
,
1079 torture_assert_int_equal_goto(tctx
, result
.count
, num_files
, ret
, done
,
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");
1095 talloc_free(result
.list
);
1097 smb2_util_close(tree
, h
);
1098 smb2_deltree(tree
, DNAME
);
1099 talloc_free(mem_ctx
);
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;
1115 struct multiple_result result
;
1116 struct smb2_create create
;
1118 struct smb2_handle h
;
1119 union smb_search_data
*d
;
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
, "");
1139 smb2_util_close(tree
, create
.out
.file
.handle
);
1142 ZERO_STRUCT(result
);
1146 f
.in
.file
.handle
= h
;
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
;
1153 status
= smb2_find_level(tree
, tree
, &f
, &count
, &d
);
1154 if (NT_STATUS_EQUAL(status
, STATUS_NO_MORE_FILES
))
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
)) {
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");
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 */
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
);
1189 status
= smb2_find_level(tree
, tree
, &f
, &count
, &d
);
1190 if (NT_STATUS_EQUAL(status
, STATUS_NO_MORE_FILES
))
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
)) {
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 "
1211 smb2_util_close(tree
, h
);
1212 smb2_deltree(tree
, DNAME
);
1213 talloc_free(mem_ctx
);
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;
1228 /* These should be evenly divisible */
1229 int num_at_len
= num_files
/ max_len
;
1230 struct file_elem files
[2000] = {};
1234 struct smb2_create create
;
1236 struct smb2_handle h
;
1237 union smb_search_data
*d
;
1238 int i
, j
, file_count
= 0;
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
);
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
);
1280 f
.in
.file
.handle
= h
;
1282 f
.in
.max_response_size
= 0x100;
1283 f
.in
.level
= SMB2_FIND_BOTH_DIRECTORY_INFO
;
1286 status
= smb2_find_level(tree
, tree
, &f
, &count
, &d
);
1287 if (NT_STATUS_EQUAL(status
, STATUS_NO_MORE_FILES
))
1289 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
, "");
1291 for (i
= 0; i
< count
; i
++) {
1293 const char *found
= d
[i
].both_directory_info
.name
.s
;
1295 if (!strcmp(found
, ".") || !strcmp(found
, ".."))
1299 for (j
= 0; j
< 2000; j
++) {
1300 if (!strcmp(files
[j
].name
, found
)) {
1301 files
[j
].found
= true;
1310 torture_result(tctx
, TORTURE_FAIL
,
1311 "(%s): didn't expect %s\n",
1312 __location__
, found
);
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
,
1324 for (i
= 0; i
< num_files
; i
++) {
1328 torture_result(tctx
, TORTURE_FAIL
,
1329 "(%s): expected to find %s, but didn't\n",
1330 __location__
, files
[j
].name
);
1335 smb2_util_close(tree
, h
);
1336 smb2_deltree(tree
, DNAME
);
1337 talloc_free(mem_ctx
);
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");