1 /*
2    Unix SMB/CIFS implementation.
3    RAW_SEARCH_* individual test suite
4    Copyright (C) Andrew Tridgell 2003
5 
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15 
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20 
21 #include "includes.h"
22 #include "torture/torture.h"
23 #include "system/filesys.h"
24 #include "libcli/raw/libcliraw.h"
25 #include "libcli/libcli.h"
26 #include "torture/util.h"
27 
28 
29 #define BASEDIR "\\testsearch"
30 
31 /*
32   callback function for single_search
33 */
single_search_callback(void * private,union smb_search_data * file)34 static BOOL single_search_callback(void *private, union smb_search_data *file)
35 {
36 	union smb_search_data *data = private;
37 
38 	*data = *file;
39 
40 	return True;
41 }
42 
43 /*
44   do a single file (non-wildcard) search
45 */
torture_single_search(struct smbcli_state * cli,TALLOC_CTX * mem_ctx,const char * pattern,enum smb_search_level level,enum smb_search_data_level data_level,uint16_t attrib,union smb_search_data * data)46 _PUBLIC_ NTSTATUS torture_single_search(struct smbcli_state *cli,
47 			       TALLOC_CTX *mem_ctx,
48 			       const char *pattern,
49 			       enum smb_search_level level,
50 			       enum smb_search_data_level data_level,
51 			       uint16_t attrib,
52 			       union smb_search_data *data)
53 {
54 	union smb_search_first io;
55 	union smb_search_close c;
56 	NTSTATUS status;
57 
58 	switch (level) {
59 	case RAW_SEARCH_SEARCH:
60 	case RAW_SEARCH_FFIRST:
61 	case RAW_SEARCH_FUNIQUE:
62 		io.search_first.level = level;
63 		io.search_first.data_level = RAW_SEARCH_DATA_SEARCH;
64 		io.search_first.in.max_count = 1;
65 		io.search_first.in.search_attrib = attrib;
66 		io.search_first.in.pattern = pattern;
67 		break;
68 
69 	case RAW_SEARCH_TRANS2:
70 		io.t2ffirst.level = RAW_SEARCH_TRANS2;
71 		io.t2ffirst.data_level = data_level;
72 		io.t2ffirst.in.search_attrib = attrib;
73 		io.t2ffirst.in.max_count = 1;
74 		io.t2ffirst.in.flags = FLAG_TRANS2_FIND_CLOSE;
75 		io.t2ffirst.in.storage_type = 0;
76 		io.t2ffirst.in.pattern = pattern;
77 		break;
78 
79 	case RAW_SEARCH_SMB2:
80 		return NT_STATUS_INVALID_LEVEL;
81 	}
82 
83 	status = smb_raw_search_first(cli->tree, mem_ctx,
84 				      &io, (void *)data, single_search_callback);
85 
86 	if (NT_STATUS_IS_OK(status) && level == RAW_SEARCH_FFIRST) {
87 		c.fclose.level = RAW_FINDCLOSE_FCLOSE;
88 		c.fclose.in.max_count = 1;
89 		c.fclose.in.search_attrib = 0;
90 		c.fclose.in.id = data->search.id;
91 		status = smb_raw_search_close(cli->tree, &c);
92 	}
93 
94 	return status;
95 }
96 
97 
98 static struct {
99 	const char *name;
100 	enum smb_search_level level;
101 	enum smb_search_data_level data_level;
102 	int name_offset;
103 	int resume_key_offset;
104 	uint32_t capability_mask;
105 	NTSTATUS status;
106 	union smb_search_data data;
107 } levels[] = {
108 	{"FFIRST",
109 	 RAW_SEARCH_FFIRST, RAW_SEARCH_DATA_SEARCH,
110 	 offsetof(union smb_search_data, search.name),
111 	 -1,
112 	},
113 	{"FUNIQUE",
114 	 RAW_SEARCH_FUNIQUE, RAW_SEARCH_DATA_SEARCH,
115 	 offsetof(union smb_search_data, search.name),
116 	 -1,
117 	},
118 	{"SEARCH",
119 	 RAW_SEARCH_SEARCH, RAW_SEARCH_DATA_SEARCH,
120 	 offsetof(union smb_search_data, search.name),
121 	 -1,
122 	},
123 	{"STANDARD",
124 	 RAW_SEARCH_TRANS2, RAW_SEARCH_DATA_STANDARD,
125 	 offsetof(union smb_search_data, standard.name.s),
126 	 offsetof(union smb_search_data, standard.resume_key),
127 	},
128 	{"EA_SIZE",
129 	 RAW_SEARCH_TRANS2, RAW_SEARCH_DATA_EA_SIZE,
130 	 offsetof(union smb_search_data, ea_size.name.s),
131 	 offsetof(union smb_search_data, ea_size.resume_key),
132 	},
133 	{"DIRECTORY_INFO",
134 	 RAW_SEARCH_TRANS2, RAW_SEARCH_DATA_DIRECTORY_INFO,
135 	 offsetof(union smb_search_data, directory_info.name.s),
136 	 offsetof(union smb_search_data, directory_info.file_index),
137 	},
138 	{"FULL_DIRECTORY_INFO",
139 	 RAW_SEARCH_TRANS2, RAW_SEARCH_DATA_FULL_DIRECTORY_INFO,
140 	 offsetof(union smb_search_data, full_directory_info.name.s),
141 	 offsetof(union smb_search_data, full_directory_info.file_index),
142 	},
143 	{"NAME_INFO",
144 	 RAW_SEARCH_TRANS2, RAW_SEARCH_DATA_NAME_INFO,
145 	 offsetof(union smb_search_data, name_info.name.s),
146 	 offsetof(union smb_search_data, name_info.file_index),
147 	},
148 	{"BOTH_DIRECTORY_INFO",
149 	 RAW_SEARCH_TRANS2, RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO,
150 	 offsetof(union smb_search_data, both_directory_info.name.s),
151 	 offsetof(union smb_search_data, both_directory_info.file_index),
152 	},
153 	{"ID_FULL_DIRECTORY_INFO",
154 	 RAW_SEARCH_TRANS2, RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO,
155 	 offsetof(union smb_search_data, id_full_directory_info.name.s),
156 	 offsetof(union smb_search_data, id_full_directory_info.file_index),
157 	},
158 	{"ID_BOTH_DIRECTORY_INFO",
159 	 RAW_SEARCH_TRANS2, RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO,
160 	 offsetof(union smb_search_data, id_both_directory_info.name.s),
161 	 offsetof(union smb_search_data, id_both_directory_info.file_index),
162 	},
163 	{"UNIX_INFO",
164 	 RAW_SEARCH_TRANS2, RAW_SEARCH_DATA_UNIX_INFO,
165 	 offsetof(union smb_search_data, unix_info.name),
166 	 offsetof(union smb_search_data, unix_info.file_index),
167 	 CAP_UNIX}
168 };
169 
170 
171 /*
172   return level name
173 */
level_name(enum smb_search_level level,enum smb_search_data_level data_level)174 static const char *level_name(enum smb_search_level level,
175 			      enum smb_search_data_level data_level)
176 {
177 	int i;
178 	for (i=0;i<ARRAY_SIZE(levels);i++) {
179 		if (level == levels[i].level &&
180 		    data_level == levels[i].data_level) {
181 			return levels[i].name;
182 		}
183 	}
184 	return NULL;
185 }
186 
187 /*
188   extract the name from a smb_data structure and level
189 */
extract_name(void * data,enum smb_search_level level,enum smb_search_data_level data_level)190 static const char *extract_name(void *data, enum smb_search_level level,
191 				enum smb_search_data_level data_level)
192 {
193 	int i;
194 	for (i=0;i<ARRAY_SIZE(levels);i++) {
195 		if (level == levels[i].level &&
196 		    data_level == levels[i].data_level) {
197 			return *(const char **)(levels[i].name_offset + (char *)data);
198 		}
199 	}
200 	return NULL;
201 }
202 
203 /*
204   extract the name from a smb_data structure and level
205 */
extract_resume_key(void * data,enum smb_search_level level,enum smb_search_data_level data_level)206 static uint32_t extract_resume_key(void *data, enum smb_search_level level,
207 				   enum smb_search_data_level data_level)
208 {
209 	int i;
210 	for (i=0;i<ARRAY_SIZE(levels);i++) {
211 		if (level == levels[i].level &&
212 		    data_level == levels[i].data_level) {
213 			return *(uint32_t *)(levels[i].resume_key_offset + (char *)data);
214 		}
215 	}
216 	return 0;
217 }
218 
219 /* find a level in the table by name */
find(const char * name)220 static union smb_search_data *find(const char *name)
221 {
222 	int i;
223 	for (i=0;i<ARRAY_SIZE(levels);i++) {
224 		if (NT_STATUS_IS_OK(levels[i].status) &&
225 		    strcmp(levels[i].name, name) == 0) {
226 			return &levels[i].data;
227 		}
228 	}
229 	return NULL;
230 }
231 
232 /*
233    basic testing of all RAW_SEARCH_* calls using a single file
234 */
test_one_file(struct smbcli_state * cli,TALLOC_CTX * mem_ctx)235 static BOOL test_one_file(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
236 {
237 	BOOL ret = True;
238 	int fnum;
239 	const char *fname = "\\torture_search.txt";
240 	const char *fname2 = "\\torture_search-NOTEXIST.txt";
241 	NTSTATUS status;
242 	int i;
243 	union smb_fileinfo all_info, alt_info, name_info, internal_info;
244 	union smb_search_data *s;
245 
246 	printf("Testing one file searches\n");
247 
248 	fnum = create_complex_file(cli, mem_ctx, fname);
249 	if (fnum == -1) {
250 		printf("ERROR: open of %s failed (%s)\n", fname, smbcli_errstr(cli->tree));
251 		ret = False;
252 		goto done;
253 	}
254 
255 	/* call all the levels */
256 	for (i=0;i<ARRAY_SIZE(levels);i++) {
257 		NTSTATUS expected_status;
258 		uint32_t cap = cli->transport->negotiate.capabilities;
259 
260 		printf("testing %s\n", levels[i].name);
261 
262 		levels[i].status = torture_single_search(cli, mem_ctx, fname,
263 							 levels[i].level,
264 							 levels[i].data_level,
265 							 0,
266 							 &levels[i].data);
267 
268 		/* see if this server claims to support this level */
269 		if ((cap & levels[i].capability_mask) != levels[i].capability_mask) {
270 			printf("search level %s(%d) not supported by server\n",
271 			       levels[i].name, (int)levels[i].level);
272 			continue;
273 		}
274 
275 		if (!NT_STATUS_IS_OK(levels[i].status)) {
276 			printf("search level %s(%d) failed - %s\n",
277 			       levels[i].name, (int)levels[i].level,
278 			       nt_errstr(levels[i].status));
279 			ret = False;
280 			continue;
281 		}
282 
283 		status = torture_single_search(cli, mem_ctx, fname2,
284 					       levels[i].level,
285 					       levels[i].data_level,
286 					       0,
287 					       &levels[i].data);
288 
289 		expected_status = NT_STATUS_NO_SUCH_FILE;
290 		if (levels[i].level == RAW_SEARCH_SEARCH ||
291 		    levels[i].level == RAW_SEARCH_FFIRST ||
292 		    levels[i].level == RAW_SEARCH_FUNIQUE) {
293 			expected_status = STATUS_NO_MORE_FILES;
294 		}
295 		if (!NT_STATUS_EQUAL(status, expected_status)) {
296 			printf("search level %s(%d) should fail with %s - %s\n",
297 			       levels[i].name, (int)levels[i].level,
298 			       nt_errstr(expected_status),
299 			       nt_errstr(status));
300 			ret = False;
301 		}
302 	}
303 
304 	/* get the all_info file into to check against */
305 	all_info.generic.level = RAW_FILEINFO_ALL_INFO;
306 	all_info.generic.in.file.path = fname;
307 	status = smb_raw_pathinfo(cli->tree, mem_ctx, &all_info);
308 	if (!NT_STATUS_IS_OK(status)) {
309 		printf("RAW_FILEINFO_ALL_INFO failed - %s\n", nt_errstr(status));
310 		ret = False;
311 		goto done;
312 	}
313 
314 	alt_info.generic.level = RAW_FILEINFO_ALT_NAME_INFO;
315 	alt_info.generic.in.file.path = fname;
316 	status = smb_raw_pathinfo(cli->tree, mem_ctx, &alt_info);
317 	if (!NT_STATUS_IS_OK(status)) {
318 		printf("RAW_FILEINFO_ALT_NAME_INFO failed - %s\n", nt_errstr(status));
319 		ret = False;
320 		goto done;
321 	}
322 
323 	internal_info.generic.level = RAW_FILEINFO_INTERNAL_INFORMATION;
324 	internal_info.generic.in.file.path = fname;
325 	status = smb_raw_pathinfo(cli->tree, mem_ctx, &internal_info);
326 	if (!NT_STATUS_IS_OK(status)) {
327 		printf("RAW_FILEINFO_INTERNAL_INFORMATION failed - %s\n", nt_errstr(status));
328 		ret = False;
329 		goto done;
330 	}
331 
332 	name_info.generic.level = RAW_FILEINFO_NAME_INFO;
333 	name_info.generic.in.file.path = fname;
334 	status = smb_raw_pathinfo(cli->tree, mem_ctx, &name_info);
335 	if (!NT_STATUS_IS_OK(status)) {
336 		printf("RAW_FILEINFO_NAME_INFO failed - %s\n", nt_errstr(status));
337 		ret = False;
338 		goto done;
339 	}
340 
341 #define CHECK_VAL(name, sname1, field1, v, sname2, field2) do { \
342 	s = find(name); \
343 	if (s) { \
344 		if ((s->sname1.field1) != (v.sname2.out.field2)) { \
345 			printf("(%s) %s/%s [0x%x] != %s/%s [0x%x]\n", \
346 			       __location__, \
347 				#sname1, #field1, (int)s->sname1.field1, \
348 				#sname2, #field2, (int)v.sname2.out.field2); \
349 			ret = False; \
350 		} \
351 	}} while (0)
352 
353 #define CHECK_TIME(name, sname1, field1, v, sname2, field2) do { \
354 	s = find(name); \
355 	if (s) { \
356 		if (s->sname1.field1 != (~1 & nt_time_to_unix(v.sname2.out.field2))) { \
357 			printf("(%s) %s/%s [%s] != %s/%s [%s]\n", \
358 			       __location__, \
359 				#sname1, #field1, timestring(mem_ctx, s->sname1.field1), \
360 				#sname2, #field2, nt_time_string(mem_ctx, v.sname2.out.field2)); \
361 			ret = False; \
362 		} \
363 	}} while (0)
364 
365 #define CHECK_NTTIME(name, sname1, field1, v, sname2, field2) do { \
366 	s = find(name); \
367 	if (s) { \
368 		if (s->sname1.field1 != v.sname2.out.field2) { \
369 			printf("(%s) %s/%s [%s] != %s/%s [%s]\n", \
370 			       __location__, \
371 				#sname1, #field1, nt_time_string(mem_ctx, s->sname1.field1), \
372 				#sname2, #field2, nt_time_string(mem_ctx, v.sname2.out.field2)); \
373 			ret = False; \
374 		} \
375 	}} while (0)
376 
377 #define CHECK_STR(name, sname1, field1, v, sname2, field2) do { \
378 	s = find(name); \
379 	if (s) { \
380 		if (!s->sname1.field1 || strcmp(s->sname1.field1, v.sname2.out.field2.s)) { \
381 			printf("(%s) %s/%s [%s] != %s/%s [%s]\n", \
382 			       __location__, \
383 				#sname1, #field1, s->sname1.field1, \
384 				#sname2, #field2, v.sname2.out.field2.s); \
385 			ret = False; \
386 		} \
387 	}} while (0)
388 
389 #define CHECK_WSTR(name, sname1, field1, v, sname2, field2, flags) do { \
390 	s = find(name); \
391 	if (s) { \
392 		if (!s->sname1.field1.s || \
393 		    strcmp(s->sname1.field1.s, v.sname2.out.field2.s) || \
394 		    wire_bad_flags(&s->sname1.field1, flags, cli)) { \
395 			printf("(%s) %s/%s [%s] != %s/%s [%s]\n", \
396 			       __location__, \
397 				#sname1, #field1, s->sname1.field1.s, \
398 				#sname2, #field2, v.sname2.out.field2.s); \
399 			ret = False; \
400 		} \
401 	}} while (0)
402 
403 #define CHECK_NAME(name, sname1, field1, fname, flags) do { \
404 	s = find(name); \
405 	if (s) { \
406 		if (!s->sname1.field1.s || \
407 		    strcmp(s->sname1.field1.s, fname) || \
408 		    wire_bad_flags(&s->sname1.field1, flags, cli)) { \
409 			printf("(%s) %s/%s [%s] != %s\n", \
410 			       __location__, \
411 				#sname1, #field1, s->sname1.field1.s, \
412 				fname); \
413 			ret = False; \
414 		} \
415 	}} while (0)
416 
417 #define CHECK_UNIX_NAME(name, sname1, field1, fname, flags) do { \
418 	s = find(name); \
419 	if (s) { \
420 		if (!s->sname1.field1 || \
421 		    strcmp(s->sname1.field1, fname)) { \
422 			printf("(%s) %s/%s [%s] != %s\n", \
423 			       __location__, \
424 				#sname1, #field1, s->sname1.field1, \
425 				fname); \
426 			ret = False; \
427 		} \
428 	}} while (0)
429 
430 	/* check that all the results are as expected */
431 	CHECK_VAL("SEARCH",              search,              attrib, all_info, all_info, attrib&0xFFF);
432 	CHECK_VAL("STANDARD",            standard,            attrib, all_info, all_info, attrib&0xFFF);
433 	CHECK_VAL("EA_SIZE",             ea_size,             attrib, all_info, all_info, attrib&0xFFF);
434 	CHECK_VAL("DIRECTORY_INFO",      directory_info,      attrib, all_info, all_info, attrib);
435 	CHECK_VAL("FULL_DIRECTORY_INFO", full_directory_info, attrib, all_info, all_info, attrib);
436 	CHECK_VAL("BOTH_DIRECTORY_INFO", both_directory_info, attrib, all_info, all_info, attrib);
437 	CHECK_VAL("ID_FULL_DIRECTORY_INFO", id_full_directory_info,           attrib, all_info, all_info, attrib);
438 	CHECK_VAL("ID_BOTH_DIRECTORY_INFO", id_both_directory_info,           attrib, all_info, all_info, attrib);
439 
440 	CHECK_TIME("SEARCH",             search,              write_time, all_info, all_info, write_time);
441 	CHECK_TIME("STANDARD",           standard,            write_time, all_info, all_info, write_time);
442 	CHECK_TIME("EA_SIZE",            ea_size,             write_time, all_info, all_info, write_time);
443 	CHECK_TIME("STANDARD",           standard,            create_time, all_info, all_info, create_time);
444 	CHECK_TIME("EA_SIZE",            ea_size,             create_time, all_info, all_info, create_time);
445 	CHECK_TIME("STANDARD",           standard,            access_time, all_info, all_info, access_time);
446 	CHECK_TIME("EA_SIZE",            ea_size,             access_time, all_info, all_info, access_time);
447 
448 	CHECK_NTTIME("DIRECTORY_INFO",      directory_info,      write_time, all_info, all_info, write_time);
449 	CHECK_NTTIME("FULL_DIRECTORY_INFO", full_directory_info, write_time, all_info, all_info, write_time);
450 	CHECK_NTTIME("BOTH_DIRECTORY_INFO", both_directory_info, write_time, all_info, all_info, write_time);
451 	CHECK_NTTIME("ID_FULL_DIRECTORY_INFO", id_full_directory_info,           write_time, all_info, all_info, write_time);
452 	CHECK_NTTIME("ID_BOTH_DIRECTORY_INFO", id_both_directory_info,           write_time, all_info, all_info, write_time);
453 
454 	CHECK_NTTIME("DIRECTORY_INFO",      directory_info,      create_time, all_info, all_info, create_time);
455 	CHECK_NTTIME("FULL_DIRECTORY_INFO", full_directory_info, create_time, all_info, all_info, create_time);
456 	CHECK_NTTIME("BOTH_DIRECTORY_INFO", both_directory_info, create_time, all_info, all_info, create_time);
457 	CHECK_NTTIME("ID_FULL_DIRECTORY_INFO", id_full_directory_info,           create_time, all_info, all_info, create_time);
458 	CHECK_NTTIME("ID_BOTH_DIRECTORY_INFO", id_both_directory_info,           create_time, all_info, all_info, create_time);
459 
460 	CHECK_NTTIME("DIRECTORY_INFO",      directory_info,      access_time, all_info, all_info, access_time);
461 	CHECK_NTTIME("FULL_DIRECTORY_INFO", full_directory_info, access_time, all_info, all_info, access_time);
462 	CHECK_NTTIME("BOTH_DIRECTORY_INFO", both_directory_info, access_time, all_info, all_info, access_time);
463 	CHECK_NTTIME("ID_FULL_DIRECTORY_INFO", id_full_directory_info,           access_time, all_info, all_info, access_time);
464 	CHECK_NTTIME("ID_BOTH_DIRECTORY_INFO", id_both_directory_info,           access_time, all_info, all_info, access_time);
465 
466 	CHECK_NTTIME("DIRECTORY_INFO",      directory_info,      change_time, all_info, all_info, change_time);
467 	CHECK_NTTIME("FULL_DIRECTORY_INFO", full_directory_info, change_time, all_info, all_info, change_time);
468 	CHECK_NTTIME("BOTH_DIRECTORY_INFO", both_directory_info, change_time, all_info, all_info, change_time);
469 	CHECK_NTTIME("ID_FULL_DIRECTORY_INFO", id_full_directory_info,           change_time, all_info, all_info, change_time);
470 	CHECK_NTTIME("ID_BOTH_DIRECTORY_INFO", id_both_directory_info,           change_time, all_info, all_info, change_time);
471 
472 	CHECK_VAL("SEARCH",              search,              size, all_info, all_info, size);
473 	CHECK_VAL("STANDARD",            standard,            size, all_info, all_info, size);
474 	CHECK_VAL("EA_SIZE",             ea_size,             size, all_info, all_info, size);
475 	CHECK_VAL("DIRECTORY_INFO",      directory_info,      size, all_info, all_info, size);
476 	CHECK_VAL("FULL_DIRECTORY_INFO", full_directory_info, size, all_info, all_info, size);
477 	CHECK_VAL("BOTH_DIRECTORY_INFO", both_directory_info, size, all_info, all_info, size);
478 	CHECK_VAL("ID_FULL_DIRECTORY_INFO", id_full_directory_info,           size, all_info, all_info, size);
479 	CHECK_VAL("ID_BOTH_DIRECTORY_INFO", id_both_directory_info,           size, all_info, all_info, size);
480 	CHECK_VAL("UNIX_INFO",           unix_info,           size, all_info, all_info, size);
481 
482 	CHECK_VAL("STANDARD",            standard,            alloc_size, all_info, all_info, alloc_size);
483 	CHECK_VAL("EA_SIZE",             ea_size,             alloc_size, all_info, all_info, alloc_size);
484 	CHECK_VAL("DIRECTORY_INFO",      directory_info,      alloc_size, all_info, all_info, alloc_size);
485 	CHECK_VAL("FULL_DIRECTORY_INFO", full_directory_info, alloc_size, all_info, all_info, alloc_size);
486 	CHECK_VAL("BOTH_DIRECTORY_INFO", both_directory_info, alloc_size, all_info, all_info, alloc_size);
487 	CHECK_VAL("ID_FULL_DIRECTORY_INFO", id_full_directory_info,           alloc_size, all_info, all_info, alloc_size);
488 	CHECK_VAL("ID_BOTH_DIRECTORY_INFO", id_both_directory_info,           alloc_size, all_info, all_info, alloc_size);
489 	CHECK_VAL("UNIX_INFO",           unix_info,           alloc_size, all_info, all_info, alloc_size);
490 
491 	CHECK_VAL("EA_SIZE",             ea_size,             ea_size, all_info, all_info, ea_size);
492 	CHECK_VAL("FULL_DIRECTORY_INFO", full_directory_info, ea_size, all_info, all_info, ea_size);
493 	CHECK_VAL("BOTH_DIRECTORY_INFO", both_directory_info, ea_size, all_info, all_info, ea_size);
494 	CHECK_VAL("ID_FULL_DIRECTORY_INFO", id_full_directory_info,           ea_size, all_info, all_info, ea_size);
495 	CHECK_VAL("ID_BOTH_DIRECTORY_INFO", id_both_directory_info,           ea_size, all_info, all_info, ea_size);
496 
497 	CHECK_STR("SEARCH", search, name, alt_info, alt_name_info, fname);
498 	CHECK_WSTR("BOTH_DIRECTORY_INFO", both_directory_info, short_name, alt_info, alt_name_info, fname, STR_UNICODE);
499 
500 	CHECK_NAME("STANDARD",            standard,            name, fname+1, 0);
501 	CHECK_NAME("EA_SIZE",             ea_size,             name, fname+1, 0);
502 	CHECK_NAME("DIRECTORY_INFO",      directory_info,      name, fname+1, STR_TERMINATE_ASCII);
503 	CHECK_NAME("FULL_DIRECTORY_INFO", full_directory_info, name, fname+1, STR_TERMINATE_ASCII);
504 	CHECK_NAME("NAME_INFO",           name_info,           name, fname+1, STR_TERMINATE_ASCII);
505 	CHECK_NAME("BOTH_DIRECTORY_INFO", both_directory_info, name, fname+1, STR_TERMINATE_ASCII);
506 	CHECK_NAME("ID_FULL_DIRECTORY_INFO", id_full_directory_info,           name, fname+1, STR_TERMINATE_ASCII);
507 	CHECK_NAME("ID_BOTH_DIRECTORY_INFO", id_both_directory_info,           name, fname+1, STR_TERMINATE_ASCII);
508 	CHECK_UNIX_NAME("UNIX_INFO",           unix_info,           name, fname+1, STR_TERMINATE_ASCII);
509 
510 	CHECK_VAL("ID_FULL_DIRECTORY_INFO", id_full_directory_info, file_id, internal_info, internal_information, file_id);
511 	CHECK_VAL("ID_BOTH_DIRECTORY_INFO", id_both_directory_info, file_id, internal_info, internal_information, file_id);
512 
513 done:
514 	smb_raw_exit(cli->session);
515 	smbcli_unlink(cli->tree, fname);
516 
517 	return ret;
518 }
519 
520 
521 struct multiple_result {
522 	TALLOC_CTX *mem_ctx;
523 	int count;
524 	union smb_search_data *list;
525 };
526 
527 /*
528   callback function for multiple_search
529 */
multiple_search_callback(void * private,union smb_search_data * file)530 static BOOL multiple_search_callback(void *private, union smb_search_data *file)
531 {
532 	struct multiple_result *data = private;
533 
534 
535 	data->count++;
536 	data->list = talloc_realloc(data->mem_ctx,
537 				      data->list,
538 				      union smb_search_data,
539 				      data->count);
540 
541 	data->list[data->count-1] = *file;
542 
543 	return True;
544 }
545 
546 enum continue_type {CONT_FLAGS, CONT_NAME, CONT_RESUME_KEY};
547 
548 /*
549   do a single file (non-wildcard) search
550 */
multiple_search(struct smbcli_state * cli,TALLOC_CTX * mem_ctx,const char * pattern,enum smb_search_data_level data_level,enum continue_type cont_type,void * data)551 static NTSTATUS multiple_search(struct smbcli_state *cli,
552 				TALLOC_CTX *mem_ctx,
553 				const char *pattern,
554 				enum smb_search_data_level data_level,
555 				enum continue_type cont_type,
556 				void *data)
557 {
558 	union smb_search_first io;
559 	union smb_search_next io2;
560 	NTSTATUS status;
561 	const int per_search = 100;
562 	struct multiple_result *result = data;
563 
564 	if (data_level == RAW_SEARCH_DATA_SEARCH) {
565 		io.search_first.level = RAW_SEARCH_SEARCH;
566 		io.search_first.data_level = RAW_SEARCH_DATA_SEARCH;
567 		io.search_first.in.max_count = per_search;
568 		io.search_first.in.search_attrib = 0;
569 		io.search_first.in.pattern = pattern;
570 	} else {
571 		io.t2ffirst.level = RAW_SEARCH_TRANS2;
572 		io.t2ffirst.data_level = data_level;
573 		io.t2ffirst.in.search_attrib = 0;
574 		io.t2ffirst.in.max_count = per_search;
575 		io.t2ffirst.in.flags = FLAG_TRANS2_FIND_CLOSE_IF_END;
576 		io.t2ffirst.in.storage_type = 0;
577 		io.t2ffirst.in.pattern = pattern;
578 		if (cont_type == CONT_RESUME_KEY) {
579 			io.t2ffirst.in.flags |= FLAG_TRANS2_FIND_REQUIRE_RESUME |
580 				FLAG_TRANS2_FIND_BACKUP_INTENT;
581 		}
582 	}
583 
584 	status = smb_raw_search_first(cli->tree, mem_ctx,
585 				      &io, data, multiple_search_callback);
586 
587 
588 	while (NT_STATUS_IS_OK(status)) {
589 		if (data_level == RAW_SEARCH_DATA_SEARCH) {
590 			io2.search_next.level = RAW_SEARCH_SEARCH;
591 			io2.search_next.data_level = RAW_SEARCH_DATA_SEARCH;
592 			io2.search_next.in.max_count = per_search;
593 			io2.search_next.in.search_attrib = 0;
594 			io2.search_next.in.id = result->list[result->count-1].search.id;
595 		} else {
596 			io2.t2fnext.level = RAW_SEARCH_TRANS2;
597 			io2.t2fnext.data_level = data_level;
598 			io2.t2fnext.in.handle = io.t2ffirst.out.handle;
599 			io2.t2fnext.in.max_count = per_search;
600 			io2.t2fnext.in.resume_key = 0;
601 			io2.t2fnext.in.flags = FLAG_TRANS2_FIND_CLOSE_IF_END;
602 			io2.t2fnext.in.last_name = "";
603 			switch (cont_type) {
604 			case CONT_RESUME_KEY:
605 				io2.t2fnext.in.resume_key = extract_resume_key(&result->list[result->count-1],
606 									       io2.t2fnext.level, io2.t2fnext.data_level);
607 				if (io2.t2fnext.in.resume_key == 0) {
608 					printf("Server does not support resume by key for level %s\n",
609 					       level_name(io2.t2fnext.level, io2.t2fnext.data_level));
610 					return NT_STATUS_NOT_SUPPORTED;
611 				}
612 				io2.t2fnext.in.flags |= FLAG_TRANS2_FIND_REQUIRE_RESUME |
613 					FLAG_TRANS2_FIND_BACKUP_INTENT;
614 				break;
615 			case CONT_NAME:
616 				io2.t2fnext.in.last_name = extract_name(&result->list[result->count-1],
617 									io2.t2fnext.level, io2.t2fnext.data_level);
618 				break;
619 			case CONT_FLAGS:
620 				io2.t2fnext.in.flags |= FLAG_TRANS2_FIND_CONTINUE;
621 				break;
622 			}
623 		}
624 
625 		status = smb_raw_search_next(cli->tree, mem_ctx,
626 					     &io2, data, multiple_search_callback);
627 		if (!NT_STATUS_IS_OK(status)) {
628 			break;
629 		}
630 		if (data_level == RAW_SEARCH_DATA_SEARCH) {
631 			if (io2.search_next.out.count == 0) {
632 				break;
633 			}
634 		} else if (io2.t2fnext.out.count == 0 ||
635 			   io2.t2fnext.out.end_of_search) {
636 			break;
637 		}
638 	}
639 
640 	return status;
641 }
642 
643 #define CHECK_STATUS(status, correct) do { \
644 	if (!NT_STATUS_EQUAL(status, correct)) { \
645 		printf("(%s) Incorrect status %s - should be %s\n", \
646 		       __location__, nt_errstr(status), nt_errstr(correct)); \
647 		ret = False; \
648 		goto done; \
649 	}} while (0)
650 
651 #define CHECK_VALUE(v, correct) do { \
652 	if ((v) != (correct)) { \
653 		printf("(%s) Incorrect value %s=%ld - should be %ld\n", \
654 		       __location__, #v, (long)v, (long)correct); \
655 		ret = False; \
656 		goto done; \
657 	}} while (0)
658 
659 #define CHECK_STRING(v, correct) do { \
660 	if (strcasecmp_m(v, correct) != 0) { \
661 		printf("(%s) Incorrect value %s='%s' - should be '%s'\n", \
662 		       __location__, #v, v, correct); \
663 		ret = False; \
664 	}} while (0)
665 
666 
667 static enum smb_search_data_level compare_data_level;
668 
search_compare(union smb_search_data * d1,union smb_search_data * d2)669 static int search_compare(union smb_search_data *d1, union smb_search_data *d2)
670 {
671 	const char *s1, *s2;
672 	enum smb_search_level level;
673 
674 	if (compare_data_level == RAW_SEARCH_DATA_SEARCH) {
675 		level = RAW_SEARCH_SEARCH;
676 	} else {
677 		level = RAW_SEARCH_TRANS2;
678 	}
679 
680 	s1 = extract_name(d1, level, compare_data_level);
681 	s2 = extract_name(d2, level, compare_data_level);
682 	return strcmp_safe(s1, s2);
683 }
684 
685 
686 
687 /*
688    basic testing of search calls using many files
689 */
test_many_files(struct smbcli_state * cli,TALLOC_CTX * mem_ctx)690 static BOOL test_many_files(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
691 {
692 	const int num_files = 700;
693 	int i, fnum, t;
694 	char *fname;
695 	BOOL ret = True;
696 	NTSTATUS status;
697 	struct multiple_result result;
698 	struct {
699 		const char *name;
700 		const char *cont_name;
701 		enum smb_search_data_level data_level;
702 		enum continue_type cont_type;
703 	} search_types[] = {
704 		{"SEARCH",              "ID",    RAW_SEARCH_DATA_SEARCH,              CONT_RESUME_KEY},
705 		{"BOTH_DIRECTORY_INFO", "NAME",  RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO, CONT_NAME},
706 		{"BOTH_DIRECTORY_INFO", "FLAGS", RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO, CONT_FLAGS},
707 		{"BOTH_DIRECTORY_INFO", "KEY",   RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO, CONT_RESUME_KEY},
708 		{"STANDARD",            "FLAGS", RAW_SEARCH_DATA_STANDARD,            CONT_FLAGS},
709 		{"STANDARD",            "KEY",   RAW_SEARCH_DATA_STANDARD,            CONT_RESUME_KEY},
710 		{"STANDARD",            "NAME",  RAW_SEARCH_DATA_STANDARD,            CONT_NAME},
711 		{"EA_SIZE",             "FLAGS", RAW_SEARCH_DATA_EA_SIZE,             CONT_FLAGS},
712 		{"EA_SIZE",             "KEY",   RAW_SEARCH_DATA_EA_SIZE,             CONT_RESUME_KEY},
713 		{"EA_SIZE",             "NAME",  RAW_SEARCH_DATA_EA_SIZE,             CONT_NAME},
714 		{"DIRECTORY_INFO",      "FLAGS", RAW_SEARCH_DATA_DIRECTORY_INFO,      CONT_FLAGS},
715 		{"DIRECTORY_INFO",      "KEY",   RAW_SEARCH_DATA_DIRECTORY_INFO,      CONT_RESUME_KEY},
716 		{"DIRECTORY_INFO",      "NAME",  RAW_SEARCH_DATA_DIRECTORY_INFO,      CONT_NAME},
717 		{"FULL_DIRECTORY_INFO",    "FLAGS", RAW_SEARCH_DATA_FULL_DIRECTORY_INFO,    CONT_FLAGS},
718 		{"FULL_DIRECTORY_INFO",    "KEY",   RAW_SEARCH_DATA_FULL_DIRECTORY_INFO,    CONT_RESUME_KEY},
719 		{"FULL_DIRECTORY_INFO",    "NAME",  RAW_SEARCH_DATA_FULL_DIRECTORY_INFO,    CONT_NAME},
720 		{"ID_FULL_DIRECTORY_INFO", "FLAGS", RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO, CONT_FLAGS},
721 		{"ID_FULL_DIRECTORY_INFO", "KEY",   RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO, CONT_RESUME_KEY},
722 		{"ID_FULL_DIRECTORY_INFO", "NAME",  RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO, CONT_NAME},
723 		{"ID_BOTH_DIRECTORY_INFO", "NAME",  RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO, CONT_NAME},
724 		{"ID_BOTH_DIRECTORY_INFO", "FLAGS", RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO, CONT_FLAGS},
725 		{"ID_BOTH_DIRECTORY_INFO", "KEY",   RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO, CONT_RESUME_KEY}
726 	};
727 
728 	if (!torture_setup_dir(cli, BASEDIR)) {
729 		return False;
730 	}
731 
732 	printf("Testing with %d files\n", num_files);
733 
734 	for (i=0;i<num_files;i++) {
735 		fname = talloc_asprintf(cli, BASEDIR "\\t%03d-%d.txt", i, i);
736 		fnum = smbcli_open(cli->tree, fname, O_CREAT|O_RDWR, DENY_NONE);
737 		if (fnum == -1) {
738 			printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree));
739 			ret = False;
740 			goto done;
741 		}
742 		talloc_free(fname);
743 		smbcli_close(cli->tree, fnum);
744 	}
745 
746 
747 	for (t=0;t<ARRAY_SIZE(search_types);t++) {
748 		ZERO_STRUCT(result);
749 		result.mem_ctx = talloc_new(mem_ctx);
750 
751 		printf("Continue %s via %s\n", search_types[t].name, search_types[t].cont_name);
752 
753 		status = multiple_search(cli, mem_ctx, BASEDIR "\\*.*",
754 					 search_types[t].data_level,
755 					 search_types[t].cont_type,
756 					 &result);
757 
758 		if (!NT_STATUS_IS_OK(status)) {
759 			printf("search type %s failed - %s\n",
760 			       search_types[t].name,
761 			       nt_errstr(status));
762 			ret = False;
763 			continue;
764 		}
765 		CHECK_VALUE(result.count, num_files);
766 
767 		compare_data_level = search_types[t].data_level;
768 
769 		qsort(result.list, result.count, sizeof(result.list[0]),
770 		      QSORT_CAST  search_compare);
771 
772 		for (i=0;i<result.count;i++) {
773 			const char *s;
774 			enum smb_search_level level;
775 			if (compare_data_level == RAW_SEARCH_DATA_SEARCH) {
776 				level = RAW_SEARCH_SEARCH;
777 			} else {
778 				level = RAW_SEARCH_TRANS2;
779 			}
780 			s = extract_name(&result.list[i], level, compare_data_level);
781 			fname = talloc_asprintf(cli, "t%03d-%d.txt", i, i);
782 			if (strcmp(fname, s)) {
783 				printf("Incorrect name %s at entry %d\n", s, i);
784 				ret = False;
785 				break;
786 			}
787 			talloc_free(fname);
788 		}
789 		talloc_free(result.mem_ctx);
790 	}
791 
792 done:
793 	smb_raw_exit(cli->session);
794 	smbcli_deltree(cli->tree, BASEDIR);
795 
796 	return ret;
797 }
798 
799 /*
800   check a individual file result
801 */
check_result(struct multiple_result * result,const char * name,BOOL exist,uint32_t attrib)802 static BOOL check_result(struct multiple_result *result, const char *name, BOOL exist, uint32_t attrib)
803 {
804 	int i;
805 	for (i=0;i<result->count;i++) {
806 		if (strcmp(name, result->list[i].both_directory_info.name.s) == 0) break;
807 	}
808 	if (i == result->count) {
809 		if (exist) {
810 			printf("failed: '%s' should exist with attribute %s\n",
811 			       name, attrib_string(result->list, attrib));
812 			return False;
813 		}
814 		return True;
815 	}
816 
817 	if (!exist) {
818 		printf("failed: '%s' should NOT exist (has attribute %s)\n",
819 		       name, attrib_string(result->list, result->list[i].both_directory_info.attrib));
820 		return False;
821 	}
822 
823 	if ((result->list[i].both_directory_info.attrib&0xFFF) != attrib) {
824 		printf("failed: '%s' should have attribute 0x%x (has 0x%x)\n",
825 		       name,
826 		       attrib, result->list[i].both_directory_info.attrib);
827 		return False;
828 	}
829 	return True;
830 }
831 
832 /*
833    test what happens when the directory is modified during a search
834 */
test_modify_search(struct smbcli_state * cli,TALLOC_CTX * mem_ctx)835 static BOOL test_modify_search(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
836 {
837 	const int num_files = 20;
838 	int i, fnum;
839 	char *fname;
840 	BOOL ret = True;
841 	NTSTATUS status;
842 	struct multiple_result result;
843 	union smb_search_first io;
844 	union smb_search_next io2;
845 	union smb_setfileinfo sfinfo;
846 
847 	if (!torture_setup_dir(cli, BASEDIR)) {
848 		return False;
849 	}
850 
851 	printf("Creating %d files\n", num_files);
852 
853 	for (i=num_files-1;i>=0;i--) {
854 		fname = talloc_asprintf(cli, BASEDIR "\\t%03d-%d.txt", i, i);
855 		fnum = smbcli_open(cli->tree, fname, O_CREAT|O_RDWR, DENY_NONE);
856 		if (fnum == -1) {
857 			printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree));
858 			ret = False;
859 			goto done;
860 		}
861 		talloc_free(fname);
862 		smbcli_close(cli->tree, fnum);
863 	}
864 
865 	printf("pulling the first file\n");
866 	ZERO_STRUCT(result);
867 	result.mem_ctx = talloc_new(mem_ctx);
868 
869 	io.t2ffirst.level = RAW_SEARCH_TRANS2;
870 	io.t2ffirst.data_level = RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO;
871 	io.t2ffirst.in.search_attrib = 0;
872 	io.t2ffirst.in.max_count = 0;
873 	io.t2ffirst.in.flags = 0;
874 	io.t2ffirst.in.storage_type = 0;
875 	io.t2ffirst.in.pattern = BASEDIR "\\*.*";
876 
877 	status = smb_raw_search_first(cli->tree, mem_ctx,
878 				      &io, &result, multiple_search_callback);
879 	CHECK_STATUS(status, NT_STATUS_OK);
880 	CHECK_VALUE(result.count, 1);
881 
882 	printf("pulling the second file\n");
883 	io2.t2fnext.level = RAW_SEARCH_TRANS2;
884 	io2.t2fnext.data_level = RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO;
885 	io2.t2fnext.in.handle = io.t2ffirst.out.handle;
886 	io2.t2fnext.in.max_count = 1;
887 	io2.t2fnext.in.resume_key = 0;
888 	io2.t2fnext.in.flags = 0;
889 	io2.t2fnext.in.last_name = result.list[result.count-1].both_directory_info.name.s;
890 
891 	status = smb_raw_search_next(cli->tree, mem_ctx,
892 				     &io2, &result, multiple_search_callback);
893 	CHECK_STATUS(status, NT_STATUS_OK);
894 	CHECK_VALUE(result.count, 2);
895 
896 	result.count = 0;
897 
898 	printf("Changing attributes and deleting\n");
899 	smbcli_open(cli->tree, BASEDIR "\\T003-03.txt.2", O_CREAT|O_RDWR, DENY_NONE);
900 	smbcli_open(cli->tree, BASEDIR "\\T013-13.txt.2", O_CREAT|O_RDWR, DENY_NONE);
901 	fnum = create_complex_file(cli, mem_ctx, BASEDIR "\\T013-13.txt.3");
902 	smbcli_unlink(cli->tree, BASEDIR "\\T014-14.txt");
903 	torture_set_file_attribute(cli->tree, BASEDIR "\\T015-15.txt", FILE_ATTRIBUTE_HIDDEN);
904 	torture_set_file_attribute(cli->tree, BASEDIR "\\T016-16.txt", FILE_ATTRIBUTE_NORMAL);
905 	torture_set_file_attribute(cli->tree, BASEDIR "\\T017-17.txt", FILE_ATTRIBUTE_SYSTEM);
906 	torture_set_file_attribute(cli->tree, BASEDIR "\\T018-18.txt", 0);
907 	sfinfo.generic.level = RAW_SFILEINFO_DISPOSITION_INFORMATION;
908 	sfinfo.generic.in.file.fnum = fnum;
909 	sfinfo.disposition_info.in.delete_on_close = 1;
910 	status = smb_raw_setfileinfo(cli->tree, &sfinfo);
911 	CHECK_STATUS(status, NT_STATUS_OK);
912 
913 	io2.t2fnext.level = RAW_SEARCH_TRANS2;
914 	io2.t2fnext.data_level = RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO;
915 	io2.t2fnext.in.handle = io.t2ffirst.out.handle;
916 	io2.t2fnext.in.max_count = num_files + 3;
917 	io2.t2fnext.in.resume_key = 0;
918 	io2.t2fnext.in.flags = 0;
919 	io2.t2fnext.in.last_name = ".";
920 
921 	status = smb_raw_search_next(cli->tree, mem_ctx,
922 				     &io2, &result, multiple_search_callback);
923 	CHECK_STATUS(status, NT_STATUS_OK);
924 	CHECK_VALUE(result.count, 20);
925 
926 	ret &= check_result(&result, "t009-9.txt", True, FILE_ATTRIBUTE_ARCHIVE);
927 	ret &= check_result(&result, "t014-14.txt", False, 0);
928 	ret &= check_result(&result, "t015-15.txt", False, 0);
929 	ret &= check_result(&result, "t016-16.txt", True, FILE_ATTRIBUTE_NORMAL);
930 	ret &= check_result(&result, "t017-17.txt", False, 0);
931 	ret &= check_result(&result, "t018-18.txt", True, FILE_ATTRIBUTE_ARCHIVE);
932 	ret &= check_result(&result, "t019-19.txt", True, FILE_ATTRIBUTE_ARCHIVE);
933 	ret &= check_result(&result, "T013-13.txt.2", True, FILE_ATTRIBUTE_ARCHIVE);
934 	ret &= check_result(&result, "T003-3.txt.2", False, 0);
935 	ret &= check_result(&result, "T013-13.txt.3", True, FILE_ATTRIBUTE_ARCHIVE);
936 
937 	if (!ret) {
938 		for (i=0;i<result.count;i++) {
939 			printf("%s %s (0x%x)\n",
940 			       result.list[i].both_directory_info.name.s,
941 			       attrib_string(mem_ctx, result.list[i].both_directory_info.attrib),
942 			       result.list[i].both_directory_info.attrib);
943 		}
944 	}
945 
946 done:
947 	smb_raw_exit(cli->session);
948 	smbcli_deltree(cli->tree, BASEDIR);
949 
950 	return ret;
951 }
952 
953 
954 /*
955    testing if directories always come back sorted
956 */
test_sorted(struct smbcli_state * cli,TALLOC_CTX * mem_ctx)957 static BOOL test_sorted(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
958 {
959 	const int num_files = 700;
960 	int i, fnum;
961 	char *fname;
962 	BOOL ret = True;
963 	NTSTATUS status;
964 	struct multiple_result result;
965 
966 	if (!torture_setup_dir(cli, BASEDIR)) {
967 		return False;
968 	}
969 
970 	printf("Creating %d files\n", num_files);
971 
972 	for (i=0;i<num_files;i++) {
973 		fname = talloc_asprintf(cli, BASEDIR "\\%s.txt", generate_random_str_list(mem_ctx, 10, "abcdefgh"));
974 		fnum = smbcli_open(cli->tree, fname, O_CREAT|O_RDWR, DENY_NONE);
975 		if (fnum == -1) {
976 			printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree));
977 			ret = False;
978 			goto done;
979 		}
980 		talloc_free(fname);
981 		smbcli_close(cli->tree, fnum);
982 	}
983 
984 
985 	ZERO_STRUCT(result);
986 	result.mem_ctx = mem_ctx;
987 
988 	status = multiple_search(cli, mem_ctx, BASEDIR "\\*.*",
989 				 RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO,
990 				 CONT_NAME, &result);
991 	CHECK_STATUS(status, NT_STATUS_OK);
992 	CHECK_VALUE(result.count, num_files);
993 
994 	for (i=0;i<num_files-1;i++) {
995 		const char *name1, *name2;
996 		name1 = result.list[i].both_directory_info.name.s;
997 		name2 = result.list[i+1].both_directory_info.name.s;
998 		if (strcasecmp_m(name1, name2) > 0) {
999 			printf("non-alphabetical order at entry %d  '%s' '%s'\n",
1000 			       i, name1, name2);
1001 			printf("Server does not produce sorted directory listings (not an error)\n");
1002 			goto done;
1003 		}
1004 	}
1005 
1006 	talloc_free(result.list);
1007 
1008 done:
1009 	smb_raw_exit(cli->session);
1010 	smbcli_deltree(cli->tree, BASEDIR);
1011 
1012 	return ret;
1013 }
1014 
1015 
1016 
1017 /*
1018    basic testing of many old style search calls using separate dirs
1019 */
test_many_dirs(struct smbcli_state * cli,TALLOC_CTX * mem_ctx)1020 static BOOL test_many_dirs(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
1021 {
1022 	const int num_dirs = 20;
1023 	int i, fnum, n;
1024 	char *fname, *dname;
1025 	BOOL ret = True;
1026 	NTSTATUS status;
1027 	union smb_search_data *file, *file2, *file3;
1028 
1029 	if (!torture_setup_dir(cli, BASEDIR)) {
1030 		return False;
1031 	}
1032 
1033 	printf("Creating %d dirs\n", num_dirs);
1034 
1035 	for (i=0;i<num_dirs;i++) {
1036 		dname = talloc_asprintf(cli, BASEDIR "\\d%d", i);
1037 		status = smbcli_mkdir(cli->tree, dname);
1038 		if (!NT_STATUS_IS_OK(status)) {
1039 			printf("(%s) Failed to create %s - %s\n",
1040 			       __location__, dname, nt_errstr(status));
1041 			ret = False;
1042 			goto done;
1043 		}
1044 
1045 		for (n=0;n<3;n++) {
1046 			fname = talloc_asprintf(cli, BASEDIR "\\d%d\\f%d-%d.txt", i, i, n);
1047 			fnum = smbcli_open(cli->tree, fname, O_CREAT|O_RDWR, DENY_NONE);
1048 			if (fnum == -1) {
1049 				printf("(%s) Failed to create %s - %s\n",
1050 				       __location__, fname, smbcli_errstr(cli->tree));
1051 				ret = False;
1052 				goto done;
1053 			}
1054 			talloc_free(fname);
1055 			smbcli_close(cli->tree, fnum);
1056 		}
1057 
1058 		talloc_free(dname);
1059 	}
1060 
1061 	file  = talloc_zero_array(mem_ctx, union smb_search_data, num_dirs);
1062 	file2 = talloc_zero_array(mem_ctx, union smb_search_data, num_dirs);
1063 	file3 = talloc_zero_array(mem_ctx, union smb_search_data, num_dirs);
1064 
1065 	printf("Search first on %d dirs\n", num_dirs);
1066 
1067 	for (i=0;i<num_dirs;i++) {
1068 		union smb_search_first io;
1069 		io.search_first.level = RAW_SEARCH_SEARCH;
1070 		io.search_first.data_level = RAW_SEARCH_DATA_SEARCH;
1071 		io.search_first.in.max_count = 1;
1072 		io.search_first.in.search_attrib = 0;
1073 		io.search_first.in.pattern = talloc_asprintf(mem_ctx, BASEDIR "\\d%d\\*.txt", i);
1074 		fname = talloc_asprintf(mem_ctx, "f%d-", i);
1075 
1076 		io.search_first.out.count = 0;
1077 
1078 		status = smb_raw_search_first(cli->tree, mem_ctx,
1079 					      &io, (void *)&file[i], single_search_callback);
1080 		if (io.search_first.out.count != 1) {
1081 			printf("(%s) search first gave %d entries for dir %d - %s\n",
1082 			       __location__, io.search_first.out.count, i, nt_errstr(status));
1083 			ret = False;
1084 			goto done;
1085 		}
1086 		CHECK_STATUS(status, NT_STATUS_OK);
1087 		if (strncasecmp(file[i].search.name, fname, strlen(fname)) != 0) {
1088 			printf("(%s) incorrect name '%s' expected '%s'[12].txt\n",
1089 			       __location__, file[i].search.name, fname);
1090 			ret = False;
1091 			goto done;
1092 		}
1093 
1094 		talloc_free(fname);
1095 	}
1096 
1097 	printf("Search next on %d dirs\n", num_dirs);
1098 
1099 	for (i=0;i<num_dirs;i++) {
1100 		union smb_search_next io2;
1101 
1102 		io2.search_next.level = RAW_SEARCH_SEARCH;
1103 		io2.search_next.data_level = RAW_SEARCH_DATA_SEARCH;
1104 		io2.search_next.in.max_count = 1;
1105 		io2.search_next.in.search_attrib = 0;
1106 		io2.search_next.in.id = file[i].search.id;
1107 		fname = talloc_asprintf(mem_ctx, "f%d-", i);
1108 
1109 		io2.search_next.out.count = 0;
1110 
1111 		status = smb_raw_search_next(cli->tree, mem_ctx,
1112 					     &io2, (void *)&file2[i], single_search_callback);
1113 		if (io2.search_next.out.count != 1) {
1114 			printf("(%s) search next gave %d entries for dir %d - %s\n",
1115 			       __location__, io2.search_next.out.count, i, nt_errstr(status));
1116 			ret = False;
1117 			goto done;
1118 		}
1119 		CHECK_STATUS(status, NT_STATUS_OK);
1120 		if (strncasecmp(file2[i].search.name, fname, strlen(fname)) != 0) {
1121 			printf("(%s) incorrect name '%s' expected '%s'[12].txt\n",
1122 			       __location__, file2[i].search.name, fname);
1123 			ret = False;
1124 			goto done;
1125 		}
1126 
1127 		talloc_free(fname);
1128 	}
1129 
1130 
1131 	printf("Search next (rewind) on %d dirs\n", num_dirs);
1132 
1133 	for (i=0;i<num_dirs;i++) {
1134 		union smb_search_next io2;
1135 
1136 		io2.search_next.level = RAW_SEARCH_SEARCH;
1137 		io2.search_next.data_level = RAW_SEARCH_DATA_SEARCH;
1138 		io2.search_next.in.max_count = 1;
1139 		io2.search_next.in.search_attrib = 0;
1140 		io2.search_next.in.id = file[i].search.id;
1141 		fname = talloc_asprintf(mem_ctx, "f%d-", i);
1142 		io2.search_next.out.count = 0;
1143 
1144 		status = smb_raw_search_next(cli->tree, mem_ctx,
1145 					     &io2, (void *)&file3[i], single_search_callback);
1146 		if (io2.search_next.out.count != 1) {
1147 			printf("(%s) search next gave %d entries for dir %d - %s\n",
1148 			       __location__, io2.search_next.out.count, i, nt_errstr(status));
1149 			ret = False;
1150 			goto done;
1151 		}
1152 		CHECK_STATUS(status, NT_STATUS_OK);
1153 
1154 		if (strncasecmp(file3[i].search.name, file2[i].search.name, 3) != 0) {
1155 			printf("(%s) incorrect name '%s' on rewind at dir %d\n",
1156 			       __location__, file2[i].search.name, i);
1157 			ret = False;
1158 			goto done;
1159 		}
1160 
1161 		if (strcmp(file3[i].search.name, file2[i].search.name) != 0) {
1162 			printf("(%s) server did not rewind - got '%s' expected '%s'\n",
1163 			       __location__, file3[i].search.name, file2[i].search.name);
1164 			ret = False;
1165 			goto done;
1166 		}
1167 
1168 		talloc_free(fname);
1169 	}
1170 
1171 
1172 done:
1173 	smb_raw_exit(cli->session);
1174 	smbcli_deltree(cli->tree, BASEDIR);
1175 
1176 	return ret;
1177 }
1178 
1179 
1180 /*
1181    testing of OS/2 style delete
1182 */
test_os2_delete(struct smbcli_state * cli,TALLOC_CTX * mem_ctx)1183 static BOOL test_os2_delete(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
1184 {
1185 	const int num_files = 700;
1186 	const int delete_count = 4;
1187 	int total_deleted = 0;
1188 	int i, fnum;
1189 	char *fname;
1190 	BOOL ret = True;
1191 	NTSTATUS status;
1192 	union smb_search_first io;
1193 	union smb_search_next io2;
1194 	struct multiple_result result;
1195 
1196 	if (!torture_setup_dir(cli, BASEDIR)) {
1197 		return False;
1198 	}
1199 
1200 	printf("Testing OS/2 style delete on %d files\n", num_files);
1201 
1202 	for (i=0;i<num_files;i++) {
1203 		fname = talloc_asprintf(cli, BASEDIR "\\file%u.txt", i);
1204 		fnum = smbcli_open(cli->tree, fname, O_CREAT|O_RDWR, DENY_NONE);
1205 		if (fnum == -1) {
1206 			printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree));
1207 			ret = False;
1208 			goto done;
1209 		}
1210 		talloc_free(fname);
1211 		smbcli_close(cli->tree, fnum);
1212 	}
1213 
1214 
1215 	ZERO_STRUCT(result);
1216 	result.mem_ctx = mem_ctx;
1217 
1218 	io.t2ffirst.level = RAW_SEARCH_TRANS2;
1219 	io.t2ffirst.data_level = RAW_SEARCH_DATA_EA_SIZE;
1220 	io.t2ffirst.in.search_attrib = 0;
1221 	io.t2ffirst.in.max_count = 100;
1222 	io.t2ffirst.in.flags = FLAG_TRANS2_FIND_REQUIRE_RESUME;
1223 	io.t2ffirst.in.storage_type = 0;
1224 	io.t2ffirst.in.pattern = BASEDIR "\\*";
1225 
1226 	status = smb_raw_search_first(cli->tree, mem_ctx,
1227 				      &io, &result, multiple_search_callback);
1228 	CHECK_STATUS(status, NT_STATUS_OK);
1229 
1230 	for (i=0;i<MIN(result.count, delete_count);i++) {
1231 		fname = talloc_asprintf(cli, BASEDIR "\\%s", result.list[i].ea_size.name.s);
1232 		status = smbcli_unlink(cli->tree, fname);
1233 		CHECK_STATUS(status, NT_STATUS_OK);
1234 		total_deleted++;
1235 		talloc_free(fname);
1236 	}
1237 
1238 	io2.t2fnext.level = RAW_SEARCH_TRANS2;
1239 	io2.t2fnext.data_level = RAW_SEARCH_DATA_EA_SIZE;
1240 	io2.t2fnext.in.handle = io.t2ffirst.out.handle;
1241 	io2.t2fnext.in.max_count = 100;
1242 	io2.t2fnext.in.resume_key = result.list[i-1].ea_size.resume_key;
1243 	io2.t2fnext.in.flags = FLAG_TRANS2_FIND_REQUIRE_RESUME;
1244 	io2.t2fnext.in.last_name = result.list[i-1].ea_size.name.s;
1245 
1246 	do {
1247 		ZERO_STRUCT(result);
1248 		result.mem_ctx = mem_ctx;
1249 
1250 		status = smb_raw_search_next(cli->tree, mem_ctx,
1251 					     &io2, &result, multiple_search_callback);
1252 		if (!NT_STATUS_IS_OK(status)) {
1253 			break;
1254 		}
1255 
1256 		for (i=0;i<MIN(result.count, delete_count);i++) {
1257 			fname = talloc_asprintf(cli, BASEDIR "\\%s", result.list[i].ea_size.name.s);
1258 			status = smbcli_unlink(cli->tree, fname);
1259 			CHECK_STATUS(status, NT_STATUS_OK);
1260 			total_deleted++;
1261 			talloc_free(fname);
1262 		}
1263 
1264 		if (i>0) {
1265 			io2.t2fnext.in.resume_key = result.list[i-1].ea_size.resume_key;
1266 			io2.t2fnext.in.last_name = result.list[i-1].ea_size.name.s;
1267 		}
1268 	} while (NT_STATUS_IS_OK(status) && result.count != 0);
1269 
1270 	CHECK_STATUS(status, NT_STATUS_OK);
1271 
1272 	if (total_deleted != num_files) {
1273 		printf("error: deleted %d - expected to delete %d\n",
1274 		       total_deleted, num_files);
1275 		ret = False;
1276 	}
1277 
1278 done:
1279 	smb_raw_exit(cli->session);
1280 	smbcli_deltree(cli->tree, BASEDIR);
1281 
1282 	return ret;
1283 }
1284 
1285 
ealist_cmp(union smb_search_data * r1,union smb_search_data * r2)1286 static int ealist_cmp(union smb_search_data *r1, union smb_search_data *r2)
1287 {
1288 	return strcmp(r1->ea_list.name.s, r2->ea_list.name.s);
1289 }
1290 
1291 /*
1292    testing of the rather strange ea_list level
1293 */
test_ea_list(struct smbcli_state * cli,TALLOC_CTX * mem_ctx)1294 static BOOL test_ea_list(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
1295 {
1296 	int  fnum;
1297 	BOOL ret = True;
1298 	NTSTATUS status;
1299 	union smb_search_first io;
1300 	union smb_search_next nxt;
1301 	struct multiple_result result;
1302 	union smb_setfileinfo setfile;
1303 
1304 	if (!torture_setup_dir(cli, BASEDIR)) {
1305 		return False;
1306 	}
1307 
1308 	printf("Testing RAW_SEARCH_EA_LIST level\n");
1309 
1310 	fnum = smbcli_open(cli->tree, BASEDIR "\\file1.txt", O_CREAT|O_RDWR, DENY_NONE);
1311 	smbcli_close(cli->tree, fnum);
1312 
1313 	fnum = smbcli_open(cli->tree, BASEDIR "\\file2.txt", O_CREAT|O_RDWR, DENY_NONE);
1314 	smbcli_close(cli->tree, fnum);
1315 
1316 	fnum = smbcli_open(cli->tree, BASEDIR "\\file3.txt", O_CREAT|O_RDWR, DENY_NONE);
1317 	smbcli_close(cli->tree, fnum);
1318 
1319 	setfile.generic.level = RAW_SFILEINFO_EA_SET;
1320 	setfile.generic.in.file.path = BASEDIR "\\file2.txt";
1321 	setfile.ea_set.in.num_eas = 2;
1322 	setfile.ea_set.in.eas = talloc_array(mem_ctx, struct ea_struct, 2);
1323 	setfile.ea_set.in.eas[0].flags = 0;
1324 	setfile.ea_set.in.eas[0].name.s = "EA ONE";
1325 	setfile.ea_set.in.eas[0].value = data_blob_string_const("VALUE 1");
1326 	setfile.ea_set.in.eas[1].flags = 0;
1327 	setfile.ea_set.in.eas[1].name.s = "SECOND EA";
1328 	setfile.ea_set.in.eas[1].value = data_blob_string_const("Value Two");
1329 
1330 	status = smb_raw_setpathinfo(cli->tree, &setfile);
1331 	CHECK_STATUS(status, NT_STATUS_OK);
1332 
1333 	setfile.generic.in.file.path = BASEDIR "\\file3.txt";
1334 	status = smb_raw_setpathinfo(cli->tree, &setfile);
1335 	CHECK_STATUS(status, NT_STATUS_OK);
1336 
1337 	ZERO_STRUCT(result);
1338 	result.mem_ctx = mem_ctx;
1339 
1340 	io.t2ffirst.level = RAW_SEARCH_TRANS2;
1341 	io.t2ffirst.data_level = RAW_SEARCH_DATA_EA_LIST;
1342 	io.t2ffirst.in.search_attrib = 0;
1343 	io.t2ffirst.in.max_count = 2;
1344 	io.t2ffirst.in.flags = FLAG_TRANS2_FIND_REQUIRE_RESUME;
1345 	io.t2ffirst.in.storage_type = 0;
1346 	io.t2ffirst.in.pattern = BASEDIR "\\*";
1347 	io.t2ffirst.in.num_names = 2;
1348 	io.t2ffirst.in.ea_names = talloc_array(mem_ctx, struct ea_name, 2);
1349 	io.t2ffirst.in.ea_names[0].name.s = "SECOND EA";
1350 	io.t2ffirst.in.ea_names[1].name.s = "THIRD EA";
1351 
1352 	status = smb_raw_search_first(cli->tree, mem_ctx,
1353 				      &io, &result, multiple_search_callback);
1354 	CHECK_STATUS(status, NT_STATUS_OK);
1355 	CHECK_VALUE(result.count, 2);
1356 
1357 	nxt.t2fnext.level = RAW_SEARCH_TRANS2;
1358 	nxt.t2fnext.data_level = RAW_SEARCH_DATA_EA_LIST;
1359 	nxt.t2fnext.in.handle = io.t2ffirst.out.handle;
1360 	nxt.t2fnext.in.max_count = 2;
1361 	nxt.t2fnext.in.resume_key = result.list[1].ea_list.resume_key;
1362 	nxt.t2fnext.in.flags = FLAG_TRANS2_FIND_REQUIRE_RESUME | FLAG_TRANS2_FIND_CONTINUE;
1363 	nxt.t2fnext.in.last_name = result.list[1].ea_list.name.s;
1364 	nxt.t2fnext.in.num_names = 2;
1365 	nxt.t2fnext.in.ea_names = talloc_array(mem_ctx, struct ea_name, 2);
1366 	nxt.t2fnext.in.ea_names[0].name.s = "SECOND EA";
1367 	nxt.t2fnext.in.ea_names[1].name.s = "THIRD EA";
1368 
1369 	status = smb_raw_search_next(cli->tree, mem_ctx,
1370 				     &nxt, &result, multiple_search_callback);
1371 	CHECK_STATUS(status, NT_STATUS_OK);
1372 
1373 	/* we have to sort the result as different servers can return directories
1374 	   in different orders */
1375 	qsort(result.list, result.count, sizeof(result.list[0]),
1376 	      (comparison_fn_t)ealist_cmp);
1377 
1378 	CHECK_VALUE(result.count, 3);
1379 	CHECK_VALUE(result.list[0].ea_list.eas.num_eas, 2);
1380 	CHECK_STRING(result.list[0].ea_list.name.s, "file1.txt");
1381 	CHECK_STRING(result.list[0].ea_list.eas.eas[0].name.s, "SECOND EA");
1382 	CHECK_VALUE(result.list[0].ea_list.eas.eas[0].value.length, 0);
1383 	CHECK_STRING(result.list[0].ea_list.eas.eas[1].name.s, "THIRD EA");
1384 	CHECK_VALUE(result.list[0].ea_list.eas.eas[1].value.length, 0);
1385 
1386 	CHECK_STRING(result.list[1].ea_list.name.s, "file2.txt");
1387 	CHECK_STRING(result.list[1].ea_list.eas.eas[0].name.s, "SECOND EA");
1388 	CHECK_VALUE(result.list[1].ea_list.eas.eas[0].value.length, 9);
1389 	CHECK_STRING((const char *)result.list[1].ea_list.eas.eas[0].value.data, "Value Two");
1390 	CHECK_STRING(result.list[1].ea_list.eas.eas[1].name.s, "THIRD EA");
1391 	CHECK_VALUE(result.list[1].ea_list.eas.eas[1].value.length, 0);
1392 
1393 	CHECK_STRING(result.list[2].ea_list.name.s, "file3.txt");
1394 	CHECK_STRING(result.list[2].ea_list.eas.eas[0].name.s, "SECOND EA");
1395 	CHECK_VALUE(result.list[2].ea_list.eas.eas[0].value.length, 9);
1396 	CHECK_STRING((const char *)result.list[2].ea_list.eas.eas[0].value.data, "Value Two");
1397 	CHECK_STRING(result.list[2].ea_list.eas.eas[1].name.s, "THIRD EA");
1398 	CHECK_VALUE(result.list[2].ea_list.eas.eas[1].value.length, 0);
1399 
1400 done:
1401 	smb_raw_exit(cli->session);
1402 	smbcli_deltree(cli->tree, BASEDIR);
1403 
1404 	return ret;
1405 }
1406 
1407 
1408 
1409 /*
1410    basic testing of all RAW_SEARCH_* calls using a single file
1411 */
torture_raw_search(struct torture_context * torture)1412 BOOL torture_raw_search(struct torture_context *torture)
1413 {
1414 	struct smbcli_state *cli;
1415 	BOOL ret = True;
1416 	TALLOC_CTX *mem_ctx;
1417 
1418 	if (!torture_open_connection(&cli, 0)) {
1419 		return False;
1420 	}
1421 
1422 	mem_ctx = talloc_init("torture_search");
1423 
1424 	ret &= test_one_file(cli, mem_ctx);
1425 	ret &= test_many_files(cli, mem_ctx);
1426 	ret &= test_sorted(cli, mem_ctx);
1427 	ret &= test_modify_search(cli, mem_ctx);
1428 	ret &= test_many_dirs(cli, mem_ctx);
1429 	ret &= test_os2_delete(cli, mem_ctx);
1430 	ret &= test_ea_list(cli, mem_ctx);
1431 
1432 	torture_close_connection(cli);
1433 	talloc_free(mem_ctx);
1434 
1435 	return ret;
1436 }
1437