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
};
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
;
706 status
= smb2_find_level(tree
, tree
, &f
, &count
, &d
);
707 if (NT_STATUS_EQUAL(status
, STATUS_NO_MORE_FILES
))
709 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
, "");
710 if (!fill_result(result
, d
, count
, level
, data_level
)) {
711 return NT_STATUS_UNSUCCESSFUL
;
714 if (count
== 0 || result
== NULL
|| result
->count
== 0) {
715 return NT_STATUS_UNSUCCESSFUL
;
719 * After the first iteration is complete set the CONTINUE
720 * FLAGS appropriately
724 f
.in
.continue_flags
= SMB2_CONTINUE_FLAG_INDEX
;
725 switch (data_level
) {
726 case RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO
:
728 result
->list
[result
->count
-1].both_directory_info
.file_index
;
730 case RAW_SEARCH_DATA_DIRECTORY_INFO
:
732 result
->list
[result
->count
-1].directory_info
.file_index
;
734 case RAW_SEARCH_DATA_FULL_DIRECTORY_INFO
:
736 result
->list
[result
->count
-1].full_directory_info
.file_index
;
738 case RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO
:
740 result
->list
[result
->count
-1].id_full_directory_info
.file_index
;
742 case RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO
:
744 result
->list
[result
->count
-1].id_both_directory_info
.file_index
;
747 return NT_STATUS_INVALID_PARAMETER
;
751 f
.in
.continue_flags
= SMB2_CONTINUE_FLAG_SINGLE
;
755 /* we should prevent staying in the loop
757 f
.in
.continue_flags
= 0;
760 } while (count
!= 0);
769 static enum smb_search_data_level compare_data_level
;
772 static int search_compare(union smb_search_data
*d1
,
773 union smb_search_data
*d2
)
777 s1
= extract_name(d1
, level_sort
, compare_data_level
);
778 s2
= extract_name(d2
, level_sort
, compare_data_level
);
779 return strcmp_safe(s1
, s2
);
783 basic testing of search calls using many files
785 static bool test_many_files(struct torture_context
*tctx
,
786 struct smb2_tree
*tree
)
788 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
789 const int num_files
= 700;
794 struct multiple_result result
;
795 struct smb2_create create
;
796 struct smb2_handle h
;
799 const char *cont_name
;
801 enum smb_search_data_level data_level
;
802 enum continue_type cont_type
;
804 {"SMB2_FIND_BOTH_DIRECTORY_INFO", "SINGLE", SMB2_FIND_BOTH_DIRECTORY_INFO
, RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO
, CONT_SINGLE
},
805 {"SMB2_FIND_BOTH_DIRECTORY_INFO", "INDEX", SMB2_FIND_BOTH_DIRECTORY_INFO
, RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO
, CONT_INDEX
},
806 {"SMB2_FIND_BOTH_DIRECTORY_INFO", "RESTART", SMB2_FIND_BOTH_DIRECTORY_INFO
, RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO
, CONT_RESTART
},
807 {"SMB2_FIND_DIRECTORY_INFO", "SINGLE", SMB2_FIND_DIRECTORY_INFO
, RAW_SEARCH_DATA_DIRECTORY_INFO
, CONT_SINGLE
},
808 {"SMB2_FIND_DIRECTORY_INFO", "INDEX", SMB2_FIND_DIRECTORY_INFO
, RAW_SEARCH_DATA_DIRECTORY_INFO
, CONT_INDEX
},
809 {"SMB2_FIND_DIRECTORY_INFO", "RESTART", SMB2_FIND_DIRECTORY_INFO
, RAW_SEARCH_DATA_DIRECTORY_INFO
, CONT_RESTART
},
810 {"SMB2_FIND_FULL_DIRECTORY_INFO", "SINGLE", SMB2_FIND_FULL_DIRECTORY_INFO
, RAW_SEARCH_DATA_FULL_DIRECTORY_INFO
, CONT_SINGLE
},
811 {"SMB2_FIND_FULL_DIRECTORY_INFO", "INDEX", SMB2_FIND_FULL_DIRECTORY_INFO
, RAW_SEARCH_DATA_FULL_DIRECTORY_INFO
, CONT_INDEX
},
812 {"SMB2_FIND_FULL_DIRECTORY_INFO", "RESTART", SMB2_FIND_FULL_DIRECTORY_INFO
, RAW_SEARCH_DATA_FULL_DIRECTORY_INFO
, CONT_RESTART
},
813 {"SMB2_FIND_ID_FULL_DIRECTORY_INFO", "SINGLE", SMB2_FIND_ID_FULL_DIRECTORY_INFO
, RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO
, CONT_SINGLE
},
814 {"SMB2_FIND_ID_FULL_DIRECTORY_INFO", "INDEX", SMB2_FIND_ID_FULL_DIRECTORY_INFO
, RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO
, CONT_INDEX
},
815 {"SMB2_FIND_ID_FULL_DIRECTORY_INFO", "RESTART", SMB2_FIND_ID_FULL_DIRECTORY_INFO
, RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO
, CONT_RESTART
},
816 {"SMB2_FIND_ID_BOTH_DIRECTORY_INFO", "SINGLE", SMB2_FIND_ID_BOTH_DIRECTORY_INFO
, RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO
, CONT_SINGLE
},
817 {"SMB2_FIND_ID_BOTH_DIRECTORY_INFO", "INDEX", SMB2_FIND_ID_BOTH_DIRECTORY_INFO
, RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO
, CONT_INDEX
},
818 {"SMB2_FIND_ID_BOTH_DIRECTORY_INFO", "RESTART", SMB2_FIND_ID_BOTH_DIRECTORY_INFO
, RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO
, CONT_RESTART
}
821 smb2_deltree(tree
, DNAME
);
822 status
= torture_smb2_testdir(tree
, DNAME
, &h
);
823 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
, "");
825 torture_comment(tctx
, "Testing with %d files\n", num_files
);
827 create
.in
.desired_access
= SEC_RIGHTS_FILE_ALL
;
828 create
.in
.file_attributes
= FILE_ATTRIBUTE_NORMAL
;
829 create
.in
.create_disposition
= NTCREATEX_DISP_CREATE
;
831 for (i
=num_files
-1;i
>=0;i
--) {
832 fname
= talloc_asprintf(mem_ctx
, DNAME
"\\t%03d-%d.txt", i
, i
);
833 create
.in
.fname
= talloc_asprintf(mem_ctx
, "%s", fname
);
834 status
= smb2_create(tree
, mem_ctx
, &create
);
835 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
, "");
836 smb2_util_close(tree
, create
.out
.file
.handle
);
840 for (t
=0;t
<ARRAY_SIZE(search_types
);t
++) {
842 result
.tctx
= talloc_new(tctx
);
844 torture_comment(tctx
,
845 "Continue %s via %s\n", search_types
[t
].name
,
846 search_types
[t
].cont_name
);
847 status
= multiple_smb2_search(tree
, tctx
, "*",
848 search_types
[t
].level
,
849 search_types
[t
].data_level
,
850 search_types
[t
].cont_type
,
853 torture_assert_int_equal_goto(tctx
, result
.count
, num_files
,
856 compare_data_level
= search_types
[t
].data_level
;
857 level_sort
= search_types
[t
].level
;
859 TYPESAFE_QSORT(result
.list
, result
.count
, search_compare
);
861 for (i
=0;i
<result
.count
;i
++) {
863 s
= extract_name(&result
.list
[i
],
864 search_types
[t
].level
,
866 fname
= talloc_asprintf(mem_ctx
, "t%03d-%d.txt", i
, i
);
867 torture_assert_str_equal_goto(tctx
, s
, fname
, ret
,
868 done
, "Incorrect name");
871 talloc_free(result
.tctx
);
875 smb2_util_close(tree
, h
);
876 smb2_deltree(tree
, DNAME
);
877 talloc_free(mem_ctx
);
883 check an individual file result
885 static bool check_result(struct torture_context
*tctx
,
886 struct multiple_result
*result
,
892 for (i
=0;i
<result
->count
;i
++) {
894 result
->list
[i
].both_directory_info
.name
.s
) == 0) {
898 if (i
== result
->count
) {
900 torture_result(tctx
, TORTURE_FAIL
,
901 "failed: '%s' should exist with attribute %s\n",
902 name
, attrib_string(result
->list
, attrib
));
909 torture_result(tctx
, TORTURE_FAIL
,
910 "failed: '%s' should NOT exist (has attribute %s)\n",
911 name
, attrib_string(result
->list
,
912 result
->list
[i
].both_directory_info
.attrib
));
916 if ((result
->list
[i
].both_directory_info
.attrib
&0xFFF) != attrib
) {
917 torture_result(tctx
, TORTURE_FAIL
,
918 "failed: '%s' should have attribute 0x%x (has 0x%x)\n",
919 name
, attrib
, result
->list
[i
].both_directory_info
.attrib
);
926 test what happens when the directory is modified during a search
928 static bool test_modify_search(struct torture_context
*tctx
,
929 struct smb2_tree
*tree
)
931 struct multiple_result result
;
932 union smb_setfileinfo sfinfo
;
933 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
934 struct smb2_create create
;
935 struct smb2_handle h
;
937 union smb_search_data
*d
;
938 struct file_elem files
[703] = {};
939 int num_files
= ARRAY_SIZE(files
)-3;
945 smb2_deltree(tree
, DNAME
);
947 status
= torture_smb2_testdir(tree
, DNAME
, &h
);
948 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
, "");
950 torture_comment(tctx
, "Creating %d files\n", num_files
);
953 create
.in
.desired_access
= SEC_RIGHTS_FILE_ALL
;
954 create
.in
.file_attributes
= FILE_ATTRIBUTE_NORMAL
;
955 create
.in
.create_disposition
= NTCREATEX_DISP_CREATE
;
957 for (i
= num_files
-1; i
>= 0; i
--) {
958 files
[i
].name
= talloc_asprintf(mem_ctx
, "t%03d-%d.txt", i
, i
);
959 create
.in
.fname
= talloc_asprintf(mem_ctx
, "%s\\%s",
960 DNAME
, files
[i
].name
);
961 status
= smb2_create(tree
, mem_ctx
, &create
);
962 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
, "");
963 smb2_util_close(tree
, create
.out
.file
.handle
);
966 torture_comment(tctx
, "pulling the first two files\n");
968 result
.tctx
= talloc_new(tctx
);
971 f
.in
.file
.handle
= h
;
973 f
.in
.continue_flags
= SMB2_CONTINUE_FLAG_SINGLE
;
974 f
.in
.max_response_size
= 0x100;
975 f
.in
.level
= SMB2_FIND_BOTH_DIRECTORY_INFO
;
978 status
= smb2_find_level(tree
, tree
, &f
, &count
, &d
);
979 if (NT_STATUS_EQUAL(status
, STATUS_NO_MORE_FILES
))
981 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
, "");
982 if (!fill_result(&result
, d
, count
, f
.in
.level
,
983 RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO
)) {
987 } while (result
.count
< 2);
989 torture_comment(tctx
, "Changing attributes and deleting\n");
992 create
.in
.desired_access
= SEC_RIGHTS_FILE_ALL
;
993 create
.in
.file_attributes
= FILE_ATTRIBUTE_NORMAL
;
994 create
.in
.create_disposition
= NTCREATEX_DISP_CREATE
;
996 files
[num_files
].name
= talloc_asprintf(mem_ctx
, "T003-03.txt.2");
997 create
.in
.fname
= talloc_asprintf(mem_ctx
, "%s\\%s", DNAME
,
998 files
[num_files
].name
);
999 status
= smb2_create(tree
, mem_ctx
, &create
);
1000 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
, "");
1001 smb2_util_close(tree
, create
.out
.file
.handle
);
1003 ZERO_STRUCT(create
);
1004 create
.in
.desired_access
= SEC_RIGHTS_FILE_ALL
;
1005 create
.in
.file_attributes
= FILE_ATTRIBUTE_NORMAL
;
1006 create
.in
.create_disposition
= NTCREATEX_DISP_CREATE
;
1008 files
[num_files
+ 1].name
= talloc_asprintf(mem_ctx
, "T013-13.txt.2");
1009 create
.in
.fname
= talloc_asprintf(mem_ctx
, "%s\\%s", DNAME
,
1010 files
[num_files
+ 1].name
);
1011 status
= smb2_create(tree
, mem_ctx
, &create
);
1012 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
, "");
1013 smb2_util_close(tree
, create
.out
.file
.handle
);
1015 files
[num_files
+ 2].name
= talloc_asprintf(mem_ctx
, "T013-13.txt.3");
1016 status
= smb2_create_complex_file(tctx
, tree
, DNAME
"\\T013-13.txt.3", &h
);
1017 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
, "");
1019 smb2_util_unlink(tree
, DNAME
"\\T014-14.txt");
1020 smb2_util_setatr(tree
, DNAME
"\\T015-15.txt", FILE_ATTRIBUTE_HIDDEN
);
1021 smb2_util_setatr(tree
, DNAME
"\\T016-16.txt", FILE_ATTRIBUTE_NORMAL
);
1022 smb2_util_setatr(tree
, DNAME
"\\T017-17.txt", FILE_ATTRIBUTE_SYSTEM
);
1023 smb2_util_setatr(tree
, DNAME
"\\T018-18.txt", 0);
1024 smb2_util_setatr(tree
, DNAME
"\\T039-39.txt", FILE_ATTRIBUTE_HIDDEN
);
1025 smb2_util_setatr(tree
, DNAME
"\\T000-0.txt", FILE_ATTRIBUTE_HIDDEN
);
1026 sfinfo
.generic
.level
= RAW_SFILEINFO_DISPOSITION_INFORMATION
;
1027 sfinfo
.generic
.in
.file
.path
= DNAME
"\\T013-13.txt.3";
1028 sfinfo
.disposition_info
.in
.delete_on_close
= 1;
1029 status
= smb2_composite_setpathinfo(tree
, &sfinfo
);
1030 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
, "");
1032 /* Reset the numfiles to include the new files and start the
1033 * search from the beginning */
1034 num_files
= num_files
+ 2;
1036 f
.in
.continue_flags
= SMB2_CONTINUE_FLAG_RESTART
;
1040 status
= smb2_find_level(tree
, tree
, &f
, &count
, &d
);
1041 if (NT_STATUS_EQUAL(status
, STATUS_NO_MORE_FILES
))
1043 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
, "");
1044 if (!fill_result(&result
, d
, count
, f
.in
.level
,
1045 RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO
)) {
1049 f
.in
.continue_flags
= 0;
1050 f
.in
.max_response_size
= 4096;
1051 } while (count
!= 0);
1054 ret
&= check_result(tctx
, &result
, "t039-39.txt", true, FILE_ATTRIBUTE_HIDDEN
);
1055 ret
&= check_result(tctx
, &result
, "t000-0.txt", true, FILE_ATTRIBUTE_HIDDEN
);
1056 ret
&= check_result(tctx
, &result
, "t014-14.txt", false, 0);
1057 ret
&= check_result(tctx
, &result
, "t015-15.txt", true, FILE_ATTRIBUTE_HIDDEN
);
1058 ret
&= check_result(tctx
, &result
, "t016-16.txt", true, FILE_ATTRIBUTE_NORMAL
);
1059 ret
&= check_result(tctx
, &result
, "t017-17.txt", true, FILE_ATTRIBUTE_SYSTEM
);
1060 ret
&= check_result(tctx
, &result
, "t018-18.txt", true, FILE_ATTRIBUTE_ARCHIVE
);
1061 ret
&= check_result(tctx
, &result
, "t019-19.txt", true, FILE_ATTRIBUTE_ARCHIVE
);
1062 ret
&= check_result(tctx
, &result
, "T013-13.txt.2", true, FILE_ATTRIBUTE_ARCHIVE
);
1063 ret
&= check_result(tctx
, &result
, "T003-3.txt.2", false, 0);
1064 ret
&= check_result(tctx
, &result
, "T013-13.txt.3", true, FILE_ATTRIBUTE_NORMAL
);
1067 for (i
=0;i
<result
.count
;i
++) {
1068 torture_warning(tctx
, "%s %s (0x%x)\n",
1069 result
.list
[i
].both_directory_info
.name
.s
,
1071 result
.list
[i
].both_directory_info
.attrib
),
1072 result
.list
[i
].both_directory_info
.attrib
);
1076 smb2_util_close(tree
, h
);
1077 smb2_deltree(tree
, DNAME
);
1078 talloc_free(mem_ctx
);
1084 testing if directories always come back sorted
1086 static bool test_sorted(struct torture_context
*tctx
,
1087 struct smb2_tree
*tree
)
1089 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
1090 const int num_files
= 700;
1092 struct file_elem files
[700] = {};
1095 struct multiple_result result
;
1096 struct smb2_handle h
;
1098 torture_comment(tctx
, "Testing if directories always come back "
1100 status
= populate_tree(tctx
, mem_ctx
, tree
, files
, num_files
, &h
);
1101 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
, "");
1103 ZERO_STRUCT(result
);
1106 status
= multiple_smb2_search(tree
, tctx
, "*",
1107 SMB2_FIND_BOTH_DIRECTORY_INFO
,
1108 RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO
,
1109 SMB2_CONTINUE_FLAG_SINGLE
,
1112 torture_assert_int_equal_goto(tctx
, result
.count
, num_files
, ret
, done
,
1115 for (i
=0;i
<num_files
-1;i
++) {
1116 const char *name1
, *name2
;
1117 name1
= result
.list
[i
].both_directory_info
.name
.s
;
1118 name2
= result
.list
[i
+1].both_directory_info
.name
.s
;
1119 if (strcasecmp_m(name1
, name2
) > 0) {
1120 torture_comment(tctx
, "non-alphabetical order at entry "
1121 "%d '%s' '%s'\n", i
, name1
, name2
);
1122 torture_comment(tctx
,
1123 "Server does not produce sorted directory listings"
1124 "(not an error)\n");
1128 talloc_free(result
.list
);
1130 smb2_util_close(tree
, h
);
1131 smb2_deltree(tree
, DNAME
);
1132 talloc_free(mem_ctx
);
1137 /* test the behavior of file_index field in the SMB2_FIND struct */
1138 static bool test_file_index(struct torture_context
*tctx
,
1139 struct smb2_tree
*tree
)
1141 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
1142 const int num_files
= 100;
1143 int resume_index
= 4;
1148 struct multiple_result result
;
1149 struct smb2_create create
;
1151 struct smb2_handle h
;
1152 union smb_search_data
*d
;
1155 smb2_deltree(tree
, DNAME
);
1157 status
= torture_smb2_testdir(tree
, DNAME
, &h
);
1158 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
, "");
1160 torture_comment(tctx
, "Testing the behavior of file_index flag\n");
1162 ZERO_STRUCT(create
);
1163 create
.in
.desired_access
= SEC_RIGHTS_FILE_ALL
;
1164 create
.in
.file_attributes
= FILE_ATTRIBUTE_NORMAL
;
1165 create
.in
.create_disposition
= NTCREATEX_DISP_CREATE
;
1166 for (i
= num_files
-1; i
>= 0; i
--) {
1167 fname
= talloc_asprintf(mem_ctx
, DNAME
"\\file%u.txt", i
);
1168 create
.in
.fname
= fname
;
1169 status
= smb2_create(tree
, mem_ctx
, &create
);
1170 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
, "");
1172 smb2_util_close(tree
, create
.out
.file
.handle
);
1175 ZERO_STRUCT(result
);
1179 f
.in
.file
.handle
= h
;
1181 f
.in
.continue_flags
= SMB2_CONTINUE_FLAG_SINGLE
;
1182 f
.in
.max_response_size
= 0x1000;
1183 f
.in
.level
= SMB2_FIND_FULL_DIRECTORY_INFO
;
1186 status
= smb2_find_level(tree
, tree
, &f
, &count
, &d
);
1187 if (NT_STATUS_EQUAL(status
, STATUS_NO_MORE_FILES
))
1189 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
, "");
1190 if (!fill_result(&result
, d
, count
, f
.in
.level
,
1191 RAW_SEARCH_DATA_FULL_DIRECTORY_INFO
)) {
1195 } while(result
.count
< 10);
1197 if (result
.list
[0].full_directory_info
.file_index
== 0) {
1198 torture_skip_goto(tctx
, done
,
1199 "Talking to a server that doesn't provide a "
1200 "file index.\nWindows servers using NTFS do "
1201 "not provide a file_index. Skipping test\n");
1203 /* We are not talking to a Windows based server. Windows
1204 * servers using NTFS do not provide a file_index. Windows
1205 * servers using FAT do provide a file index, however in both
1206 * cases they do not honor a file index on a resume request.
1207 * See MS-FSCC <62> and MS-SMB2 <54> for more information. */
1209 /* Set the file_index flag to point to the fifth file from the
1210 * previous enumeration and try to start the subsequent
1211 * searches from that point */
1213 result
.list
[resume_index
].full_directory_info
.file_index
;
1214 f
.in
.continue_flags
= SMB2_CONTINUE_FLAG_INDEX
;
1216 /* get the name of the next expected file */
1217 fname
= talloc_asprintf(mem_ctx
, DNAME
"\\%s",
1218 result
.list
[resume_index
].full_directory_info
.name
.s
);
1220 ZERO_STRUCT(result
);
1222 status
= smb2_find_level(tree
, tree
, &f
, &count
, &d
);
1223 if (NT_STATUS_EQUAL(status
, STATUS_NO_MORE_FILES
))
1225 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
, "");
1226 if (!fill_result(&result
, d
, count
, f
.in
.level
,
1227 RAW_SEARCH_DATA_FULL_DIRECTORY_INFO
)) {
1232 result
.list
[0].full_directory_info
.name
.s
)) {
1233 torture_comment(tctx
, "Next expected file: %s but the "
1234 "server returned %s\n", fname
,
1235 result
.list
[0].full_directory_info
.name
.s
);
1236 torture_comment(tctx
,
1237 "Not an error. Resuming using a file "
1238 "index is an optional feature of the "
1244 smb2_util_close(tree
, h
);
1245 smb2_deltree(tree
, DNAME
);
1246 talloc_free(mem_ctx
);
1252 * Tests directory enumeration in a directory containing >1000 files with
1253 * names of varying lengths.
1255 static bool test_large_files(struct torture_context
*tctx
,
1256 struct smb2_tree
*tree
)
1258 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
1259 const int num_files
= 2000;
1261 /* These should be evenly divisible */
1262 int num_at_len
= num_files
/ max_len
;
1263 struct file_elem files
[2000] = {};
1267 struct smb2_create create
;
1269 struct smb2_handle h
= {{0}};
1270 union smb_search_data
*d
;
1271 int i
, j
, file_count
= 0;
1275 torture_comment(tctx
,
1276 "Testing directory enumeration in a directory with >1000 files\n");
1278 smb2_deltree(tree
, DNAME
);
1280 ZERO_STRUCT(create
);
1281 create
.in
.desired_access
= SEC_RIGHTS_DIR_ALL
;
1282 create
.in
.create_options
= NTCREATEX_OPTIONS_DIRECTORY
;
1283 create
.in
.file_attributes
= FILE_ATTRIBUTE_DIRECTORY
;
1284 create
.in
.share_access
= NTCREATEX_SHARE_ACCESS_READ
|
1285 NTCREATEX_SHARE_ACCESS_WRITE
|
1286 NTCREATEX_SHARE_ACCESS_DELETE
;
1287 create
.in
.create_disposition
= NTCREATEX_DISP_CREATE
;
1288 create
.in
.fname
= DNAME
;
1290 status
= smb2_create(tree
, mem_ctx
, &create
);
1291 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
, "");
1292 h
= create
.out
.file
.handle
;
1294 ZERO_STRUCT(create
);
1295 create
.in
.desired_access
= SEC_RIGHTS_FILE_ALL
;
1296 create
.in
.file_attributes
= FILE_ATTRIBUTE_NORMAL
;
1297 create
.in
.create_disposition
= NTCREATEX_DISP_CREATE
;
1299 for (i
= 0; i
< num_files
; i
++) {
1300 if (i
% num_at_len
== 0) {
1301 strs
= generate_unique_strs(mem_ctx
, len
, num_at_len
);
1304 files
[i
].name
= strs
[i
% num_at_len
];
1305 create
.in
.fname
= talloc_asprintf(mem_ctx
, "%s\\%s",
1306 DNAME
, files
[i
].name
);
1307 status
= smb2_create(tree
, mem_ctx
, &create
);
1308 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
, "");
1309 smb2_util_close(tree
, create
.out
.file
.handle
);
1313 f
.in
.file
.handle
= h
;
1315 f
.in
.max_response_size
= 0x100;
1316 f
.in
.level
= SMB2_FIND_BOTH_DIRECTORY_INFO
;
1319 status
= smb2_find_level(tree
, tree
, &f
, &count
, &d
);
1320 if (NT_STATUS_EQUAL(status
, STATUS_NO_MORE_FILES
))
1322 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
, "");
1324 for (i
= 0; i
< count
; i
++) {
1326 const char *found
= d
[i
].both_directory_info
.name
.s
;
1328 if (!strcmp(found
, ".") || !strcmp(found
, ".."))
1332 for (j
= 0; j
< 2000; j
++) {
1333 if (!strcmp(files
[j
].name
, found
)) {
1334 files
[j
].found
= true;
1343 torture_result(tctx
, TORTURE_FAIL
,
1344 "(%s): didn't expect %s\n",
1345 __location__
, found
);
1349 file_count
= file_count
+ i
;
1350 f
.in
.continue_flags
= 0;
1351 f
.in
.max_response_size
= 4096;
1352 } while (count
!= 0);
1354 torture_assert_int_equal_goto(tctx
, file_count
, num_files
+ 2, ret
,
1357 for (i
= 0; i
< num_files
; i
++) {
1361 torture_result(tctx
, TORTURE_FAIL
,
1362 "(%s): expected to find %s, but didn't\n",
1363 __location__
, files
[j
].name
);
1368 smb2_util_close(tree
, h
);
1369 smb2_deltree(tree
, DNAME
);
1370 talloc_free(mem_ctx
);
1375 struct torture_suite
*torture_smb2_dir_init(void)
1377 struct torture_suite
*suite
=
1378 torture_suite_create(talloc_autofree_context(), "dir");
1380 torture_suite_add_1smb2_test(suite
, "find", test_find
);
1381 torture_suite_add_1smb2_test(suite
, "fixed", test_fixed
);
1382 torture_suite_add_1smb2_test(suite
, "one", test_one_file
);
1383 torture_suite_add_1smb2_test(suite
, "many", test_many_files
);
1384 torture_suite_add_1smb2_test(suite
, "modify", test_modify_search
);
1385 torture_suite_add_1smb2_test(suite
, "sorted", test_sorted
);
1386 torture_suite_add_1smb2_test(suite
, "file-index", test_file_index
);
1387 torture_suite_add_1smb2_test(suite
, "large-files", test_large_files
);
1388 suite
->description
= talloc_strdup(suite
, "SMB2-DIR tests");