2 Unix SMB/CIFS implementation.
3 RAW_FILEINFO_* individual test suite
4 Copyright (C) Andrew Tridgell 2003
5 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "torture/torture.h"
23 #include "libcli/raw/libcliraw.h"
24 #include "libcli/libcli.h"
25 #include "torture/util.h"
26 #include "librpc/rpc/dcerpc.h"
27 #include "torture/rpc/rpc.h"
28 #include "torture/raw/proto.h"
32 enum smb_fileinfo_level level;
34 uint_t only_handles:1;
35 uint32_t capability_mask;
36 uint_t expected_ipc_access_denied:1;
37 NTSTATUS expected_ipc_fnum_status;
38 NTSTATUS fnum_status, fname_status;
39 union smb_fileinfo fnum_finfo, fname_finfo;
42 .level = RAW_FILEINFO_GETATTR,
45 .expected_ipc_access_denied = 1},
47 .level = RAW_FILEINFO_GETATTRE,
51 .level = RAW_FILEINFO_STANDARD, },
53 .level = RAW_FILEINFO_EA_SIZE },
55 .level = RAW_FILEINFO_ALL_EAS,
56 .expected_ipc_fnum_status = NT_STATUS_ACCESS_DENIED,
58 { .name ="IS_NAME_VALID",
59 .level = RAW_FILEINFO_IS_NAME_VALID,
62 { .name ="BASIC_INFO",
63 .level = RAW_FILEINFO_BASIC_INFO },
64 { .name ="STANDARD_INFO",
65 .level = RAW_FILEINFO_STANDARD_INFO },
67 .level = RAW_FILEINFO_EA_INFO },
69 .level = RAW_FILEINFO_NAME_INFO },
71 .level = RAW_FILEINFO_ALL_INFO },
72 { .name ="ALT_NAME_INFO",
73 .level = RAW_FILEINFO_ALT_NAME_INFO,
74 .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER
76 { .name ="STREAM_INFO",
77 .level = RAW_FILEINFO_STREAM_INFO,
78 .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER
80 { .name ="COMPRESSION_INFO",
81 .level = RAW_FILEINFO_COMPRESSION_INFO,
82 .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER
84 { .name ="UNIX_BASIC_INFO",
85 .level = RAW_FILEINFO_UNIX_BASIC,
88 .capability_mask = CAP_UNIX},
89 { .name ="UNIX_LINK_INFO",
90 .level = RAW_FILEINFO_UNIX_LINK,
93 .capability_mask = CAP_UNIX},
94 { .name ="BASIC_INFORMATION",
95 .level = RAW_FILEINFO_BASIC_INFORMATION },
96 { .name ="STANDARD_INFORMATION",
97 .level = RAW_FILEINFO_STANDARD_INFORMATION },
98 { .name ="INTERNAL_INFORMATION",
99 .level = RAW_FILEINFO_INTERNAL_INFORMATION },
100 { .name ="EA_INFORMATION",
101 .level = RAW_FILEINFO_EA_INFORMATION },
102 { .name = "ACCESS_INFORMATION",
103 .level = RAW_FILEINFO_ACCESS_INFORMATION },
104 { .name = "NAME_INFORMATION",
105 .level = RAW_FILEINFO_NAME_INFORMATION },
106 { .name ="POSITION_INFORMATION",
107 .level = RAW_FILEINFO_POSITION_INFORMATION },
108 { .name ="MODE_INFORMATION",
109 .level = RAW_FILEINFO_MODE_INFORMATION },
110 { .name ="ALIGNMENT_INFORMATION",
111 .level = RAW_FILEINFO_ALIGNMENT_INFORMATION },
112 { .name ="ALL_INFORMATION",
113 .level = RAW_FILEINFO_ALL_INFORMATION },
114 { .name ="ALT_NAME_INFORMATION",
115 .level = RAW_FILEINFO_ALT_NAME_INFORMATION,
116 .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER
118 { .name ="STREAM_INFORMATION",
119 .level = RAW_FILEINFO_STREAM_INFORMATION,
120 .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER
122 { .name = "COMPRESSION_INFORMATION",
123 .level = RAW_FILEINFO_COMPRESSION_INFORMATION,
124 .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER
126 { .name ="NETWORK_OPEN_INFORMATION",
127 .level = RAW_FILEINFO_NETWORK_OPEN_INFORMATION,
128 .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER
130 { .name = "ATTRIBUTE_TAG_INFORMATION",
131 .level = RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION,
132 .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER
138 compare a dos time (2 second resolution) to a nt time
140 static int dos_nt_time_cmp(time_t t, NTTIME nt)
142 time_t t2 = nt_time_to_unix(nt);
143 if (abs(t2 - t) <= 2) return 0;
149 find a level in the levels[] table
151 static union smb_fileinfo *fnum_find(const char *name)
154 for (i=0; levels[i].name; i++) {
155 if (NT_STATUS_IS_OK(levels[i].fnum_status) &&
156 strcmp(name, levels[i].name) == 0 &&
157 !levels[i].only_paths) {
158 return &levels[i].fnum_finfo;
165 find a level in the levels[] table
167 static union smb_fileinfo *fname_find(bool is_ipc, const char *name)
173 for (i=0; levels[i].name; i++) {
174 if (NT_STATUS_IS_OK(levels[i].fname_status) &&
175 strcmp(name, levels[i].name) == 0 &&
176 !levels[i].only_handles) {
177 return &levels[i].fname_finfo;
183 /* local macros to make the code below more readable */
184 #define VAL_EQUAL(n1, v1, n2, v2) do {if (s1->n1.out.v1 != s2->n2.out.v2) { \
185 printf("%s/%s [%u] != %s/%s [%u] at %s(%d)\n", \
186 #n1, #v1, (uint_t)s1->n1.out.v1, \
187 #n2, #v2, (uint_t)s2->n2.out.v2, \
188 __FILE__, __LINE__); \
192 #define STR_EQUAL(n1, v1, n2, v2) do {if (strcmp_safe(s1->n1.out.v1.s, s2->n2.out.v2.s) || \
193 s1->n1.out.v1.private_length != s2->n2.out.v2.private_length) { \
194 printf("%s/%s [%s/%d] != %s/%s [%s/%d] at %s(%d)\n", \
195 #n1, #v1, s1->n1.out.v1.s, s1->n1.out.v1.private_length, \
196 #n2, #v2, s2->n2.out.v2.s, s2->n2.out.v2.private_length, \
197 __FILE__, __LINE__); \
201 #define STRUCT_EQUAL(n1, v1, n2, v2) do {if (memcmp(&s1->n1.out.v1,&s2->n2.out.v2,sizeof(s1->n1.out.v1))) { \
202 printf("%s/%s != %s/%s at %s(%d)\n", \
205 __FILE__, __LINE__); \
209 /* used to find hints on unknown values - and to make sure
212 #define VAL_UNKNOWN(n1, v1) do {if (s1->n1.out.v1 != 0) { \
213 printf("%s/%s non-zero unknown - %u (0x%x) at %s(%d)\n", \
215 (uint_t)s1->n1.out.v1, \
216 (uint_t)s1->n1.out.v1, \
217 __FILE__, __LINE__); \
222 /* basic testing of all RAW_FILEINFO_* calls
223 for each call we test that it succeeds, and where possible test
224 for consistency between the calls.
226 static bool torture_raw_qfileinfo_internals(struct torture_context *torture,
228 struct smbcli_tree *tree,
229 int fnum, const char *fname,
235 union smb_fileinfo *s1, *s2;
237 uint64_t correct_size;
238 uint32_t correct_attrib;
239 const char *correct_name;
240 bool skip_streams = false;
242 /* scan all the fileinfo and pathinfo levels */
243 for (i=0; levels[i].name; i++) {
244 if (!levels[i].only_paths) {
245 levels[i].fnum_finfo.generic.level = levels[i].level;
246 levels[i].fnum_finfo.generic.in.file.fnum = fnum;
247 levels[i].fnum_status = smb_raw_fileinfo(tree, mem_ctx,
248 &levels[i].fnum_finfo);
251 if (!levels[i].only_handles) {
252 levels[i].fname_finfo.generic.level = levels[i].level;
253 levels[i].fname_finfo.generic.in.file.path = talloc_strdup(mem_ctx, fname);
254 levels[i].fname_status = smb_raw_pathinfo(tree, mem_ctx,
255 &levels[i].fname_finfo);
259 /* check for completely broken levels */
260 for (count=i=0; levels[i].name; i++) {
261 uint32_t cap = tree->session->transport->negotiate.capabilities;
262 /* see if this server claims to support this level */
263 if ((cap & levels[i].capability_mask) != levels[i].capability_mask) {
268 if (levels[i].expected_ipc_access_denied && NT_STATUS_EQUAL(NT_STATUS_ACCESS_DENIED, levels[i].fname_status)) {
269 } else if (!levels[i].only_handles && !NT_STATUS_EQUAL(NT_STATUS_INVALID_DEVICE_REQUEST, levels[i].fname_status)) {
270 printf("ERROR: fname level %s failed, expected NT_STATUS_INVALID_DEVICE_REQUEST - %s\n",
271 levels[i].name, nt_errstr(levels[i].fname_status));
274 if (!levels[i].only_paths && !NT_STATUS_EQUAL(levels[i].expected_ipc_fnum_status, levels[i].fnum_status)) {
275 printf("ERROR: fnum level %s failed, expected %s - %s\n",
276 levels[i].name, nt_errstr(levels[i].expected_ipc_fnum_status),
277 nt_errstr(levels[i].fnum_status));
281 if (!levels[i].only_paths && !NT_STATUS_IS_OK(levels[i].fnum_status)) {
282 printf("ERROR: fnum level %s failed - %s\n",
283 levels[i].name, nt_errstr(levels[i].fnum_status));
286 if (!levels[i].only_handles && !NT_STATUS_IS_OK(levels[i].fname_status)) {
287 printf("ERROR: fname level %s failed - %s\n",
288 levels[i].name, nt_errstr(levels[i].fname_status));
297 printf("%d levels failed\n", count);
299 torture_fail(torture, "too many level failures - giving up");
303 /* see if we can do streams */
304 s1 = fnum_find("STREAM_INFO");
305 if (!s1 || s1->stream_info.out.num_streams == 0) {
307 printf("STREAM_INFO broken (%d) - skipping streams checks\n",
308 s1 ? s1->stream_info.out.num_streams : -1);
314 /* this code is incredibly repititive but doesn't lend itself to loops, so
315 we use lots of macros to make it less painful */
317 /* first off we check the levels that are supposed to be aliases. It will be quite rare for
318 this code to fail, but we need to check it for completeness */
322 #define ALIAS_CHECK(sname1, sname2) \
324 s1 = fnum_find(sname1); s2 = fnum_find(sname2); \
325 if (s1 && s2) { INFO_CHECK } \
326 s1 = fname_find(is_ipc, sname1); s2 = fname_find(is_ipc, sname2); \
327 if (s1 && s2) { INFO_CHECK } \
328 s1 = fnum_find(sname1); s2 = fname_find(is_ipc, sname2); \
329 if (s1 && s2) { INFO_CHECK } \
333 STRUCT_EQUAL(basic_info, create_time, basic_info, create_time); \
334 STRUCT_EQUAL(basic_info, access_time, basic_info, access_time); \
335 STRUCT_EQUAL(basic_info, write_time, basic_info, write_time); \
336 STRUCT_EQUAL(basic_info, change_time, basic_info, change_time); \
337 VAL_EQUAL (basic_info, attrib, basic_info, attrib);
339 ALIAS_CHECK("BASIC_INFO", "BASIC_INFORMATION");
343 VAL_EQUAL(standard_info, alloc_size, standard_info, alloc_size); \
344 VAL_EQUAL(standard_info, size, standard_info, size); \
345 VAL_EQUAL(standard_info, nlink, standard_info, nlink); \
346 VAL_EQUAL(standard_info, delete_pending, standard_info, delete_pending); \
347 VAL_EQUAL(standard_info, directory, standard_info, directory);
349 ALIAS_CHECK("STANDARD_INFO", "STANDARD_INFORMATION");
353 VAL_EQUAL(ea_info, ea_size, ea_info, ea_size);
355 ALIAS_CHECK("EA_INFO", "EA_INFORMATION");
359 STR_EQUAL(name_info, fname, name_info, fname);
361 ALIAS_CHECK("NAME_INFO", "NAME_INFORMATION");
365 STRUCT_EQUAL(all_info, create_time, all_info, create_time); \
366 STRUCT_EQUAL(all_info, access_time, all_info, access_time); \
367 STRUCT_EQUAL(all_info, write_time, all_info, write_time); \
368 STRUCT_EQUAL(all_info, change_time, all_info, change_time); \
369 VAL_EQUAL(all_info, attrib, all_info, attrib); \
370 VAL_EQUAL(all_info, alloc_size, all_info, alloc_size); \
371 VAL_EQUAL(all_info, size, all_info, size); \
372 VAL_EQUAL(all_info, nlink, all_info, nlink); \
373 VAL_EQUAL(all_info, delete_pending, all_info, delete_pending); \
374 VAL_EQUAL(all_info, directory, all_info, directory); \
375 VAL_EQUAL(all_info, ea_size, all_info, ea_size); \
376 STR_EQUAL(all_info, fname, all_info, fname);
378 ALIAS_CHECK("ALL_INFO", "ALL_INFORMATION");
382 VAL_EQUAL(compression_info, compressed_size,compression_info, compressed_size); \
383 VAL_EQUAL(compression_info, format, compression_info, format); \
384 VAL_EQUAL(compression_info, unit_shift, compression_info, unit_shift); \
385 VAL_EQUAL(compression_info, chunk_shift, compression_info, chunk_shift); \
386 VAL_EQUAL(compression_info, cluster_shift, compression_info, cluster_shift);
388 ALIAS_CHECK("COMPRESSION_INFO", "COMPRESSION_INFORMATION");
393 STR_EQUAL(alt_name_info, fname, alt_name_info, fname);
395 ALIAS_CHECK("ALT_NAME_INFO", "ALT_NAME_INFORMATION");
398 #define TIME_CHECK_NT(sname, stype, tfield) do { \
399 s1 = fnum_find(sname); \
400 if (s1 && memcmp(&s1->stype.out.tfield, &correct_time, sizeof(correct_time)) != 0) { \
401 printf("(%d) handle %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \
402 nt_time_string(mem_ctx, s1->stype.out.tfield), \
403 nt_time_string(mem_ctx, correct_time)); \
406 s1 = fname_find(is_ipc, sname); \
407 if (s1 && memcmp(&s1->stype.out.tfield, &correct_time, sizeof(correct_time)) != 0) { \
408 printf("(%d) path %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \
409 nt_time_string(mem_ctx, s1->stype.out.tfield), \
410 nt_time_string(mem_ctx, correct_time)); \
414 #define TIME_CHECK_DOS(sname, stype, tfield) do { \
415 s1 = fnum_find(sname); \
416 if (s1 && dos_nt_time_cmp(s1->stype.out.tfield, correct_time) != 0) { \
417 printf("(%d) handle %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \
418 timestring(mem_ctx, s1->stype.out.tfield), \
419 nt_time_string(mem_ctx, correct_time)); \
422 s1 = fname_find(is_ipc, sname); \
423 if (s1 && dos_nt_time_cmp(s1->stype.out.tfield, correct_time) != 0) { \
424 printf("(%d) path %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \
425 timestring(mem_ctx, s1->stype.out.tfield), \
426 nt_time_string(mem_ctx, correct_time)); \
431 #define TIME_CHECK_UNX(sname, stype, tfield) do { \
432 s1 = fnum_find(sname); \
433 if (s1 && unx_nt_time_cmp(s1->stype.out.tfield, correct_time) != 0) { \
434 printf("(%d) handle %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \
435 timestring(mem_ctx, s1->stype.out.tfield), \
436 nt_time_string(mem_ctx, correct_time)); \
439 s1 = fname_find(is_ipc, sname); \
440 if (s1 && unx_nt_time_cmp(s1->stype.out.tfield, correct_time) != 0) { \
441 printf("(%d) path %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \
442 timestring(mem_ctx, s1->stype.out.tfield), \
443 nt_time_string(mem_ctx, correct_time)); \
448 /* now check that all the times that are supposed to be equal are correct */
449 s1 = fnum_find("BASIC_INFO");
450 correct_time = s1->basic_info.out.create_time;
451 torture_comment(torture, "create_time: %s\n", nt_time_string(mem_ctx, correct_time));
453 TIME_CHECK_NT ("BASIC_INFO", basic_info, create_time);
454 TIME_CHECK_NT ("BASIC_INFORMATION", basic_info, create_time);
455 TIME_CHECK_DOS("GETATTRE", getattre, create_time);
456 TIME_CHECK_DOS("STANDARD", standard, create_time);
457 TIME_CHECK_DOS("EA_SIZE", ea_size, create_time);
458 TIME_CHECK_NT ("ALL_INFO", all_info, create_time);
459 TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information, create_time);
461 s1 = fnum_find("BASIC_INFO");
462 correct_time = s1->basic_info.out.access_time;
463 torture_comment(torture, "access_time: %s\n", nt_time_string(mem_ctx, correct_time));
465 TIME_CHECK_NT ("BASIC_INFO", basic_info, access_time);
466 TIME_CHECK_NT ("BASIC_INFORMATION", basic_info, access_time);
467 TIME_CHECK_DOS("GETATTRE", getattre, access_time);
468 TIME_CHECK_DOS("STANDARD", standard, access_time);
469 TIME_CHECK_DOS("EA_SIZE", ea_size, access_time);
470 TIME_CHECK_NT ("ALL_INFO", all_info, access_time);
471 TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information, access_time);
473 s1 = fnum_find("BASIC_INFO");
474 correct_time = s1->basic_info.out.write_time;
475 torture_comment(torture, "write_time : %s\n", nt_time_string(mem_ctx, correct_time));
477 TIME_CHECK_NT ("BASIC_INFO", basic_info, write_time);
478 TIME_CHECK_NT ("BASIC_INFORMATION", basic_info, write_time);
479 TIME_CHECK_DOS("GETATTR", getattr, write_time);
480 TIME_CHECK_DOS("GETATTRE", getattre, write_time);
481 TIME_CHECK_DOS("STANDARD", standard, write_time);
482 TIME_CHECK_DOS("EA_SIZE", ea_size, write_time);
483 TIME_CHECK_NT ("ALL_INFO", all_info, write_time);
484 TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information, write_time);
486 s1 = fnum_find("BASIC_INFO");
487 correct_time = s1->basic_info.out.change_time;
488 torture_comment(torture, "change_time: %s\n", nt_time_string(mem_ctx, correct_time));
490 TIME_CHECK_NT ("BASIC_INFO", basic_info, change_time);
491 TIME_CHECK_NT ("BASIC_INFORMATION", basic_info, change_time);
492 TIME_CHECK_NT ("ALL_INFO", all_info, change_time);
493 TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information, change_time);
496 #define SIZE_CHECK(sname, stype, tfield) do { \
497 s1 = fnum_find(sname); \
498 if (s1 && s1->stype.out.tfield != correct_size) { \
499 printf("(%d) handle %s/%s incorrect - %u should be %u\n", __LINE__, #stype, #tfield, \
500 (uint_t)s1->stype.out.tfield, \
501 (uint_t)correct_size); \
504 s1 = fname_find(is_ipc, sname); \
505 if (s1 && s1->stype.out.tfield != correct_size) { \
506 printf("(%d) path %s/%s incorrect - %u should be %u\n", __LINE__, #stype, #tfield, \
507 (uint_t)s1->stype.out.tfield, \
508 (uint_t)correct_size); \
512 s1 = fnum_find("STANDARD_INFO");
513 correct_size = s1->standard_info.out.size;
514 torture_comment(torture, "size: %u\n", (uint_t)correct_size);
516 SIZE_CHECK("GETATTR", getattr, size);
517 SIZE_CHECK("GETATTRE", getattre, size);
518 SIZE_CHECK("STANDARD", standard, size);
519 SIZE_CHECK("EA_SIZE", ea_size, size);
520 SIZE_CHECK("STANDARD_INFO", standard_info, size);
521 SIZE_CHECK("STANDARD_INFORMATION", standard_info, size);
522 SIZE_CHECK("ALL_INFO", all_info, size);
523 SIZE_CHECK("ALL_INFORMATION", all_info, size);
524 SIZE_CHECK("COMPRESSION_INFO", compression_info, compressed_size);
525 SIZE_CHECK("COMPRESSION_INFORMATION", compression_info, compressed_size);
526 SIZE_CHECK("NETWORK_OPEN_INFORMATION", network_open_information, size);
528 SIZE_CHECK("STREAM_INFO", stream_info, streams[0].size);
529 SIZE_CHECK("STREAM_INFORMATION", stream_info, streams[0].size);
533 s1 = fnum_find("STANDARD_INFO");
534 correct_size = s1->standard_info.out.alloc_size;
535 torture_comment(torture, "alloc_size: %u\n", (uint_t)correct_size);
537 SIZE_CHECK("GETATTRE", getattre, alloc_size);
538 SIZE_CHECK("STANDARD", standard, alloc_size);
539 SIZE_CHECK("EA_SIZE", ea_size, alloc_size);
540 SIZE_CHECK("STANDARD_INFO", standard_info, alloc_size);
541 SIZE_CHECK("STANDARD_INFORMATION", standard_info, alloc_size);
542 SIZE_CHECK("ALL_INFO", all_info, alloc_size);
543 SIZE_CHECK("ALL_INFORMATION", all_info, alloc_size);
544 SIZE_CHECK("NETWORK_OPEN_INFORMATION", network_open_information, alloc_size);
546 SIZE_CHECK("STREAM_INFO", stream_info, streams[0].alloc_size);
547 SIZE_CHECK("STREAM_INFORMATION", stream_info, streams[0].alloc_size);
550 #define ATTRIB_CHECK(sname, stype, tfield) do { \
551 s1 = fnum_find(sname); \
552 if (s1 && s1->stype.out.tfield != correct_attrib) { \
553 printf("(%d) handle %s/%s incorrect - 0x%x should be 0x%x\n", __LINE__, #stype, #tfield, \
554 (uint_t)s1->stype.out.tfield, \
555 (uint_t)correct_attrib); \
558 s1 = fname_find(is_ipc, sname); \
559 if (s1 && s1->stype.out.tfield != correct_attrib) { \
560 printf("(%d) path %s/%s incorrect - 0x%x should be 0x%x\n", __LINE__, #stype, #tfield, \
561 (uint_t)s1->stype.out.tfield, \
562 (uint_t)correct_attrib); \
566 s1 = fnum_find("BASIC_INFO");
567 correct_attrib = s1->basic_info.out.attrib;
568 torture_comment(torture, "attrib: 0x%x\n", (uint_t)correct_attrib);
570 ATTRIB_CHECK("GETATTR", getattr, attrib);
572 ATTRIB_CHECK("GETATTRE", getattre, attrib);
573 ATTRIB_CHECK("STANDARD", standard, attrib);
574 ATTRIB_CHECK("EA_SIZE", ea_size, attrib);
576 ATTRIB_CHECK("BASIC_INFO", basic_info, attrib);
577 ATTRIB_CHECK("BASIC_INFORMATION", basic_info, attrib);
578 ATTRIB_CHECK("ALL_INFO", all_info, attrib);
579 ATTRIB_CHECK("ALL_INFORMATION", all_info, attrib);
580 ATTRIB_CHECK("NETWORK_OPEN_INFORMATION", network_open_information, attrib);
581 ATTRIB_CHECK("ATTRIBUTE_TAG_INFORMATION", attribute_tag_information, attrib);
583 correct_name = fname;
584 torture_comment(torture, "name: %s\n", correct_name);
586 #define NAME_CHECK(sname, stype, tfield, flags) do { \
587 s1 = fnum_find(sname); \
588 if (s1 && (strcmp_safe(s1->stype.out.tfield.s, correct_name) != 0 || \
589 wire_bad_flags(&s1->stype.out.tfield, flags, tree->session->transport))) { \
590 printf("(%d) handle %s/%s incorrect - '%s/%d'\n", __LINE__, #stype, #tfield, \
591 s1->stype.out.tfield.s, s1->stype.out.tfield.private_length); \
594 s1 = fname_find(is_ipc, sname); \
595 if (s1 && (strcmp_safe(s1->stype.out.tfield.s, correct_name) != 0 || \
596 wire_bad_flags(&s1->stype.out.tfield, flags, tree->session->transport))) { \
597 printf("(%d) path %s/%s incorrect - '%s/%d'\n", __LINE__, #stype, #tfield, \
598 s1->stype.out.tfield.s, s1->stype.out.tfield.private_length); \
602 NAME_CHECK("NAME_INFO", name_info, fname, STR_UNICODE);
603 NAME_CHECK("NAME_INFORMATION", name_info, fname, STR_UNICODE);
605 /* the ALL_INFO file name is the full path on the filesystem */
606 s1 = fnum_find("ALL_INFO");
607 if (s1 && !s1->all_info.out.fname.s) {
608 torture_fail(torture, "ALL_INFO didn't give a filename");
610 if (s1 && s1->all_info.out.fname.s) {
611 char *p = strrchr(s1->all_info.out.fname.s, '\\');
613 printf("Not a full path in all_info/fname? - '%s'\n",
614 s1->all_info.out.fname.s);
617 if (strcmp_safe(correct_name, p) != 0) {
618 printf("incorrect basename in all_info/fname - '%s'\n",
619 s1->all_info.out.fname.s);
623 if (wire_bad_flags(&s1->all_info.out.fname, STR_UNICODE, tree->session->transport)) {
624 printf("Should not null terminate all_info/fname\n");
629 s1 = fnum_find("ALT_NAME_INFO");
631 correct_name = s1->alt_name_info.out.fname.s;
632 torture_comment(torture, "alt_name: %s\n", correct_name);
634 NAME_CHECK("ALT_NAME_INFO", alt_name_info, fname, STR_UNICODE);
635 NAME_CHECK("ALT_NAME_INFORMATION", alt_name_info, fname, STR_UNICODE);
637 /* and make sure we can open by alternate name */
638 smbcli_close(tree, fnum);
639 fnum = smbcli_nt_create_full(tree, correct_name, 0,
641 FILE_ATTRIBUTE_NORMAL,
642 NTCREATEX_SHARE_ACCESS_DELETE|
643 NTCREATEX_SHARE_ACCESS_READ|
644 NTCREATEX_SHARE_ACCESS_WRITE,
645 NTCREATEX_DISP_OVERWRITE_IF,
648 printf("Unable to open by alt_name - %s\n", smbcli_errstr(tree));
653 correct_name = "::$DATA";
654 torture_comment(torture, "stream_name: %s\n", correct_name);
656 NAME_CHECK("STREAM_INFO", stream_info, streams[0].stream_name, STR_UNICODE);
657 NAME_CHECK("STREAM_INFORMATION", stream_info, streams[0].stream_name, STR_UNICODE);
661 /* make sure the EAs look right */
662 s1 = fnum_find("ALL_EAS");
663 s2 = fnum_find("ALL_INFO");
665 for (i=0;i<s1->all_eas.out.num_eas;i++) {
666 printf(" flags=%d %s=%*.*s\n",
667 s1->all_eas.out.eas[i].flags,
668 s1->all_eas.out.eas[i].name.s,
669 (int)s1->all_eas.out.eas[i].value.length,
670 (int)s1->all_eas.out.eas[i].value.length,
671 s1->all_eas.out.eas[i].value.data);
675 if (s1->all_eas.out.num_eas == 0) {
676 if (s2->all_info.out.ea_size != 0) {
677 printf("ERROR: num_eas==0 but fnum all_info.out.ea_size == %d\n",
678 s2->all_info.out.ea_size);
681 if (s2->all_info.out.ea_size !=
682 ea_list_size(s1->all_eas.out.num_eas, s1->all_eas.out.eas)) {
683 printf("ERROR: ea_list_size=%d != fnum all_info.out.ea_size=%d\n",
684 (int)ea_list_size(s1->all_eas.out.num_eas, s1->all_eas.out.eas),
685 (int)s2->all_info.out.ea_size);
689 s2 = fname_find(is_ipc, "ALL_EAS");
691 VAL_EQUAL(all_eas, num_eas, all_eas, num_eas);
692 for (i=0;i<s1->all_eas.out.num_eas;i++) {
693 VAL_EQUAL(all_eas, eas[i].flags, all_eas, eas[i].flags);
694 STR_EQUAL(all_eas, eas[i].name, all_eas, eas[i].name);
695 VAL_EQUAL(all_eas, eas[i].value.length, all_eas, eas[i].value.length);
699 #define VAL_CHECK(sname1, stype1, tfield1, sname2, stype2, tfield2) do { \
700 s1 = fnum_find(sname1); s2 = fnum_find(sname2); \
701 if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \
702 printf("(%d) handle %s/%s != %s/%s - 0x%x vs 0x%x\n", __LINE__, \
703 #stype1, #tfield1, #stype2, #tfield2, \
704 s1->stype1.out.tfield1, s2->stype2.out.tfield2); \
707 s1 = fname_find(is_ipc, sname1); s2 = fname_find(is_ipc, sname2); \
708 if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \
709 printf("(%d) path %s/%s != %s/%s - 0x%x vs 0x%x\n", __LINE__, \
710 #stype1, #tfield1, #stype2, #tfield2, \
711 s1->stype1.out.tfield1, s2->stype2.out.tfield2); \
714 s1 = fnum_find(sname1); s2 = fname_find(is_ipc, sname2); \
715 if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \
716 printf("(%d) handle %s/%s != path %s/%s - 0x%x vs 0x%x\n", __LINE__, \
717 #stype1, #tfield1, #stype2, #tfield2, \
718 s1->stype1.out.tfield1, s2->stype2.out.tfield2); \
721 s1 = fname_find(is_ipc, sname1); s2 = fnum_find(sname2); \
722 if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \
723 printf("(%d) path %s/%s != handle %s/%s - 0x%x vs 0x%x\n", __LINE__, \
724 #stype1, #tfield1, #stype2, #tfield2, \
725 s1->stype1.out.tfield1, s2->stype2.out.tfield2); \
729 VAL_CHECK("STANDARD_INFO", standard_info, delete_pending,
730 "ALL_INFO", all_info, delete_pending);
731 VAL_CHECK("STANDARD_INFO", standard_info, directory,
732 "ALL_INFO", all_info, directory);
733 VAL_CHECK("STANDARD_INFO", standard_info, nlink,
734 "ALL_INFO", all_info, nlink);
735 s1 = fnum_find("BASIC_INFO");
737 if (s1->basic_info.out.attrib != FILE_ATTRIBUTE_NORMAL) {
738 printf("(%d) attrib basic_info/nlink incorrect - %d should be %d\n", __LINE__, s1->basic_info.out.attrib, FILE_ATTRIBUTE_NORMAL);
742 s1 = fnum_find("STANDARD_INFO");
744 if (s1->standard_info.out.nlink != 1) {
745 printf("(%d) nlinks standard_info/nlink incorrect - %d should be 1\n", __LINE__, s1->standard_info.out.nlink);
748 if (s1->standard_info.out.delete_pending != 1) {
749 printf("(%d) nlinks standard_info/delete_pending incorrect - %d should be 1\n", __LINE__, s1->standard_info.out.delete_pending);
753 VAL_CHECK("EA_INFO", ea_info, ea_size,
754 "ALL_INFO", all_info, ea_size);
756 VAL_CHECK("EA_SIZE", ea_size, ea_size,
757 "ALL_INFO", all_info, ea_size);
760 #define NAME_PATH_CHECK(sname, stype, field) do { \
761 s1 = fname_find(is_ipc, sname); s2 = fnum_find(sname); \
763 VAL_EQUAL(stype, field, stype, field); \
768 s1 = fnum_find("INTERNAL_INFORMATION");
770 torture_comment(torture, "file_id=%.0f\n", (double)s1->internal_information.out.file_id);
773 NAME_PATH_CHECK("INTERNAL_INFORMATION", internal_information, file_id);
774 NAME_PATH_CHECK("POSITION_INFORMATION", position_information, position);
776 printf("fnum pos = %.0f, fname pos = %.0f\n",
777 (double)s2->position_information.out.position,
778 (double)s1->position_information.out.position );
780 NAME_PATH_CHECK("MODE_INFORMATION", mode_information, mode);
781 NAME_PATH_CHECK("ALIGNMENT_INFORMATION", alignment_information, alignment_requirement);
782 NAME_PATH_CHECK("ATTRIBUTE_TAG_INFORMATION", attribute_tag_information, attrib);
783 NAME_PATH_CHECK("ATTRIBUTE_TAG_INFORMATION", attribute_tag_information, reparse_tag);
786 /* these are expected to differ */
787 NAME_PATH_CHECK("ACCESS_INFORMATION", access_information, access_flags);
791 #define UNKNOWN_CHECK(sname, stype, tfield) do { \
792 s1 = fnum_find(sname); \
793 if (s1 && s1->stype.out.tfield != 0) { \
794 printf("(%d) handle %s/%s unknown != 0 (0x%x)\n", __LINE__, \
796 (uint_t)s1->stype.out.tfield); \
798 s1 = fname_find(is_ipc, sname); \
799 if (s1 && s1->stype.out.tfield != 0) { \
800 printf("(%d) path %s/%s unknown != 0 (0x%x)\n", __LINE__, \
802 (uint_t)s1->stype.out.tfield); \
805 /* now get a bit fancier .... */
807 /* when we set the delete disposition then the link count should drop
808 to 0 and delete_pending should be 1 */
813 /* basic testing of all RAW_FILEINFO_* calls
814 for each call we test that it succeeds, and where possible test
815 for consistency between the calls.
817 bool torture_raw_qfileinfo(struct torture_context *torture,
818 struct smbcli_state *cli)
822 const char *fname = "\\torture_qfileinfo.txt";
824 fnum = create_complex_file(cli, torture, fname);
826 printf("ERROR: open of %s failed (%s)\n", fname, smbcli_errstr(cli->tree));
830 ret = torture_raw_qfileinfo_internals(torture, torture, cli->tree, fnum, fname, false /* is_ipc */);
832 smbcli_close(cli->tree, fnum);
833 smbcli_unlink(cli->tree, fname);
838 bool torture_raw_qfileinfo_pipe(struct torture_context *torture,
839 struct smbcli_state *cli)
843 const char *fname = "\\lsass";
844 struct dcerpc_pipe *p;
845 struct smbcli_tree *ipc_tree;
848 if (!(p = dcerpc_pipe_init(torture, cli->tree->session->transport->socket->event.ctx))) {
852 status = dcerpc_pipe_open_smb(p, cli->tree, fname);
853 torture_assert_ntstatus_ok(torture, status, "dcerpc_pipe_open_smb failed");
855 ipc_tree = dcerpc_smb_tree(p->conn);
856 fnum = dcerpc_smb_fnum(p->conn);
858 ret = torture_raw_qfileinfo_internals(torture, torture, ipc_tree, fnum, fname, true /* is_ipc */);