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