1 /*
2    Unix SMB/CIFS implementation.
3    RAW_FILEINFO_* 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 "libcli/raw/libcliraw.h"
24 #include "libcli/libcli.h"
25 #include "torture/util.h"
26 
27 static struct {
28 	const char *name;
29 	enum smb_fileinfo_level level;
30 	uint_t only_paths:1;
31 	uint_t only_handles:1;
32 	uint32_t capability_mask;
33 	NTSTATUS fnum_status, fname_status;
34 	union smb_fileinfo fnum_finfo, fname_finfo;
35 } levels[] = {
36 	{ "GETATTR",                   RAW_FILEINFO_GETATTR,           1, 0, },
37 	{ "GETATTRE",                  RAW_FILEINFO_GETATTRE,          0, 1, },
38 	{ "STANDARD",                  RAW_FILEINFO_STANDARD, },
39 	{ "EA_SIZE",                   RAW_FILEINFO_EA_SIZE, },
40 	{ "ALL_EAS",                   RAW_FILEINFO_ALL_EAS, },
41 	{ "IS_NAME_VALID",             RAW_FILEINFO_IS_NAME_VALID,     1, 0, },
42 	{ "BASIC_INFO",                RAW_FILEINFO_BASIC_INFO, },
43 	{ "STANDARD_INFO",             RAW_FILEINFO_STANDARD_INFO, },
44 	{ "EA_INFO",                   RAW_FILEINFO_EA_INFO, },
45 	{ "NAME_INFO",                 RAW_FILEINFO_NAME_INFO, },
46 	{ "ALL_INFO",                  RAW_FILEINFO_ALL_INFO, },
47 	{ "ALT_NAME_INFO",             RAW_FILEINFO_ALT_NAME_INFO, },
48 	{ "STREAM_INFO",               RAW_FILEINFO_STREAM_INFO, },
49 	{ "COMPRESSION_INFO",          RAW_FILEINFO_COMPRESSION_INFO, },
50 	{ "UNIX_BASIC_INFO",           RAW_FILEINFO_UNIX_BASIC, 0, 0, CAP_UNIX},
51 	{ "UNIX_LINK_INFO",            RAW_FILEINFO_UNIX_LINK, 0, 0, CAP_UNIX},
52 	{ "BASIC_INFORMATION",         RAW_FILEINFO_BASIC_INFORMATION, },
53 	{ "STANDARD_INFORMATION",      RAW_FILEINFO_STANDARD_INFORMATION, },
54 	{ "INTERNAL_INFORMATION",      RAW_FILEINFO_INTERNAL_INFORMATION, },
55 	{ "EA_INFORMATION",            RAW_FILEINFO_EA_INFORMATION, },
56 	{ "ACCESS_INFORMATION",        RAW_FILEINFO_ACCESS_INFORMATION, },
57 	{ "NAME_INFORMATION",          RAW_FILEINFO_NAME_INFORMATION, },
58 	{ "POSITION_INFORMATION",      RAW_FILEINFO_POSITION_INFORMATION, },
59 	{ "MODE_INFORMATION",          RAW_FILEINFO_MODE_INFORMATION, },
60 	{ "ALIGNMENT_INFORMATION",     RAW_FILEINFO_ALIGNMENT_INFORMATION, },
61 	{ "ALL_INFORMATION",           RAW_FILEINFO_ALL_INFORMATION, },
62 	{ "ALT_NAME_INFORMATION",      RAW_FILEINFO_ALT_NAME_INFORMATION, },
63 	{ "STREAM_INFORMATION",        RAW_FILEINFO_STREAM_INFORMATION, },
64 	{ "COMPRESSION_INFORMATION",   RAW_FILEINFO_COMPRESSION_INFORMATION, },
65 	{ "NETWORK_OPEN_INFORMATION",  RAW_FILEINFO_NETWORK_OPEN_INFORMATION, },
66 	{ "ATTRIBUTE_TAG_INFORMATION", RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION, },
67 	{ NULL, }
68 };
69 
70 /*
71   compare a dos time (2 second resolution) to a nt time
72 */
dos_nt_time_cmp(time_t t,NTTIME nt)73 static int dos_nt_time_cmp(time_t t, NTTIME nt)
74 {
75 	time_t t2 = nt_time_to_unix(nt);
76 	if (abs(t2 - t) <= 2) return 0;
77 	return t2 - t;
78 }
79 
80 
81 /*
82   find a level in the levels[] table
83 */
fnum_find(const char * name)84 static union smb_fileinfo *fnum_find(const char *name)
85 {
86 	int i;
87 	for (i=0; levels[i].name; i++) {
88 		if (NT_STATUS_IS_OK(levels[i].fnum_status) &&
89 		    strcmp(name, levels[i].name) == 0 &&
90 		    !levels[i].only_paths) {
91 			return &levels[i].fnum_finfo;
92 		}
93 	}
94 	return NULL;
95 }
96 
97 /*
98   find a level in the levels[] table
99 */
fname_find(const char * name)100 static union smb_fileinfo *fname_find(const char *name)
101 {
102 	int i;
103 	for (i=0; levels[i].name; i++) {
104 		if (NT_STATUS_IS_OK(levels[i].fname_status) &&
105 		    strcmp(name, levels[i].name) == 0 &&
106 		    !levels[i].only_handles) {
107 			return &levels[i].fname_finfo;
108 		}
109 	}
110 	return NULL;
111 }
112 
113 /* local macros to make the code below more readable */
114 #define VAL_EQUAL(n1, v1, n2, v2) do {if (s1->n1.out.v1 != s2->n2.out.v2) { \
115         printf("%s/%s [%u] != %s/%s [%u] at %s(%d)\n", \
116                #n1, #v1, (uint_t)s1->n1.out.v1, \
117                #n2, #v2, (uint_t)s2->n2.out.v2, \
118 	       __FILE__, __LINE__); \
119         ret = False; \
120 }} while(0)
121 
122 #define STR_EQUAL(n1, v1, n2, v2) do {if (strcmp_safe(s1->n1.out.v1.s, s2->n2.out.v2.s) || \
123 					  s1->n1.out.v1.private_length != s2->n2.out.v2.private_length) { \
124         printf("%s/%s [%s/%d] != %s/%s [%s/%d] at %s(%d)\n", \
125                #n1, #v1, s1->n1.out.v1.s, s1->n1.out.v1.private_length, \
126                #n2, #v2, s2->n2.out.v2.s, s2->n2.out.v2.private_length, \
127 	       __FILE__, __LINE__); \
128         ret = False; \
129 }} while(0)
130 
131 #define STRUCT_EQUAL(n1, v1, n2, v2) do {if (memcmp(&s1->n1.out.v1,&s2->n2.out.v2,sizeof(s1->n1.out.v1))) { \
132         printf("%s/%s != %s/%s at %s(%d)\n", \
133                #n1, #v1, \
134                #n2, #v2, \
135 	       __FILE__, __LINE__); \
136         ret = False; \
137 }} while(0)
138 
139 /* used to find hints on unknown values - and to make sure
140    we zero-fill */
141 #if 0 /* unused */
142 #define VAL_UNKNOWN(n1, v1) do {if (s1->n1.out.v1 != 0) { \
143         printf("%s/%s non-zero unknown - %u (0x%x) at %s(%d)\n", \
144                #n1, #v1, \
145 	       (uint_t)s1->n1.out.v1, \
146 	       (uint_t)s1->n1.out.v1, \
147 	       __FILE__, __LINE__); \
148         ret = False; \
149 }} while(0)
150 #endif
151 
152 /* basic testing of all RAW_FILEINFO_* calls
153    for each call we test that it succeeds, and where possible test
154    for consistency between the calls.
155 */
torture_raw_qfileinfo(struct torture_context * torture)156 BOOL torture_raw_qfileinfo(struct torture_context *torture)
157 {
158 	struct smbcli_state *cli;
159 	int i;
160 	BOOL ret = True;
161 	int count;
162 	union smb_fileinfo *s1, *s2;
163 	TALLOC_CTX *mem_ctx;
164 	int fnum;
165 	const char *fname = "\\torture_qfileinfo.txt";
166 	NTTIME correct_time;
167 	uint64_t correct_size;
168 	uint32_t correct_attrib;
169 	const char *correct_name;
170 	BOOL skip_streams = False;
171 
172 	if (!torture_open_connection(&cli, 0)) {
173 		return False;
174 	}
175 
176 	mem_ctx = talloc_init("torture_qfileinfo");
177 
178 	fnum = create_complex_file(cli, mem_ctx, fname);
179 	if (fnum == -1) {
180 		printf("ERROR: open of %s failed (%s)\n", fname, smbcli_errstr(cli->tree));
181 		ret = False;
182 		goto done;
183 	}
184 
185 
186 	/* scan all the fileinfo and pathinfo levels */
187 	for (i=0; levels[i].name; i++) {
188 		if (!levels[i].only_paths) {
189 			levels[i].fnum_finfo.generic.level = levels[i].level;
190 			levels[i].fnum_finfo.generic.in.file.fnum = fnum;
191 			levels[i].fnum_status = smb_raw_fileinfo(cli->tree, mem_ctx,
192 								 &levels[i].fnum_finfo);
193 		}
194 
195 		if (!levels[i].only_handles) {
196 			levels[i].fname_finfo.generic.level = levels[i].level;
197 			levels[i].fname_finfo.generic.in.file.path = talloc_strdup(mem_ctx, fname);
198 			levels[i].fname_status = smb_raw_pathinfo(cli->tree, mem_ctx,
199 								  &levels[i].fname_finfo);
200 		}
201 	}
202 
203 	/* check for completely broken levels */
204 	for (count=i=0; levels[i].name; i++) {
205 		uint32_t cap = cli->transport->negotiate.capabilities;
206 		/* see if this server claims to support this level */
207 		if ((cap & levels[i].capability_mask) != levels[i].capability_mask) {
208 			continue;
209 		}
210 
211 		if (!levels[i].only_paths && !NT_STATUS_IS_OK(levels[i].fnum_status)) {
212 			printf("ERROR: level %s failed - %s\n",
213 			       levels[i].name, nt_errstr(levels[i].fnum_status));
214 			count++;
215 		}
216 		if (!levels[i].only_handles && !NT_STATUS_IS_OK(levels[i].fname_status)) {
217 			printf("ERROR: level %s failed - %s\n",
218 			       levels[i].name, nt_errstr(levels[i].fname_status));
219 			count++;
220 		}
221 	}
222 
223 	if (count != 0) {
224 		ret = False;
225 		printf("%d levels failed\n", count);
226 		if (count > 35) {
227 			printf("too many level failures - giving up\n");
228 			goto done;
229 		}
230 	}
231 
232 	/* see if we can do streams */
233 	s1 = fnum_find("STREAM_INFO");
234 	if (!s1 || s1->stream_info.out.num_streams == 0) {
235 		printf("STREAM_INFO broken (%d) - skipping streams checks\n",
236 		       s1 ? s1->stream_info.out.num_streams : -1);
237 		skip_streams = True;
238 	}
239 
240 
241 	/* this code is incredibly repititive but doesn't lend itself to loops, so
242 	   we use lots of macros to make it less painful */
243 
244 	/* first off we check the levels that are supposed to be aliases. It will be quite rare for
245 	   this code to fail, but we need to check it for completeness */
246 
247 
248 
249 #define ALIAS_CHECK(sname1, sname2) \
250 	do { \
251 		s1 = fnum_find(sname1);	 s2 = fnum_find(sname2); \
252 		if (s1 && s2) { INFO_CHECK } \
253 		s1 = fname_find(sname1); s2 = fname_find(sname2); \
254 		if (s1 && s2) { INFO_CHECK } \
255 		s1 = fnum_find(sname1);	 s2 = fname_find(sname2); \
256 		if (s1 && s2) { INFO_CHECK } \
257 	} while (0)
258 
259 #define INFO_CHECK \
260 		STRUCT_EQUAL(basic_info,  create_time, basic_info, create_time); \
261 		STRUCT_EQUAL(basic_info,  access_time, basic_info, access_time); \
262 		STRUCT_EQUAL(basic_info,  write_time,  basic_info, write_time); \
263 		STRUCT_EQUAL(basic_info,  change_time, basic_info, change_time); \
264 		VAL_EQUAL   (basic_info,  attrib,      basic_info, attrib);
265 
266 	ALIAS_CHECK("BASIC_INFO", "BASIC_INFORMATION");
267 
268 #undef INFO_CHECK
269 #define INFO_CHECK \
270 		VAL_EQUAL(standard_info,  alloc_size,     standard_info, alloc_size); \
271 		VAL_EQUAL(standard_info,  size,           standard_info, size); \
272 		VAL_EQUAL(standard_info,  nlink,          standard_info, nlink); \
273 		VAL_EQUAL(standard_info,  delete_pending, standard_info, delete_pending); \
274 		VAL_EQUAL(standard_info,  directory,      standard_info, directory);
275 
276 	ALIAS_CHECK("STANDARD_INFO", "STANDARD_INFORMATION");
277 
278 #undef INFO_CHECK
279 #define INFO_CHECK \
280 		VAL_EQUAL(ea_info,  ea_size,     ea_info, ea_size);
281 
282 	ALIAS_CHECK("EA_INFO", "EA_INFORMATION");
283 
284 #undef INFO_CHECK
285 #define INFO_CHECK \
286 		STR_EQUAL(name_info,  fname,   name_info, fname);
287 
288 	ALIAS_CHECK("NAME_INFO", "NAME_INFORMATION");
289 
290 #undef INFO_CHECK
291 #define INFO_CHECK \
292 		STRUCT_EQUAL(all_info,  create_time,           all_info, create_time); \
293 		STRUCT_EQUAL(all_info,  access_time,           all_info, access_time); \
294 		STRUCT_EQUAL(all_info,  write_time,            all_info, write_time); \
295 		STRUCT_EQUAL(all_info,  change_time,           all_info, change_time); \
296 		   VAL_EQUAL(all_info,  attrib,                all_info, attrib); \
297 		   VAL_EQUAL(all_info,  alloc_size,            all_info, alloc_size); \
298 		   VAL_EQUAL(all_info,  size,                  all_info, size); \
299 		   VAL_EQUAL(all_info,  nlink,                 all_info, nlink); \
300 		   VAL_EQUAL(all_info,  delete_pending,        all_info, delete_pending); \
301 		   VAL_EQUAL(all_info,  directory,             all_info, directory); \
302 		   VAL_EQUAL(all_info,  ea_size,               all_info, ea_size); \
303 		   STR_EQUAL(all_info,  fname,                 all_info, fname);
304 
305 	ALIAS_CHECK("ALL_INFO", "ALL_INFORMATION");
306 
307 #undef INFO_CHECK
308 #define INFO_CHECK \
309 		VAL_EQUAL(compression_info, compressed_size,compression_info, compressed_size); \
310 		VAL_EQUAL(compression_info, format,         compression_info, format); \
311 		VAL_EQUAL(compression_info, unit_shift,     compression_info, unit_shift); \
312 		VAL_EQUAL(compression_info, chunk_shift,    compression_info, chunk_shift); \
313 		VAL_EQUAL(compression_info, cluster_shift,  compression_info, cluster_shift);
314 
315 	ALIAS_CHECK("COMPRESSION_INFO", "COMPRESSION_INFORMATION");
316 
317 
318 #undef INFO_CHECK
319 #define INFO_CHECK \
320 		STR_EQUAL(alt_name_info,  fname,   alt_name_info, fname);
321 
322 	ALIAS_CHECK("ALT_NAME_INFO", "ALT_NAME_INFORMATION");
323 
324 
325 #define TIME_CHECK_NT(sname, stype, tfield) do { \
326 	s1 = fnum_find(sname); \
327 	if (s1 && memcmp(&s1->stype.out.tfield, &correct_time, sizeof(correct_time)) != 0) { \
328 		printf("(%d) handle %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield,  \
329 		       nt_time_string(mem_ctx, s1->stype.out.tfield), \
330 		       nt_time_string(mem_ctx, correct_time)); \
331 		ret = False; \
332 	} \
333 	s1 = fname_find(sname); \
334 	if (s1 && memcmp(&s1->stype.out.tfield, &correct_time, sizeof(correct_time)) != 0) { \
335 		printf("(%d) path %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield,  \
336 		       nt_time_string(mem_ctx, s1->stype.out.tfield), \
337 		       nt_time_string(mem_ctx, correct_time)); \
338 		ret = False; \
339 	}} while (0)
340 
341 #define TIME_CHECK_DOS(sname, stype, tfield) do { \
342 	s1 = fnum_find(sname); \
343 	if (s1 && dos_nt_time_cmp(s1->stype.out.tfield, correct_time) != 0) { \
344 		printf("(%d) handle %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield,  \
345 		       timestring(mem_ctx, s1->stype.out.tfield), \
346 		       nt_time_string(mem_ctx, correct_time)); \
347 		ret = False; \
348 	} \
349 	s1 = fname_find(sname); \
350 	if (s1 && dos_nt_time_cmp(s1->stype.out.tfield, correct_time) != 0) { \
351 		printf("(%d) path %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield,  \
352 		       timestring(mem_ctx, s1->stype.out.tfield), \
353 		       nt_time_string(mem_ctx, correct_time)); \
354 		ret = False; \
355 	}} while (0)
356 
357 #if 0 /* unused */
358 #define TIME_CHECK_UNX(sname, stype, tfield) do { \
359 	s1 = fnum_find(sname); \
360 	if (s1 && unx_nt_time_cmp(s1->stype.out.tfield, correct_time) != 0) { \
361 		printf("(%d) handle %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield,  \
362 		       timestring(mem_ctx, s1->stype.out.tfield), \
363 		       nt_time_string(mem_ctx, correct_time)); \
364 		ret = False; \
365 	} \
366 	s1 = fname_find(sname); \
367 	if (s1 && unx_nt_time_cmp(s1->stype.out.tfield, correct_time) != 0) { \
368 		printf("(%d) path %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield,  \
369 		       timestring(mem_ctx, s1->stype.out.tfield), \
370 		       nt_time_string(mem_ctx, correct_time)); \
371 		ret = False; \
372 	}} while (0)
373 #endif
374 
375 	/* now check that all the times that are supposed to be equal are correct */
376 	s1 = fnum_find("BASIC_INFO");
377 	correct_time = s1->basic_info.out.create_time;
378 	printf("create_time: %s\n", nt_time_string(mem_ctx, correct_time));
379 
380 	TIME_CHECK_NT ("BASIC_INFO",               basic_info, create_time);
381 	TIME_CHECK_NT ("BASIC_INFORMATION",        basic_info, create_time);
382 	TIME_CHECK_DOS("GETATTRE",                 getattre,   create_time);
383 	TIME_CHECK_DOS("STANDARD",                 standard,   create_time);
384 	TIME_CHECK_DOS("EA_SIZE",                  ea_size,    create_time);
385 	TIME_CHECK_NT ("ALL_INFO",                 all_info,   create_time);
386 	TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information, create_time);
387 
388 	s1 = fnum_find("BASIC_INFO");
389 	correct_time = s1->basic_info.out.access_time;
390 	printf("access_time: %s\n", nt_time_string(mem_ctx, correct_time));
391 
392 	TIME_CHECK_NT ("BASIC_INFO",               basic_info, access_time);
393 	TIME_CHECK_NT ("BASIC_INFORMATION",        basic_info, access_time);
394 	TIME_CHECK_DOS("GETATTRE",                 getattre,   access_time);
395 	TIME_CHECK_DOS("STANDARD",                 standard,   access_time);
396 	TIME_CHECK_DOS("EA_SIZE",                  ea_size,    access_time);
397 	TIME_CHECK_NT ("ALL_INFO",                 all_info,   access_time);
398 	TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information, access_time);
399 
400 	s1 = fnum_find("BASIC_INFO");
401 	correct_time = s1->basic_info.out.write_time;
402 	printf("write_time : %s\n", nt_time_string(mem_ctx, correct_time));
403 
404 	TIME_CHECK_NT ("BASIC_INFO",               basic_info, write_time);
405 	TIME_CHECK_NT ("BASIC_INFORMATION",        basic_info, write_time);
406 	TIME_CHECK_DOS("GETATTR",                  getattr,    write_time);
407 	TIME_CHECK_DOS("GETATTRE",                 getattre,   write_time);
408 	TIME_CHECK_DOS("STANDARD",                 standard,   write_time);
409 	TIME_CHECK_DOS("EA_SIZE",                  ea_size,    write_time);
410 	TIME_CHECK_NT ("ALL_INFO",                 all_info,   write_time);
411 	TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information, write_time);
412 
413 	s1 = fnum_find("BASIC_INFO");
414 	correct_time = s1->basic_info.out.change_time;
415 	printf("change_time: %s\n", nt_time_string(mem_ctx, correct_time));
416 
417 	TIME_CHECK_NT ("BASIC_INFO",               basic_info, change_time);
418 	TIME_CHECK_NT ("BASIC_INFORMATION",        basic_info, change_time);
419 	TIME_CHECK_NT ("ALL_INFO",                 all_info,   change_time);
420 	TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information, change_time);
421 
422 
423 #define SIZE_CHECK(sname, stype, tfield) do { \
424 	s1 = fnum_find(sname); \
425 	if (s1 && s1->stype.out.tfield != correct_size) { \
426 		printf("(%d) handle %s/%s incorrect - %u should be %u\n", __LINE__, #stype, #tfield,  \
427 		       (uint_t)s1->stype.out.tfield, \
428 		       (uint_t)correct_size); \
429 		ret = False; \
430 	} \
431 	s1 = fname_find(sname); \
432 	if (s1 && s1->stype.out.tfield != correct_size) { \
433 		printf("(%d) path %s/%s incorrect - %u should be %u\n", __LINE__, #stype, #tfield,  \
434 		       (uint_t)s1->stype.out.tfield, \
435 		       (uint_t)correct_size); \
436 		ret = False; \
437 	}} while (0)
438 
439 	s1 = fnum_find("STANDARD_INFO");
440 	correct_size = s1->standard_info.out.size;
441 	printf("size: %u\n", (uint_t)correct_size);
442 
443 	SIZE_CHECK("GETATTR",                  getattr,                  size);
444 	SIZE_CHECK("GETATTRE",                 getattre,                 size);
445 	SIZE_CHECK("STANDARD",                 standard,                 size);
446 	SIZE_CHECK("EA_SIZE",                  ea_size,                  size);
447 	SIZE_CHECK("STANDARD_INFO",            standard_info,            size);
448 	SIZE_CHECK("STANDARD_INFORMATION",     standard_info,            size);
449 	SIZE_CHECK("ALL_INFO",                 all_info,                 size);
450 	SIZE_CHECK("ALL_INFORMATION",          all_info,                 size);
451 	SIZE_CHECK("COMPRESSION_INFO",         compression_info,         compressed_size);
452 	SIZE_CHECK("COMPRESSION_INFORMATION",  compression_info,         compressed_size);
453 	SIZE_CHECK("NETWORK_OPEN_INFORMATION", network_open_information, size);
454 	if (!skip_streams) {
455 		SIZE_CHECK("STREAM_INFO",              stream_info,   streams[0].size);
456 		SIZE_CHECK("STREAM_INFORMATION",       stream_info,   streams[0].size);
457 	}
458 
459 
460 	s1 = fnum_find("STANDARD_INFO");
461 	correct_size = s1->standard_info.out.alloc_size;
462 	printf("alloc_size: %u\n", (uint_t)correct_size);
463 
464 	SIZE_CHECK("GETATTRE",                 getattre,                 alloc_size);
465 	SIZE_CHECK("STANDARD",                 standard,                 alloc_size);
466 	SIZE_CHECK("EA_SIZE",                  ea_size,                  alloc_size);
467 	SIZE_CHECK("STANDARD_INFO",            standard_info,            alloc_size);
468 	SIZE_CHECK("STANDARD_INFORMATION",     standard_info,            alloc_size);
469 	SIZE_CHECK("ALL_INFO",                 all_info,                 alloc_size);
470 	SIZE_CHECK("ALL_INFORMATION",          all_info,                 alloc_size);
471 	SIZE_CHECK("NETWORK_OPEN_INFORMATION", network_open_information, alloc_size);
472 	if (!skip_streams) {
473 		SIZE_CHECK("STREAM_INFO",              stream_info,   streams[0].alloc_size);
474 		SIZE_CHECK("STREAM_INFORMATION",       stream_info,   streams[0].alloc_size);
475 	}
476 
477 #define ATTRIB_CHECK(sname, stype, tfield) do { \
478 	s1 = fnum_find(sname); \
479 	if (s1 && s1->stype.out.tfield != correct_attrib) { \
480 		printf("(%d) handle %s/%s incorrect - 0x%x should be 0x%x\n", __LINE__, #stype, #tfield,  \
481 		       (uint_t)s1->stype.out.tfield, \
482 		       (uint_t)correct_attrib); \
483 		ret = False; \
484 	} \
485 	s1 = fname_find(sname); \
486 	if (s1 && s1->stype.out.tfield != correct_attrib) { \
487 		printf("(%d) path %s/%s incorrect - 0x%x should be 0x%x\n", __LINE__, #stype, #tfield,  \
488 		       (uint_t)s1->stype.out.tfield, \
489 		       (uint_t)correct_attrib); \
490 		ret = False; \
491 	}} while (0)
492 
493 	s1 = fnum_find("BASIC_INFO");
494 	correct_attrib = s1->basic_info.out.attrib;
495 	printf("attrib: 0x%x\n", (uint_t)correct_attrib);
496 
497 	ATTRIB_CHECK("GETATTR",                   getattr,                   attrib);
498 	ATTRIB_CHECK("GETATTRE",                  getattre,                  attrib);
499 	ATTRIB_CHECK("STANDARD",                  standard,                  attrib);
500 	ATTRIB_CHECK("BASIC_INFO",                basic_info,                attrib);
501 	ATTRIB_CHECK("BASIC_INFORMATION",         basic_info,                attrib);
502 	ATTRIB_CHECK("EA_SIZE",                   ea_size,                   attrib);
503 	ATTRIB_CHECK("ALL_INFO",                  all_info,                  attrib);
504 	ATTRIB_CHECK("ALL_INFORMATION",           all_info,                  attrib);
505 	ATTRIB_CHECK("NETWORK_OPEN_INFORMATION",  network_open_information,  attrib);
506 	ATTRIB_CHECK("ATTRIBUTE_TAG_INFORMATION", attribute_tag_information, attrib);
507 
508 	correct_name = fname;
509 	printf("name: %s\n", correct_name);
510 
511 #define NAME_CHECK(sname, stype, tfield, flags) do { \
512 	s1 = fnum_find(sname); \
513 	if (s1 && (strcmp_safe(s1->stype.out.tfield.s, correct_name) != 0 || \
514 	    		wire_bad_flags(&s1->stype.out.tfield, flags, cli))) { \
515 		printf("(%d) handle %s/%s incorrect - '%s/%d'\n", __LINE__, #stype, #tfield,  \
516 		       s1->stype.out.tfield.s, s1->stype.out.tfield.private_length); \
517 		ret = False; \
518 	} \
519 	s1 = fname_find(sname); \
520 	if (s1 && (strcmp_safe(s1->stype.out.tfield.s, correct_name) != 0 || \
521 	    		wire_bad_flags(&s1->stype.out.tfield, flags, cli))) { \
522 		printf("(%d) path %s/%s incorrect - '%s/%d'\n", __LINE__, #stype, #tfield,  \
523 		       s1->stype.out.tfield.s, s1->stype.out.tfield.private_length); \
524 		ret = False; \
525 	}} while (0)
526 
527 	NAME_CHECK("NAME_INFO",        name_info, fname, STR_UNICODE);
528 	NAME_CHECK("NAME_INFORMATION", name_info, fname, STR_UNICODE);
529 
530 	/* the ALL_INFO file name is the full path on the filesystem */
531 	s1 = fnum_find("ALL_INFO");
532 	if (s1 && !s1->all_info.out.fname.s) {
533 		printf("ALL_INFO didn't give a filename\n");
534 		ret = False;
535 	}
536 	if (s1 && s1->all_info.out.fname.s) {
537 		char *p = strrchr(s1->all_info.out.fname.s, '\\');
538 		if (!p) {
539 			printf("Not a full path in all_info/fname? - '%s'\n",
540 			       s1->all_info.out.fname.s);
541 			ret = False;
542 		} else {
543 			if (strcmp_safe(correct_name, p) != 0) {
544 				printf("incorrect basename in all_info/fname - '%s'\n",
545 				       s1->all_info.out.fname.s);
546 				ret = False;
547 			}
548 		}
549 		if (wire_bad_flags(&s1->all_info.out.fname, STR_UNICODE, cli)) {
550 			printf("Should not null terminate all_info/fname\n");
551 			ret = False;
552 		}
553 	}
554 
555 	s1 = fnum_find("ALT_NAME_INFO");
556 	correct_name = s1->alt_name_info.out.fname.s;
557 	printf("alt_name: %s\n", correct_name);
558 
559 	NAME_CHECK("ALT_NAME_INFO",        alt_name_info, fname, STR_UNICODE);
560 	NAME_CHECK("ALT_NAME_INFORMATION", alt_name_info, fname, STR_UNICODE);
561 
562 	/* and make sure we can open by alternate name */
563 	smbcli_close(cli->tree, fnum);
564 	fnum = smbcli_nt_create_full(cli->tree, correct_name, 0,
565 				     SEC_RIGHTS_FILE_ALL,
566 				     FILE_ATTRIBUTE_NORMAL,
567 				     NTCREATEX_SHARE_ACCESS_DELETE|
568 				     NTCREATEX_SHARE_ACCESS_READ|
569 				     NTCREATEX_SHARE_ACCESS_WRITE,
570 				     NTCREATEX_DISP_OVERWRITE_IF,
571 				     0, 0);
572 	if (fnum == -1) {
573 		printf("Unable to open by alt_name - %s\n", smbcli_errstr(cli->tree));
574 		ret = False;
575 	}
576 
577 	if (!skip_streams) {
578 		correct_name = "::$DATA";
579 		printf("stream_name: %s\n", correct_name);
580 
581 		NAME_CHECK("STREAM_INFO",        stream_info, streams[0].stream_name, STR_UNICODE);
582 		NAME_CHECK("STREAM_INFORMATION", stream_info, streams[0].stream_name, STR_UNICODE);
583 	}
584 
585 	/* make sure the EAs look right */
586 	s1 = fnum_find("ALL_EAS");
587 	s2 = fnum_find("ALL_INFO");
588 	if (s1) {
589 		for (i=0;i<s1->all_eas.out.num_eas;i++) {
590 			printf("  flags=%d %s=%*.*s\n",
591 			       s1->all_eas.out.eas[i].flags,
592 			       s1->all_eas.out.eas[i].name.s,
593 			       (int)s1->all_eas.out.eas[i].value.length,
594 			       (int)s1->all_eas.out.eas[i].value.length,
595 			       s1->all_eas.out.eas[i].value.data);
596 		}
597 	}
598 	if (s1 && s2) {
599 		if (s1->all_eas.out.num_eas == 0) {
600 			if (s2->all_info.out.ea_size != 0) {
601 				printf("ERROR: num_eas==0 but fnum all_info.out.ea_size == %d\n",
602 				       s2->all_info.out.ea_size);
603 			}
604 		} else {
605 			if (s2->all_info.out.ea_size !=
606 			    ea_list_size(s1->all_eas.out.num_eas, s1->all_eas.out.eas)) {
607 				printf("ERROR: ea_list_size=%d != fnum all_info.out.ea_size=%d\n",
608 				       (int)ea_list_size(s1->all_eas.out.num_eas, s1->all_eas.out.eas),
609 				       (int)s2->all_info.out.ea_size);
610 			}
611 		}
612 	}
613 	s2 = fname_find("ALL_EAS");
614 	if (s2) {
615 		VAL_EQUAL(all_eas, num_eas, all_eas, num_eas);
616 		for (i=0;i<s1->all_eas.out.num_eas;i++) {
617 			VAL_EQUAL(all_eas, eas[i].flags, all_eas, eas[i].flags);
618 			STR_EQUAL(all_eas, eas[i].name, all_eas, eas[i].name);
619 			VAL_EQUAL(all_eas, eas[i].value.length, all_eas, eas[i].value.length);
620 		}
621 	}
622 
623 #define VAL_CHECK(sname1, stype1, tfield1, sname2, stype2, tfield2) do { \
624 	s1 = fnum_find(sname1); s2 = fnum_find(sname2); \
625 	if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \
626 		printf("(%d) handle %s/%s != %s/%s - 0x%x vs 0x%x\n", __LINE__, \
627                        #stype1, #tfield1, #stype2, #tfield2,  \
628 		       s1->stype1.out.tfield1, s2->stype2.out.tfield2); \
629 		ret = False; \
630 	} \
631 	s1 = fname_find(sname1); s2 = fname_find(sname2); \
632 	if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \
633 		printf("(%d) path %s/%s != %s/%s - 0x%x vs 0x%x\n", __LINE__, \
634                        #stype1, #tfield1, #stype2, #tfield2,  \
635 		       s1->stype1.out.tfield1, s2->stype2.out.tfield2); \
636 		ret = False; \
637 	} \
638 	s1 = fnum_find(sname1); s2 = fname_find(sname2); \
639 	if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \
640 		printf("(%d) handle %s/%s != path %s/%s - 0x%x vs 0x%x\n", __LINE__, \
641                        #stype1, #tfield1, #stype2, #tfield2,  \
642 		       s1->stype1.out.tfield1, s2->stype2.out.tfield2); \
643 		ret = False; \
644 	} \
645 	s1 = fname_find(sname1); s2 = fnum_find(sname2); \
646 	if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \
647 		printf("(%d) path %s/%s != handle %s/%s - 0x%x vs 0x%x\n", __LINE__, \
648                        #stype1, #tfield1, #stype2, #tfield2,  \
649 		       s1->stype1.out.tfield1, s2->stype2.out.tfield2); \
650 		ret = False; \
651 	}} while (0)
652 
653 	VAL_CHECK("STANDARD_INFO", standard_info, delete_pending,
654 		  "ALL_INFO",      all_info,      delete_pending);
655 	VAL_CHECK("STANDARD_INFO", standard_info, directory,
656 		  "ALL_INFO",      all_info,      directory);
657 	VAL_CHECK("STANDARD_INFO", standard_info, nlink,
658 		  "ALL_INFO",      all_info,      nlink);
659 	VAL_CHECK("EA_INFO",       ea_info,       ea_size,
660 		  "ALL_INFO",      all_info,      ea_size);
661 	VAL_CHECK("EA_SIZE",       ea_size,       ea_size,
662 		  "ALL_INFO",      all_info,      ea_size);
663 
664 
665 #define NAME_PATH_CHECK(sname, stype, field) do { \
666 	s1 = fname_find(sname); s2 = fnum_find(sname); \
667         if (s1 && s2) { \
668 		VAL_EQUAL(stype, field, stype, field); \
669 	} \
670 } while (0)
671 
672 
673 	s1 = fnum_find("INTERNAL_INFORMATION");
674 	if (s1) {
675 		printf("file_id=%.0f\n", (double)s1->internal_information.out.file_id);
676 	}
677 
678 	NAME_PATH_CHECK("INTERNAL_INFORMATION", internal_information, file_id);
679 	NAME_PATH_CHECK("POSITION_INFORMATION", position_information, position);
680 	if (s1 && s2) {
681 		printf("fnum pos = %.0f, fname pos = %.0f\n",
682 		       (double)s2->position_information.out.position,
683 		       (double)s1->position_information.out.position );
684 	}
685 	NAME_PATH_CHECK("MODE_INFORMATION", mode_information, mode);
686 	NAME_PATH_CHECK("ALIGNMENT_INFORMATION", alignment_information, alignment_requirement);
687 	NAME_PATH_CHECK("ATTRIBUTE_TAG_INFORMATION", attribute_tag_information, attrib);
688 	NAME_PATH_CHECK("ATTRIBUTE_TAG_INFORMATION", attribute_tag_information, reparse_tag);
689 
690 #if 0
691 	/* these are expected to differ */
692 	NAME_PATH_CHECK("ACCESS_INFORMATION", access_information, access_flags);
693 #endif
694 
695 #if 0 /* unused */
696 #define UNKNOWN_CHECK(sname, stype, tfield) do { \
697 	s1 = fnum_find(sname); \
698 	if (s1 && s1->stype.out.tfield != 0) { \
699 		printf("(%d) handle %s/%s unknown != 0 (0x%x)\n", __LINE__, \
700                        #stype, #tfield, \
701 		       (uint_t)s1->stype.out.tfield); \
702 	} \
703 	s1 = fname_find(sname); \
704 	if (s1 && s1->stype.out.tfield != 0) { \
705 		printf("(%d) path %s/%s unknown != 0 (0x%x)\n", __LINE__, \
706                        #stype, #tfield, \
707 		       (uint_t)s1->stype.out.tfield); \
708 	}} while (0)
709 #endif
710 	/* now get a bit fancier .... */
711 
712 	/* when we set the delete disposition then the link count should drop
713 	   to 0 and delete_pending should be 1 */
714 
715 
716 done:
717 	smbcli_close(cli->tree, fnum);
718 	smbcli_unlink(cli->tree, fname);
719 
720 	torture_close_connection(cli);
721 	talloc_free(mem_ctx);
722 	return ret;
723 }
724