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
);
108 static bool test_find(struct torture_context
*tctx
,
109 struct smb2_tree
*tree
)
111 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
112 struct smb2_handle h
;
114 union smb_search_data
*d
;
115 struct file_elem files
[NFILES
] = {};
119 int i
, j
, file_count
= 0;
121 status
= populate_tree(tctx
, mem_ctx
, tree
, files
, NFILES
, &h
);
124 f
.in
.file
.handle
= h
;
126 f
.in
.continue_flags
= SMB2_CONTINUE_FLAG_SINGLE
;
127 f
.in
.max_response_size
= 0x100;
128 f
.in
.level
= SMB2_FIND_BOTH_DIRECTORY_INFO
;
131 status
= smb2_find_level(tree
, tree
, &f
, &count
, &d
);
132 if (NT_STATUS_EQUAL(status
, STATUS_NO_MORE_FILES
))
134 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
, "");
136 for (i
= 0; i
< count
; i
++) {
138 const char *found
= d
[i
].both_directory_info
.name
.s
;
139 NTTIME ct
= d
[i
].both_directory_info
.create_time
;
141 if (!strcmp(found
, ".") || !strcmp(found
, ".."))
145 for (j
= 0; j
< NFILES
; j
++) {
146 if (strcmp(files
[j
].name
, found
) != 0) {
150 torture_assert_u64_equal_goto(tctx
,
151 files
[j
].create_time
,
153 talloc_asprintf(tctx
,
156 files
[j
].found
= true;
164 torture_result(tctx
, TORTURE_FAIL
,
165 "(%s): didn't expect %s\n",
166 __location__
, found
);
171 file_count
= file_count
+ i
;
172 f
.in
.continue_flags
= 0;
173 f
.in
.max_response_size
= 4096;
174 } while (count
!= 0);
176 torture_assert_int_equal_goto(tctx
, file_count
, NFILES
+ 2, ret
, done
,
179 for (i
= 0; i
< NFILES
; i
++) {
183 torture_result(tctx
, TORTURE_FAIL
,
184 "(%s): expected to find %s, but didn't\n",
185 __location__
, files
[j
].name
);
191 smb2_deltree(tree
, DNAME
);
192 talloc_free(mem_ctx
);
198 test fixed enumeration
201 static bool test_fixed(struct torture_context
*tctx
,
202 struct smb2_tree
*tree
)
204 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
205 struct smb2_create create
;
206 struct smb2_handle h
= {{0}};
207 struct smb2_handle h2
= {{0}};
209 union smb_search_data
*d
;
210 struct file_elem files
[NFILES
] = {};
216 status
= populate_tree(tctx
, mem_ctx
, tree
, files
, NFILES
, &h
);
219 create
.in
.desired_access
= SEC_RIGHTS_DIR_ALL
;
220 create
.in
.create_options
= NTCREATEX_OPTIONS_DIRECTORY
;
221 create
.in
.file_attributes
= FILE_ATTRIBUTE_DIRECTORY
;
222 create
.in
.share_access
= NTCREATEX_SHARE_ACCESS_READ
|
223 NTCREATEX_SHARE_ACCESS_WRITE
|
224 NTCREATEX_SHARE_ACCESS_DELETE
;
225 create
.in
.create_disposition
= NTCREATEX_DISP_OPEN
;
226 create
.in
.fname
= DNAME
;
228 status
= smb2_create(tree
, mem_ctx
, &create
);
229 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
, "");
230 h2
= create
.out
.file
.handle
;
233 f
.in
.file
.handle
= h
;
235 f
.in
.continue_flags
= SMB2_CONTINUE_FLAG_SINGLE
;
236 f
.in
.max_response_size
= 0x100;
237 f
.in
.level
= SMB2_FIND_BOTH_DIRECTORY_INFO
;
239 /* Start enumeration on h, then delete all from h2 */
240 status
= smb2_find_level(tree
, tree
, &f
, &count
, &d
);
241 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
, "");
243 f
.in
.file
.handle
= h2
;
246 status
= smb2_find_level(tree
, tree
, &f
, &count
, &d
);
247 if (NT_STATUS_EQUAL(status
, STATUS_NO_MORE_FILES
))
249 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
, "");
251 for (i
= 0; i
< count
; i
++) {
252 const char *found
= d
[i
].both_directory_info
.name
.s
;
253 char *path
= talloc_asprintf(mem_ctx
, "%s\\%s",
256 if (!strcmp(found
, ".") || !strcmp(found
, ".."))
259 status
= smb2_util_unlink(tree
, path
);
260 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
,
266 f
.in
.continue_flags
= 0;
267 f
.in
.max_response_size
= 4096;
268 } while (count
!= 0);
270 /* Now finish h enumeration. */
271 f
.in
.file
.handle
= h
;
274 status
= smb2_find_level(tree
, tree
, &f
, &count
, &d
);
275 if (NT_STATUS_EQUAL(status
, STATUS_NO_MORE_FILES
))
277 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
, "");
279 for (i
= 0; i
< count
; i
++) {
280 const char *found
= d
[i
].both_directory_info
.name
.s
;
282 if (!strcmp(found
, ".") || !strcmp(found
, ".."))
285 torture_result(tctx
, TORTURE_FAIL
,
286 "(%s): didn't expect %s (count=%u)\n",
287 __location__
, found
, count
);
292 f
.in
.continue_flags
= 0;
293 f
.in
.max_response_size
= 4096;
294 } while (count
!= 0);
297 smb2_util_close(tree
, h
);
298 smb2_util_close(tree
, h2
);
299 smb2_deltree(tree
, DNAME
);
300 talloc_free(mem_ctx
);
308 enum smb_search_data_level data_level
;
310 int resume_key_offset
;
311 uint32_t capability_mask
;
313 union smb_search_data data
;
315 {"SMB2_FIND_DIRECTORY_INFO",
316 SMB2_FIND_DIRECTORY_INFO
, RAW_SEARCH_DATA_DIRECTORY_INFO
,
317 offsetof(union smb_search_data
, directory_info
.name
.s
),
318 offsetof(union smb_search_data
, directory_info
.file_index
),
320 {"SMB2_FIND_FULL_DIRECTORY_INFO",
321 SMB2_FIND_FULL_DIRECTORY_INFO
, RAW_SEARCH_DATA_FULL_DIRECTORY_INFO
,
322 offsetof(union smb_search_data
, full_directory_info
.name
.s
),
323 offsetof(union smb_search_data
, full_directory_info
.file_index
),
325 {"SMB2_FIND_NAME_INFO",
326 SMB2_FIND_NAME_INFO
, RAW_SEARCH_DATA_NAME_INFO
,
327 offsetof(union smb_search_data
, name_info
.name
.s
),
328 offsetof(union smb_search_data
, name_info
.file_index
),
330 {"SMB2_FIND_BOTH_DIRECTORY_INFO",
331 SMB2_FIND_BOTH_DIRECTORY_INFO
, RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO
,
332 offsetof(union smb_search_data
, both_directory_info
.name
.s
),
333 offsetof(union smb_search_data
, both_directory_info
.file_index
),
335 {"SMB2_FIND_ID_FULL_DIRECTORY_INFO",
336 SMB2_FIND_ID_FULL_DIRECTORY_INFO
, RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO
,
337 offsetof(union smb_search_data
, id_full_directory_info
.name
.s
),
338 offsetof(union smb_search_data
, id_full_directory_info
.file_index
),
340 {"SMB2_FIND_ID_BOTH_DIRECTORY_INFO",
341 SMB2_FIND_ID_BOTH_DIRECTORY_INFO
, RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO
,
342 offsetof(union smb_search_data
, id_both_directory_info
.name
.s
),
343 offsetof(union smb_search_data
, id_both_directory_info
.file_index
),
348 extract the name from a smb_data structure and level
350 static const char *extract_name(union smb_search_data
*data
,
352 enum smb_search_data_level data_level
)
355 for (i
=0;i
<ARRAY_SIZE(levels
);i
++) {
356 if (level
== levels
[i
].level
&&
357 data_level
== levels
[i
].data_level
) {
358 return *(const char **)(levels
[i
].name_offset
+ (char *)data
);
364 /* find a level in the table by name */
365 static union smb_search_data
*find(const char *name
)
368 for (i
=0;i
<ARRAY_SIZE(levels
);i
++) {
369 if (NT_STATUS_IS_OK(levels
[i
].status
) &&
370 strcmp(levels
[i
].name
, name
) == 0) {
371 return &levels
[i
].data
;
377 static bool fill_level_data(TALLOC_CTX
*mem_ctx
,
378 union smb_search_data
*data
,
379 union smb_search_data
*d
,
382 enum smb_search_data_level data_level
)
385 const char *sname
= NULL
;
386 for (i
=0; i
< count
; i
++) {
387 sname
= extract_name(&d
[i
], level
, data_level
);
390 if (!strcmp(sname
, ".") || !strcmp(sname
, ".."))
398 NTSTATUS
torture_single_file_search(struct smb2_tree
*tree
,
402 enum smb_search_data_level data_level
,
404 union smb_search_data
*d
,
406 struct smb2_handle
*h
)
412 f
.in
.file
.handle
= *h
;
413 f
.in
.pattern
= pattern
;
414 f
.in
.continue_flags
= SMB2_CONTINUE_FLAG_RESTART
;
415 f
.in
.max_response_size
= 0x100;
418 status
= smb2_find_level(tree
, tree
, &f
, count
, &d
);
419 if (NT_STATUS_IS_OK(status
))
420 fill_level_data(mem_ctx
, &levels
[idx
].data
, d
, *count
, level
,
426 basic testing of all File Information Classes using a single file
428 static bool test_one_file(struct torture_context
*tctx
,
429 struct smb2_tree
*tree
)
431 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
433 const char *fname
= "torture_search.txt";
437 union smb_fileinfo all_info2
, alt_info
, internal_info
;
438 union smb_search_data
*s
;
439 union smb_search_data d
;
440 struct smb2_handle h
, h2
;
442 status
= torture_smb2_testdir(tree
, DNAME
, &h
);
443 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
, "");
445 status
= smb2_create_complex_file(tctx
, tree
, DNAME
"\\torture_search.txt",
447 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
, "");
449 /* call all the File Information Classes */
450 for (i
=0;i
<ARRAY_SIZE(levels
);i
++) {
451 torture_comment(tctx
, "Testing %s %d\n", levels
[i
].name
,
454 levels
[i
].status
= torture_single_file_search(tree
, mem_ctx
,
455 fname
, levels
[i
].level
, levels
[i
].data_level
,
457 torture_assert_ntstatus_ok_goto(tctx
, levels
[i
].status
, ret
,
461 /* get the all_info file into to check against */
462 all_info2
.generic
.level
= RAW_FILEINFO_SMB2_ALL_INFORMATION
;
463 all_info2
.generic
.in
.file
.handle
= h2
;
464 status
= smb2_getinfo_file(tree
, tctx
, &all_info2
);
465 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
,
466 "RAW_FILEINFO_ALL_INFO failed");
468 alt_info
.generic
.level
= RAW_FILEINFO_ALT_NAME_INFORMATION
;
469 alt_info
.generic
.in
.file
.handle
= h2
;
470 status
= smb2_getinfo_file(tree
, tctx
, &alt_info
);
471 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
,
472 "RAW_FILEINFO_ALT_NAME_INFO failed");
474 internal_info
.generic
.level
= RAW_FILEINFO_INTERNAL_INFORMATION
;
475 internal_info
.generic
.in
.file
.handle
= h2
;
476 status
= smb2_getinfo_file(tree
, tctx
, &internal_info
);
477 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
,
478 "RAW_FILEINFO_INTERNAL_INFORMATION "
481 #define CHECK_VAL(name, sname1, field1, v, sname2, field2) do { \
484 if ((s->sname1.field1) != (v.sname2.out.field2)) { \
485 torture_result(tctx, TORTURE_FAIL, \
486 "(%s) %s/%s [0x%x] != %s/%s [0x%x]\n", \
488 #sname1, #field1, (int)s->sname1.field1, \
489 #sname2, #field2, (int)v.sname2.out.field2); \
494 #define CHECK_TIME(name, sname1, field1, v, sname2, field2) do { \
497 if (s->sname1.field1 != \
498 (~1 & nt_time_to_unix(v.sname2.out.field2))) { \
499 torture_result(tctx, TORTURE_FAIL, \
500 "(%s) %s/%s [%s] != %s/%s [%s]\n", \
503 timestring(tctx, s->sname1.field1), \
505 nt_time_string(tctx, v.sname2.out.field2)); \
510 #define CHECK_NTTIME(name, sname1, field1, v, sname2, field2) do { \
513 if (s->sname1.field1 != v.sname2.out.field2) { \
514 torture_result(tctx, TORTURE_FAIL, \
515 "(%s) %s/%s [%s] != %s/%s [%s]\n", \
518 nt_time_string(tctx, s->sname1.field1), \
520 nt_time_string(tctx, v.sname2.out.field2)); \
525 #define CHECK_STR(name, sname1, field1, v, sname2, field2) do { \
528 if (!s->sname1.field1 || \
529 strcmp(s->sname1.field1, v.sname2.out.field2.s)) { \
530 torture_result(tctx, TORTURE_FAIL, \
531 "(%s) %s/%s [%s] != %s/%s [%s]\n", \
533 #sname1, #field1, s->sname1.field1, \
534 #sname2, #field2, v.sname2.out.field2.s); \
539 #define CHECK_WSTR(name, sname1, field1, v, sname2, field2, flags) do { \
542 if (!s->sname1.field1.s || \
543 strcmp(s->sname1.field1.s, v.sname2.out.field2.s)) { \
544 torture_result(tctx, TORTURE_FAIL, \
545 "(%s) %s/%s [%s] != %s/%s [%s]\n", \
547 #sname1, #field1, s->sname1.field1.s, \
548 #sname2, #field2, v.sname2.out.field2.s); \
553 #define CHECK_NAME(name, sname1, field1, fname, flags) do { \
556 if (!s->sname1.field1.s || \
557 strcmp(s->sname1.field1.s, fname)) { \
558 torture_result(tctx, TORTURE_FAIL, \
559 "(%s) %s/%s [%s] != %s\n", \
561 #sname1, #field1, s->sname1.field1.s, fname); \
566 #define CHECK_UNIX_NAME(name, sname1, field1, fname, flags) do { \
569 if (!s->sname1.field1 || \
570 strcmp(s->sname1.field1, fname)) { \
571 torture_result(tctx, TORTURE_FAIL, \
572 "(%s) %s/%s [%s] != %s\n", \
574 #sname1, #field1, s->sname1.field1, fname); \
579 /* check that all the results are as expected */
580 CHECK_VAL("SMB2_FIND_DIRECTORY_INFO", directory_info
, attrib
, all_info2
, all_info2
, attrib
);
581 CHECK_VAL("SMB2_FIND_FULL_DIRECTORY_INFO", full_directory_info
, attrib
, all_info2
, all_info2
, attrib
);
582 CHECK_VAL("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info
, attrib
, all_info2
, all_info2
, attrib
);
583 CHECK_VAL("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info
, attrib
, all_info2
, all_info2
, attrib
);
584 CHECK_VAL("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info
, attrib
, all_info2
, all_info2
, attrib
);
586 CHECK_NTTIME("SMB2_FIND_DIRECTORY_INFO", directory_info
, write_time
, all_info2
, all_info2
, write_time
);
587 CHECK_NTTIME("SMB2_FIND_FULL_DIRECTORY_INFO", full_directory_info
, write_time
, all_info2
, all_info2
, write_time
);
588 CHECK_NTTIME("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info
, write_time
, all_info2
, all_info2
, write_time
);
589 CHECK_NTTIME("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info
, write_time
, all_info2
, all_info2
, write_time
);
590 CHECK_NTTIME("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info
, write_time
, all_info2
, all_info2
, write_time
);
592 CHECK_NTTIME("SMB2_FIND_DIRECTORY_INFO", directory_info
, create_time
, all_info2
, all_info2
, create_time
);
593 CHECK_NTTIME("SMB2_FIND_FULL_DIRECTORY_INFO", full_directory_info
, create_time
, all_info2
, all_info2
, create_time
);
594 CHECK_NTTIME("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info
, create_time
, all_info2
, all_info2
, create_time
);
595 CHECK_NTTIME("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info
, create_time
, all_info2
, all_info2
, create_time
);
596 CHECK_NTTIME("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info
, create_time
, all_info2
, all_info2
, create_time
);
598 CHECK_NTTIME("SMB2_FIND_DIRECTORY_INFO", directory_info
, access_time
, all_info2
, all_info2
, access_time
);
599 CHECK_NTTIME("SMB2_FIND_FULL_DIRECTORY_INFO", full_directory_info
, access_time
, all_info2
, all_info2
, access_time
);
600 CHECK_NTTIME("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info
, access_time
, all_info2
, all_info2
, access_time
);
601 CHECK_NTTIME("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info
, access_time
, all_info2
, all_info2
, access_time
);
602 CHECK_NTTIME("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info
, access_time
, all_info2
, all_info2
, access_time
);
604 CHECK_NTTIME("SMB2_FIND_DIRECTORY_INFO", directory_info
, change_time
, all_info2
, all_info2
, change_time
);
605 CHECK_NTTIME("SMB2_FIND_FULL_DIRECTORY_INFO", full_directory_info
, change_time
, all_info2
, all_info2
, change_time
);
606 CHECK_NTTIME("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info
, change_time
, all_info2
, all_info2
, change_time
);
607 CHECK_NTTIME("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info
, change_time
, all_info2
, all_info2
, change_time
);
608 CHECK_NTTIME("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info
, change_time
, all_info2
, all_info2
, change_time
);
610 CHECK_VAL("SMB2_FIND_DIRECTORY_INFO", directory_info
, size
, all_info2
, all_info2
, size
);
611 CHECK_VAL("SMB2_FIND_FULL_DIRECTORY_INFO", full_directory_info
, size
, all_info2
, all_info2
, size
);
612 CHECK_VAL("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info
, size
, all_info2
, all_info2
, size
);
613 CHECK_VAL("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info
, size
, all_info2
, all_info2
, size
);
614 CHECK_VAL("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info
, size
, all_info2
, all_info2
, size
);
616 CHECK_VAL("SMB2_FIND_DIRECTORY_INFO", directory_info
, alloc_size
, all_info2
, all_info2
, alloc_size
);
617 CHECK_VAL("SMB2_FIND_FULL_DIRECTORY_INFO", full_directory_info
, alloc_size
, all_info2
, all_info2
, alloc_size
);
618 CHECK_VAL("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info
, alloc_size
, all_info2
, all_info2
, alloc_size
);
619 CHECK_VAL("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info
, alloc_size
, all_info2
, all_info2
, alloc_size
);
620 CHECK_VAL("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info
, alloc_size
, all_info2
, all_info2
, alloc_size
);
622 CHECK_VAL("SMB2_FIND_FULL_DIRECTORY_INFO", full_directory_info
, ea_size
, all_info2
, all_info2
, ea_size
);
623 CHECK_VAL("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info
, ea_size
, all_info2
, all_info2
, ea_size
);
624 CHECK_VAL("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info
, ea_size
, all_info2
, all_info2
, ea_size
);
625 CHECK_VAL("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info
, ea_size
, all_info2
, all_info2
, ea_size
);
627 CHECK_NAME("SMB2_FIND_DIRECTORY_INFO", directory_info
, name
, fname
, STR_TERMINATE_ASCII
);
628 CHECK_NAME("SMB2_FIND_FULL_DIRECTORY_INFO", full_directory_info
, name
, fname
, STR_TERMINATE_ASCII
);
629 CHECK_NAME("SMB2_FIND_NAME_INFO", name_info
, name
, fname
, STR_TERMINATE_ASCII
);
630 CHECK_NAME("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info
, name
, fname
, STR_TERMINATE_ASCII
);
631 CHECK_NAME("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info
, name
, fname
, STR_TERMINATE_ASCII
);
632 CHECK_NAME("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info
, name
, fname
, STR_TERMINATE_ASCII
);
634 CHECK_WSTR("SMB2_FIND_BOTH_DIRECTORY_INFO", both_directory_info
, short_name
, alt_info
, alt_name_info
, fname
, STR_UNICODE
);
636 CHECK_VAL("SMB2_FIND_ID_FULL_DIRECTORY_INFO", id_full_directory_info
, file_id
, internal_info
, internal_information
, file_id
);
637 CHECK_VAL("SMB2_FIND_ID_BOTH_DIRECTORY_INFO", id_both_directory_info
, file_id
, internal_info
, internal_information
, file_id
);
640 smb2_util_close(tree
, h
);
641 smb2_util_unlink(tree
, fname
);
642 talloc_free(mem_ctx
);
648 struct multiple_result
{
651 union smb_search_data
*list
;
654 bool fill_result(void *private_data
,
655 union smb_search_data
*file
,
658 enum smb_search_data_level data_level
)
662 struct multiple_result
*data
= (struct multiple_result
*)private_data
;
664 for (i
=0; i
<count
; i
++) {
665 sname
= extract_name(&file
[i
], level
, data_level
);
666 if (!strcmp(sname
, ".") || !(strcmp(sname
, "..")))
669 data
->list
= talloc_realloc(data
->tctx
,
671 union smb_search_data
,
673 data
->list
[data
->count
-1] = file
[i
];
678 enum continue_type
{CONT_SINGLE
, CONT_INDEX
, CONT_RESTART
, CONT_REOPEN
};
680 static NTSTATUS
multiple_smb2_search(struct smb2_tree
*tree
,
684 enum smb_search_data_level data_level
,
685 enum continue_type cont_type
,
687 struct smb2_handle
*h
)
691 unsigned int count
= 0;
692 union smb_search_data
*d
;
694 struct multiple_result
*result
= (struct multiple_result
*)data
;
697 f
.in
.file
.handle
= *h
;
698 f
.in
.pattern
= pattern
;
699 f
.in
.max_response_size
= 0x1000;
702 /* The search should start from the beginning everytime */
703 f
.in
.continue_flags
= SMB2_CONTINUE_FLAG_RESTART
;
704 if (cont_type
== CONT_REOPEN
) {
705 f
.in
.continue_flags
= SMB2_CONTINUE_FLAG_REOPEN
;
709 status
= smb2_find_level(tree
, tree
, &f
, &count
, &d
);
710 if (NT_STATUS_EQUAL(status
, STATUS_NO_MORE_FILES
))
712 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
, "");
713 if (!fill_result(result
, d
, count
, level
, data_level
)) {
714 return NT_STATUS_UNSUCCESSFUL
;
717 if (count
== 0 || result
== NULL
|| result
->count
== 0) {
718 return NT_STATUS_UNSUCCESSFUL
;
722 * After the first iteration is complete set the CONTINUE
723 * FLAGS appropriately
727 f
.in
.continue_flags
= SMB2_CONTINUE_FLAG_INDEX
;
728 switch (data_level
) {
729 case RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO
:
731 result
->list
[result
->count
-1].both_directory_info
.file_index
;
733 case RAW_SEARCH_DATA_DIRECTORY_INFO
:
735 result
->list
[result
->count
-1].directory_info
.file_index
;
737 case RAW_SEARCH_DATA_FULL_DIRECTORY_INFO
:
739 result
->list
[result
->count
-1].full_directory_info
.file_index
;
741 case RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO
:
743 result
->list
[result
->count
-1].id_full_directory_info
.file_index
;
745 case RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO
:
747 result
->list
[result
->count
-1].id_both_directory_info
.file_index
;
750 return NT_STATUS_INVALID_PARAMETER
;
754 f
.in
.continue_flags
= SMB2_CONTINUE_FLAG_SINGLE
;
758 /* we should prevent staying in the loop
760 f
.in
.continue_flags
= 0;
763 } while (count
!= 0);
772 static enum smb_search_data_level compare_data_level
;
775 static int search_compare(union smb_search_data
*d1
,
776 union smb_search_data
*d2
)
780 s1
= extract_name(d1
, level_sort
, compare_data_level
);
781 s2
= extract_name(d2
, level_sort
, compare_data_level
);
782 return strcmp_safe(s1
, s2
);
786 basic testing of search calls using many files
788 static bool test_many_files(struct torture_context
*tctx
,
789 struct smb2_tree
*tree
)
791 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
792 const int num_files
= 700;
797 struct multiple_result result
;
798 struct smb2_create create
;
799 struct smb2_handle h
;
802 const char *cont_name
;
804 enum smb_search_data_level data_level
;
805 enum continue_type cont_type
;
807 {"SMB2_FIND_BOTH_DIRECTORY_INFO", "SINGLE", SMB2_FIND_BOTH_DIRECTORY_INFO
, RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO
, CONT_SINGLE
},
808 {"SMB2_FIND_BOTH_DIRECTORY_INFO", "INDEX", SMB2_FIND_BOTH_DIRECTORY_INFO
, RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO
, CONT_INDEX
},
809 {"SMB2_FIND_BOTH_DIRECTORY_INFO", "RESTART", SMB2_FIND_BOTH_DIRECTORY_INFO
, RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO
, CONT_RESTART
},
810 {"SMB2_FIND_BOTH_DIRECTORY_INFO", "REOPEN", SMB2_FIND_BOTH_DIRECTORY_INFO
, RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO
, CONT_REOPEN
},
811 {"SMB2_FIND_DIRECTORY_INFO", "SINGLE", SMB2_FIND_DIRECTORY_INFO
, RAW_SEARCH_DATA_DIRECTORY_INFO
, CONT_SINGLE
},
812 {"SMB2_FIND_DIRECTORY_INFO", "INDEX", SMB2_FIND_DIRECTORY_INFO
, RAW_SEARCH_DATA_DIRECTORY_INFO
, CONT_INDEX
},
813 {"SMB2_FIND_DIRECTORY_INFO", "RESTART", SMB2_FIND_DIRECTORY_INFO
, RAW_SEARCH_DATA_DIRECTORY_INFO
, CONT_RESTART
},
814 {"SMB2_FIND_DIRECTORY_INFO", "REOPEN", SMB2_FIND_DIRECTORY_INFO
, RAW_SEARCH_DATA_DIRECTORY_INFO
, CONT_REOPEN
},
815 {"SMB2_FIND_FULL_DIRECTORY_INFO", "SINGLE", SMB2_FIND_FULL_DIRECTORY_INFO
, RAW_SEARCH_DATA_FULL_DIRECTORY_INFO
, CONT_SINGLE
},
816 {"SMB2_FIND_FULL_DIRECTORY_INFO", "INDEX", SMB2_FIND_FULL_DIRECTORY_INFO
, RAW_SEARCH_DATA_FULL_DIRECTORY_INFO
, CONT_INDEX
},
817 {"SMB2_FIND_FULL_DIRECTORY_INFO", "RESTART", SMB2_FIND_FULL_DIRECTORY_INFO
, RAW_SEARCH_DATA_FULL_DIRECTORY_INFO
, CONT_RESTART
},
818 {"SMB2_FIND_FULL_DIRECTORY_INFO", "REOPEN", SMB2_FIND_FULL_DIRECTORY_INFO
, RAW_SEARCH_DATA_FULL_DIRECTORY_INFO
, CONT_REOPEN
},
819 {"SMB2_FIND_ID_FULL_DIRECTORY_INFO", "SINGLE", SMB2_FIND_ID_FULL_DIRECTORY_INFO
, RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO
, CONT_SINGLE
},
820 {"SMB2_FIND_ID_FULL_DIRECTORY_INFO", "INDEX", SMB2_FIND_ID_FULL_DIRECTORY_INFO
, RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO
, CONT_INDEX
},
821 {"SMB2_FIND_ID_FULL_DIRECTORY_INFO", "RESTART", SMB2_FIND_ID_FULL_DIRECTORY_INFO
, RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO
, CONT_RESTART
},
822 {"SMB2_FIND_ID_FULL_DIRECTORY_INFO", "REOPEN", SMB2_FIND_ID_FULL_DIRECTORY_INFO
, RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO
, CONT_REOPEN
},
823 {"SMB2_FIND_ID_BOTH_DIRECTORY_INFO", "SINGLE", SMB2_FIND_ID_BOTH_DIRECTORY_INFO
, RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO
, CONT_SINGLE
},
824 {"SMB2_FIND_ID_BOTH_DIRECTORY_INFO", "INDEX", SMB2_FIND_ID_BOTH_DIRECTORY_INFO
, RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO
, CONT_INDEX
},
825 {"SMB2_FIND_ID_BOTH_DIRECTORY_INFO", "RESTART", SMB2_FIND_ID_BOTH_DIRECTORY_INFO
, RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO
, CONT_RESTART
},
826 {"SMB2_FIND_ID_BOTH_DIRECTORY_INFO", "REOPEN", SMB2_FIND_ID_BOTH_DIRECTORY_INFO
, RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO
, CONT_REOPEN
},
829 smb2_deltree(tree
, DNAME
);
830 status
= torture_smb2_testdir(tree
, DNAME
, &h
);
831 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
, "");
833 torture_comment(tctx
, "Testing with %d files\n", num_files
);
835 create
.in
.desired_access
= SEC_RIGHTS_FILE_ALL
;
836 create
.in
.file_attributes
= FILE_ATTRIBUTE_NORMAL
;
837 create
.in
.create_disposition
= NTCREATEX_DISP_CREATE
;
839 for (i
=num_files
-1;i
>=0;i
--) {
840 fname
= talloc_asprintf(mem_ctx
, DNAME
"\\t%03d-%d.txt", i
, i
);
841 create
.in
.fname
= talloc_asprintf(mem_ctx
, "%s", fname
);
842 status
= smb2_create(tree
, mem_ctx
, &create
);
843 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
, "");
844 smb2_util_close(tree
, create
.out
.file
.handle
);
848 for (t
=0;t
<ARRAY_SIZE(search_types
);t
++) {
850 result
.tctx
= talloc_new(tctx
);
852 torture_comment(tctx
,
853 "Continue %s via %s\n", search_types
[t
].name
,
854 search_types
[t
].cont_name
);
855 status
= multiple_smb2_search(tree
, tctx
, "*",
856 search_types
[t
].level
,
857 search_types
[t
].data_level
,
858 search_types
[t
].cont_type
,
861 torture_assert_int_equal_goto(tctx
, result
.count
, num_files
,
864 compare_data_level
= search_types
[t
].data_level
;
865 level_sort
= search_types
[t
].level
;
867 TYPESAFE_QSORT(result
.list
, result
.count
, search_compare
);
869 for (i
=0;i
<result
.count
;i
++) {
871 s
= extract_name(&result
.list
[i
],
872 search_types
[t
].level
,
874 fname
= talloc_asprintf(mem_ctx
, "t%03d-%d.txt", i
, i
);
875 torture_assert_str_equal_goto(tctx
, s
, fname
, ret
,
876 done
, "Incorrect name");
879 talloc_free(result
.tctx
);
883 smb2_util_close(tree
, h
);
884 smb2_deltree(tree
, DNAME
);
885 talloc_free(mem_ctx
);
891 check an individual file result
893 static bool check_result(struct torture_context
*tctx
,
894 struct multiple_result
*result
,
900 for (i
=0;i
<result
->count
;i
++) {
902 result
->list
[i
].both_directory_info
.name
.s
) == 0) {
906 if (i
== result
->count
) {
908 torture_result(tctx
, TORTURE_FAIL
,
909 "failed: '%s' should exist with attribute %s\n",
910 name
, attrib_string(result
->list
, attrib
));
917 torture_result(tctx
, TORTURE_FAIL
,
918 "failed: '%s' should NOT exist (has attribute %s)\n",
919 name
, attrib_string(result
->list
,
920 result
->list
[i
].both_directory_info
.attrib
));
924 if ((result
->list
[i
].both_directory_info
.attrib
&0xFFF) != attrib
) {
925 torture_result(tctx
, TORTURE_FAIL
,
926 "failed: '%s' should have attribute 0x%x (has 0x%x)\n",
927 name
, attrib
, result
->list
[i
].both_directory_info
.attrib
);
934 test what happens when the directory is modified during a search
936 static bool test_modify_search(struct torture_context
*tctx
,
937 struct smb2_tree
*tree
)
939 struct multiple_result result
;
940 union smb_setfileinfo sfinfo
;
941 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
942 struct smb2_create create
;
943 struct smb2_handle h
;
945 union smb_search_data
*d
;
946 struct file_elem files
[703] = {};
947 int num_files
= ARRAY_SIZE(files
)-3;
953 smb2_deltree(tree
, DNAME
);
955 status
= torture_smb2_testdir(tree
, DNAME
, &h
);
956 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
, "");
958 torture_comment(tctx
, "Creating %d files\n", num_files
);
961 create
.in
.desired_access
= SEC_RIGHTS_FILE_ALL
;
962 create
.in
.file_attributes
= FILE_ATTRIBUTE_NORMAL
;
963 create
.in
.create_disposition
= NTCREATEX_DISP_CREATE
;
965 for (i
= num_files
-1; i
>= 0; i
--) {
966 files
[i
].name
= talloc_asprintf(mem_ctx
, "t%03d-%d.txt", i
, i
);
967 create
.in
.fname
= talloc_asprintf(mem_ctx
, "%s\\%s",
968 DNAME
, files
[i
].name
);
969 status
= smb2_create(tree
, mem_ctx
, &create
);
970 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
, "");
971 smb2_util_close(tree
, create
.out
.file
.handle
);
974 torture_comment(tctx
, "pulling the first two files\n");
976 result
.tctx
= talloc_new(tctx
);
979 f
.in
.file
.handle
= h
;
981 f
.in
.continue_flags
= SMB2_CONTINUE_FLAG_SINGLE
;
982 f
.in
.max_response_size
= 0x100;
983 f
.in
.level
= SMB2_FIND_BOTH_DIRECTORY_INFO
;
986 status
= smb2_find_level(tree
, tree
, &f
, &count
, &d
);
987 if (NT_STATUS_EQUAL(status
, STATUS_NO_MORE_FILES
))
989 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
, "");
990 if (!fill_result(&result
, d
, count
, f
.in
.level
,
991 RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO
)) {
995 } while (result
.count
< 2);
997 torture_comment(tctx
, "Changing attributes and deleting\n");
1000 create
.in
.desired_access
= SEC_RIGHTS_FILE_ALL
;
1001 create
.in
.file_attributes
= FILE_ATTRIBUTE_NORMAL
;
1002 create
.in
.create_disposition
= NTCREATEX_DISP_CREATE
;
1004 files
[num_files
].name
= talloc_asprintf(mem_ctx
, "T003-03.txt.2");
1005 create
.in
.fname
= talloc_asprintf(mem_ctx
, "%s\\%s", DNAME
,
1006 files
[num_files
].name
);
1007 status
= smb2_create(tree
, mem_ctx
, &create
);
1008 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
, "");
1009 smb2_util_close(tree
, create
.out
.file
.handle
);
1011 ZERO_STRUCT(create
);
1012 create
.in
.desired_access
= SEC_RIGHTS_FILE_ALL
;
1013 create
.in
.file_attributes
= FILE_ATTRIBUTE_NORMAL
;
1014 create
.in
.create_disposition
= NTCREATEX_DISP_CREATE
;
1016 files
[num_files
+ 1].name
= talloc_asprintf(mem_ctx
, "T013-13.txt.2");
1017 create
.in
.fname
= talloc_asprintf(mem_ctx
, "%s\\%s", DNAME
,
1018 files
[num_files
+ 1].name
);
1019 status
= smb2_create(tree
, mem_ctx
, &create
);
1020 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
, "");
1021 smb2_util_close(tree
, create
.out
.file
.handle
);
1023 files
[num_files
+ 2].name
= talloc_asprintf(mem_ctx
, "T013-13.txt.3");
1024 status
= smb2_create_complex_file(tctx
, tree
, DNAME
"\\T013-13.txt.3", &h
);
1025 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
, "");
1027 smb2_util_unlink(tree
, DNAME
"\\T014-14.txt");
1028 smb2_util_setatr(tree
, DNAME
"\\T015-15.txt", FILE_ATTRIBUTE_HIDDEN
);
1029 smb2_util_setatr(tree
, DNAME
"\\T016-16.txt", FILE_ATTRIBUTE_NORMAL
);
1030 smb2_util_setatr(tree
, DNAME
"\\T017-17.txt", FILE_ATTRIBUTE_SYSTEM
);
1031 smb2_util_setatr(tree
, DNAME
"\\T018-18.txt", 0);
1032 smb2_util_setatr(tree
, DNAME
"\\T039-39.txt", FILE_ATTRIBUTE_HIDDEN
);
1033 smb2_util_setatr(tree
, DNAME
"\\T000-0.txt", FILE_ATTRIBUTE_HIDDEN
);
1034 sfinfo
.generic
.level
= RAW_SFILEINFO_DISPOSITION_INFORMATION
;
1035 sfinfo
.generic
.in
.file
.path
= DNAME
"\\T013-13.txt.3";
1036 sfinfo
.disposition_info
.in
.delete_on_close
= 1;
1037 status
= smb2_composite_setpathinfo(tree
, &sfinfo
);
1038 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
, "");
1040 /* Reset the numfiles to include the new files and start the
1041 * search from the beginning */
1042 num_files
= num_files
+ 2;
1044 f
.in
.continue_flags
= SMB2_CONTINUE_FLAG_RESTART
;
1048 status
= smb2_find_level(tree
, tree
, &f
, &count
, &d
);
1049 if (NT_STATUS_EQUAL(status
, STATUS_NO_MORE_FILES
))
1051 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
, "");
1052 if (!fill_result(&result
, d
, count
, f
.in
.level
,
1053 RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO
)) {
1057 f
.in
.continue_flags
= 0;
1058 f
.in
.max_response_size
= 4096;
1059 } while (count
!= 0);
1062 ret
&= check_result(tctx
, &result
, "t039-39.txt", true, FILE_ATTRIBUTE_HIDDEN
);
1063 ret
&= check_result(tctx
, &result
, "t000-0.txt", true, FILE_ATTRIBUTE_HIDDEN
);
1064 ret
&= check_result(tctx
, &result
, "t014-14.txt", false, 0);
1065 ret
&= check_result(tctx
, &result
, "t015-15.txt", true, FILE_ATTRIBUTE_HIDDEN
);
1066 ret
&= check_result(tctx
, &result
, "t016-16.txt", true, FILE_ATTRIBUTE_NORMAL
);
1067 ret
&= check_result(tctx
, &result
, "t017-17.txt", true, FILE_ATTRIBUTE_SYSTEM
);
1068 ret
&= check_result(tctx
, &result
, "t018-18.txt", true, FILE_ATTRIBUTE_ARCHIVE
);
1069 ret
&= check_result(tctx
, &result
, "t019-19.txt", true, FILE_ATTRIBUTE_ARCHIVE
);
1070 ret
&= check_result(tctx
, &result
, "T013-13.txt.2", true, FILE_ATTRIBUTE_ARCHIVE
);
1071 ret
&= check_result(tctx
, &result
, "T003-3.txt.2", false, 0);
1072 ret
&= check_result(tctx
, &result
, "T013-13.txt.3", true, FILE_ATTRIBUTE_NORMAL
);
1075 for (i
=0;i
<result
.count
;i
++) {
1076 torture_warning(tctx
, "%s %s (0x%x)\n",
1077 result
.list
[i
].both_directory_info
.name
.s
,
1079 result
.list
[i
].both_directory_info
.attrib
),
1080 result
.list
[i
].both_directory_info
.attrib
);
1084 smb2_util_close(tree
, h
);
1085 smb2_deltree(tree
, DNAME
);
1086 talloc_free(mem_ctx
);
1092 testing if directories always come back sorted
1094 static bool test_sorted(struct torture_context
*tctx
,
1095 struct smb2_tree
*tree
)
1097 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
1098 const int num_files
= 700;
1100 struct file_elem files
[700] = {};
1103 struct multiple_result result
;
1104 struct smb2_handle h
;
1106 torture_comment(tctx
, "Testing if directories always come back "
1108 status
= populate_tree(tctx
, mem_ctx
, tree
, files
, num_files
, &h
);
1109 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
, "");
1111 ZERO_STRUCT(result
);
1114 status
= multiple_smb2_search(tree
, tctx
, "*",
1115 SMB2_FIND_BOTH_DIRECTORY_INFO
,
1116 RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO
,
1117 SMB2_CONTINUE_FLAG_SINGLE
,
1120 torture_assert_int_equal_goto(tctx
, result
.count
, num_files
, ret
, done
,
1123 for (i
=0;i
<num_files
-1;i
++) {
1124 const char *name1
, *name2
;
1125 name1
= result
.list
[i
].both_directory_info
.name
.s
;
1126 name2
= result
.list
[i
+1].both_directory_info
.name
.s
;
1127 if (strcasecmp_m(name1
, name2
) > 0) {
1128 torture_comment(tctx
, "non-alphabetical order at entry "
1129 "%d '%s' '%s'\n", i
, name1
, name2
);
1130 torture_comment(tctx
,
1131 "Server does not produce sorted directory listings"
1132 "(not an error)\n");
1136 talloc_free(result
.list
);
1138 smb2_util_close(tree
, h
);
1139 smb2_deltree(tree
, DNAME
);
1140 talloc_free(mem_ctx
);
1145 /* test the behavior of file_index field in the SMB2_FIND struct */
1146 static bool test_file_index(struct torture_context
*tctx
,
1147 struct smb2_tree
*tree
)
1149 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
1150 const int num_files
= 100;
1151 int resume_index
= 4;
1156 struct multiple_result result
;
1157 struct smb2_create create
;
1159 struct smb2_handle h
;
1160 union smb_search_data
*d
;
1163 smb2_deltree(tree
, DNAME
);
1165 status
= torture_smb2_testdir(tree
, DNAME
, &h
);
1166 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
, "");
1168 torture_comment(tctx
, "Testing the behavior of file_index flag\n");
1170 ZERO_STRUCT(create
);
1171 create
.in
.desired_access
= SEC_RIGHTS_FILE_ALL
;
1172 create
.in
.file_attributes
= FILE_ATTRIBUTE_NORMAL
;
1173 create
.in
.create_disposition
= NTCREATEX_DISP_CREATE
;
1174 for (i
= num_files
-1; i
>= 0; i
--) {
1175 fname
= talloc_asprintf(mem_ctx
, DNAME
"\\file%u.txt", i
);
1176 create
.in
.fname
= fname
;
1177 status
= smb2_create(tree
, mem_ctx
, &create
);
1178 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
, "");
1180 smb2_util_close(tree
, create
.out
.file
.handle
);
1183 ZERO_STRUCT(result
);
1187 f
.in
.file
.handle
= h
;
1189 f
.in
.continue_flags
= SMB2_CONTINUE_FLAG_SINGLE
;
1190 f
.in
.max_response_size
= 0x1000;
1191 f
.in
.level
= SMB2_FIND_FULL_DIRECTORY_INFO
;
1194 status
= smb2_find_level(tree
, tree
, &f
, &count
, &d
);
1195 if (NT_STATUS_EQUAL(status
, STATUS_NO_MORE_FILES
))
1197 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
, "");
1198 if (!fill_result(&result
, d
, count
, f
.in
.level
,
1199 RAW_SEARCH_DATA_FULL_DIRECTORY_INFO
)) {
1203 } while(result
.count
< 10);
1205 if (result
.list
[0].full_directory_info
.file_index
== 0) {
1206 torture_skip_goto(tctx
, done
,
1207 "Talking to a server that doesn't provide a "
1208 "file index.\nWindows servers using NTFS do "
1209 "not provide a file_index. Skipping test\n");
1211 /* We are not talking to a Windows based server. Windows
1212 * servers using NTFS do not provide a file_index. Windows
1213 * servers using FAT do provide a file index, however in both
1214 * cases they do not honor a file index on a resume request.
1215 * See MS-FSCC <62> and MS-SMB2 <54> for more information. */
1217 /* Set the file_index flag to point to the fifth file from the
1218 * previous enumeration and try to start the subsequent
1219 * searches from that point */
1221 result
.list
[resume_index
].full_directory_info
.file_index
;
1222 f
.in
.continue_flags
= SMB2_CONTINUE_FLAG_INDEX
;
1224 /* get the name of the next expected file */
1225 fname
= talloc_asprintf(mem_ctx
, DNAME
"\\%s",
1226 result
.list
[resume_index
].full_directory_info
.name
.s
);
1228 ZERO_STRUCT(result
);
1230 status
= smb2_find_level(tree
, tree
, &f
, &count
, &d
);
1231 if (NT_STATUS_EQUAL(status
, STATUS_NO_MORE_FILES
))
1233 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
, "");
1234 if (!fill_result(&result
, d
, count
, f
.in
.level
,
1235 RAW_SEARCH_DATA_FULL_DIRECTORY_INFO
)) {
1240 result
.list
[0].full_directory_info
.name
.s
)) {
1241 torture_comment(tctx
, "Next expected file: %s but the "
1242 "server returned %s\n", fname
,
1243 result
.list
[0].full_directory_info
.name
.s
);
1244 torture_comment(tctx
,
1245 "Not an error. Resuming using a file "
1246 "index is an optional feature of the "
1252 smb2_util_close(tree
, h
);
1253 smb2_deltree(tree
, DNAME
);
1254 talloc_free(mem_ctx
);
1260 * Tests directory enumeration in a directory containing >1000 files with
1261 * names of varying lengths.
1263 static bool test_large_files(struct torture_context
*tctx
,
1264 struct smb2_tree
*tree
)
1266 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
1267 const int num_files
= 2000;
1269 /* These should be evenly divisible */
1270 int num_at_len
= num_files
/ max_len
;
1271 struct file_elem files
[2000] = {};
1275 struct smb2_create create
;
1277 struct smb2_handle h
= {{0}};
1278 union smb_search_data
*d
;
1279 int i
, j
, file_count
= 0;
1283 torture_comment(tctx
,
1284 "Testing directory enumeration in a directory with >1000 files\n");
1286 smb2_deltree(tree
, DNAME
);
1288 ZERO_STRUCT(create
);
1289 create
.in
.desired_access
= SEC_RIGHTS_DIR_ALL
;
1290 create
.in
.create_options
= NTCREATEX_OPTIONS_DIRECTORY
;
1291 create
.in
.file_attributes
= FILE_ATTRIBUTE_DIRECTORY
;
1292 create
.in
.share_access
= NTCREATEX_SHARE_ACCESS_READ
|
1293 NTCREATEX_SHARE_ACCESS_WRITE
|
1294 NTCREATEX_SHARE_ACCESS_DELETE
;
1295 create
.in
.create_disposition
= NTCREATEX_DISP_CREATE
;
1296 create
.in
.fname
= DNAME
;
1298 status
= smb2_create(tree
, mem_ctx
, &create
);
1299 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
, "");
1300 h
= create
.out
.file
.handle
;
1302 ZERO_STRUCT(create
);
1303 create
.in
.desired_access
= SEC_RIGHTS_FILE_ALL
;
1304 create
.in
.file_attributes
= FILE_ATTRIBUTE_NORMAL
;
1305 create
.in
.create_disposition
= NTCREATEX_DISP_CREATE
;
1307 for (i
= 0; i
< num_files
; i
++) {
1308 if (i
% num_at_len
== 0) {
1309 strs
= generate_unique_strs(mem_ctx
, len
, num_at_len
);
1312 files
[i
].name
= strs
[i
% num_at_len
];
1313 create
.in
.fname
= talloc_asprintf(mem_ctx
, "%s\\%s",
1314 DNAME
, files
[i
].name
);
1315 status
= smb2_create(tree
, mem_ctx
, &create
);
1316 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
, "");
1317 smb2_util_close(tree
, create
.out
.file
.handle
);
1321 f
.in
.file
.handle
= h
;
1323 f
.in
.max_response_size
= 0x100;
1324 f
.in
.level
= SMB2_FIND_BOTH_DIRECTORY_INFO
;
1327 status
= smb2_find_level(tree
, tree
, &f
, &count
, &d
);
1328 if (NT_STATUS_EQUAL(status
, STATUS_NO_MORE_FILES
))
1330 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
, "");
1332 for (i
= 0; i
< count
; i
++) {
1334 const char *found
= d
[i
].both_directory_info
.name
.s
;
1336 if (!strcmp(found
, ".") || !strcmp(found
, ".."))
1340 for (j
= 0; j
< 2000; j
++) {
1341 if (!strcmp(files
[j
].name
, found
)) {
1342 files
[j
].found
= true;
1351 torture_result(tctx
, TORTURE_FAIL
,
1352 "(%s): didn't expect %s\n",
1353 __location__
, found
);
1357 file_count
= file_count
+ i
;
1358 f
.in
.continue_flags
= 0;
1359 f
.in
.max_response_size
= 4096;
1360 } while (count
!= 0);
1362 torture_assert_int_equal_goto(tctx
, file_count
, num_files
+ 2, ret
,
1365 for (i
= 0; i
< num_files
; i
++) {
1369 torture_result(tctx
, TORTURE_FAIL
,
1370 "(%s): expected to find %s, but didn't\n",
1371 __location__
, files
[j
].name
);
1376 smb2_util_close(tree
, h
);
1377 smb2_deltree(tree
, DNAME
);
1378 talloc_free(mem_ctx
);
1383 struct torture_suite
*torture_smb2_dir_init(TALLOC_CTX
*ctx
)
1385 struct torture_suite
*suite
=
1386 torture_suite_create(ctx
, "dir");
1388 torture_suite_add_1smb2_test(suite
, "find", test_find
);
1389 torture_suite_add_1smb2_test(suite
, "fixed", test_fixed
);
1390 torture_suite_add_1smb2_test(suite
, "one", test_one_file
);
1391 torture_suite_add_1smb2_test(suite
, "many", test_many_files
);
1392 torture_suite_add_1smb2_test(suite
, "modify", test_modify_search
);
1393 torture_suite_add_1smb2_test(suite
, "sorted", test_sorted
);
1394 torture_suite_add_1smb2_test(suite
, "file-index", test_file_index
);
1395 torture_suite_add_1smb2_test(suite
, "large-files", test_large_files
);
1396 suite
->description
= talloc_strdup(suite
, "SMB2-DIR tests");