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