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 "libcli/raw/libcliraw.h"
23 #include "libcli/raw/raw_proto.h"
24 #include "libcli/libcli.h"
25 #include "torture/util.h"
26 #include "torture/rpc/torture_rpc.h"
27 #include "param/param.h"
28 #include "torture/raw/proto.h"
32 enum smb_fileinfo_level level
;
33 unsigned int only_paths
:1;
34 unsigned int only_handles
:1;
35 uint32_t capability_mask
;
36 unsigned int 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;
144 return t2
> t
? 1 : -1;
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, (unsigned int)s1->n1.out.v1, \
187 #n2, #v2, (unsigned int)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 (unsigned int)s1->n1.out.v1, \
216 (unsigned int)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
&&
270 NT_STATUS_EQUAL(levels
[i
].fname_status
,
271 NT_STATUS_NOT_SUPPORTED
)) {
272 torture_warning(torture
, "fname level %s %s",
274 nt_errstr(levels
[i
].fname_status
));
276 } else if (!levels
[i
].only_handles
&& !NT_STATUS_EQUAL(NT_STATUS_INVALID_DEVICE_REQUEST
, levels
[i
].fname_status
)) {
277 printf("ERROR: fname level %s failed, expected NT_STATUS_INVALID_DEVICE_REQUEST - %s\n",
278 levels
[i
].name
, nt_errstr(levels
[i
].fname_status
));
281 if (!levels
[i
].only_paths
&&
282 (NT_STATUS_EQUAL(levels
[i
].fnum_status
,
283 NT_STATUS_NOT_SUPPORTED
) ||
284 NT_STATUS_EQUAL(levels
[i
].fnum_status
,
285 NT_STATUS_NOT_IMPLEMENTED
))) {
286 torture_warning(torture
, "fnum level %s %s",
288 nt_errstr(levels
[i
].fnum_status
));
291 if (!levels
[i
].only_paths
&& !NT_STATUS_EQUAL(levels
[i
].expected_ipc_fnum_status
, levels
[i
].fnum_status
)) {
292 printf("ERROR: fnum level %s failed, expected %s - %s\n",
293 levels
[i
].name
, nt_errstr(levels
[i
].expected_ipc_fnum_status
),
294 nt_errstr(levels
[i
].fnum_status
));
298 if (!levels
[i
].only_paths
&&
299 (NT_STATUS_EQUAL(levels
[i
].fnum_status
,
300 NT_STATUS_NOT_SUPPORTED
) ||
301 NT_STATUS_EQUAL(levels
[i
].fnum_status
,
302 NT_STATUS_NOT_IMPLEMENTED
))) {
303 torture_warning(torture
, "fnum level %s %s",
305 nt_errstr(levels
[i
].fnum_status
));
309 if (!levels
[i
].only_handles
&&
310 (NT_STATUS_EQUAL(levels
[i
].fname_status
,
311 NT_STATUS_NOT_SUPPORTED
) ||
312 NT_STATUS_EQUAL(levels
[i
].fname_status
,
313 NT_STATUS_NOT_IMPLEMENTED
))) {
314 torture_warning(torture
, "fname level %s %s",
316 nt_errstr(levels
[i
].fname_status
));
320 if (!levels
[i
].only_paths
&& !NT_STATUS_IS_OK(levels
[i
].fnum_status
)) {
321 printf("ERROR: fnum level %s failed - %s\n",
322 levels
[i
].name
, nt_errstr(levels
[i
].fnum_status
));
325 if (!levels
[i
].only_handles
&& !NT_STATUS_IS_OK(levels
[i
].fname_status
)) {
326 printf("ERROR: fname level %s failed - %s\n",
327 levels
[i
].name
, nt_errstr(levels
[i
].fname_status
));
336 printf("%d levels failed\n", count
);
338 torture_fail(torture
, "too many level failures - giving up");
342 /* see if we can do streams */
343 s1
= fnum_find("STREAM_INFO");
344 if (!s1
|| s1
->stream_info
.out
.num_streams
== 0) {
346 printf("STREAM_INFO broken (%d) - skipping streams checks\n",
347 s1
? s1
->stream_info
.out
.num_streams
: -1);
353 /* this code is incredibly repititive but doesn't lend itself to loops, so
354 we use lots of macros to make it less painful */
356 /* first off we check the levels that are supposed to be aliases. It will be quite rare for
357 this code to fail, but we need to check it for completeness */
361 #define ALIAS_CHECK(sname1, sname2) \
363 s1 = fnum_find(sname1); s2 = fnum_find(sname2); \
364 if (s1 && s2) { INFO_CHECK } \
365 s1 = fname_find(is_ipc, sname1); s2 = fname_find(is_ipc, sname2); \
366 if (s1 && s2) { INFO_CHECK } \
367 s1 = fnum_find(sname1); s2 = fname_find(is_ipc, sname2); \
368 if (s1 && s2) { INFO_CHECK } \
372 STRUCT_EQUAL(basic_info, create_time, basic_info, create_time); \
373 STRUCT_EQUAL(basic_info, access_time, basic_info, access_time); \
374 STRUCT_EQUAL(basic_info, write_time, basic_info, write_time); \
375 STRUCT_EQUAL(basic_info, change_time, basic_info, change_time); \
376 VAL_EQUAL (basic_info, attrib, basic_info, attrib);
378 ALIAS_CHECK("BASIC_INFO", "BASIC_INFORMATION");
382 VAL_EQUAL(standard_info, alloc_size, standard_info, alloc_size); \
383 VAL_EQUAL(standard_info, size, standard_info, size); \
384 VAL_EQUAL(standard_info, nlink, standard_info, nlink); \
385 VAL_EQUAL(standard_info, delete_pending, standard_info, delete_pending); \
386 VAL_EQUAL(standard_info, directory, standard_info, directory);
388 ALIAS_CHECK("STANDARD_INFO", "STANDARD_INFORMATION");
392 VAL_EQUAL(ea_info, ea_size, ea_info, ea_size);
394 ALIAS_CHECK("EA_INFO", "EA_INFORMATION");
398 STR_EQUAL(name_info, fname, name_info, fname);
400 ALIAS_CHECK("NAME_INFO", "NAME_INFORMATION");
404 STRUCT_EQUAL(all_info, create_time, all_info, create_time); \
405 STRUCT_EQUAL(all_info, access_time, all_info, access_time); \
406 STRUCT_EQUAL(all_info, write_time, all_info, write_time); \
407 STRUCT_EQUAL(all_info, change_time, all_info, change_time); \
408 VAL_EQUAL(all_info, attrib, all_info, attrib); \
409 VAL_EQUAL(all_info, alloc_size, all_info, alloc_size); \
410 VAL_EQUAL(all_info, size, all_info, size); \
411 VAL_EQUAL(all_info, nlink, all_info, nlink); \
412 VAL_EQUAL(all_info, delete_pending, all_info, delete_pending); \
413 VAL_EQUAL(all_info, directory, all_info, directory); \
414 VAL_EQUAL(all_info, ea_size, all_info, ea_size); \
415 STR_EQUAL(all_info, fname, all_info, fname);
417 ALIAS_CHECK("ALL_INFO", "ALL_INFORMATION");
421 VAL_EQUAL(compression_info, compressed_size,compression_info, compressed_size); \
422 VAL_EQUAL(compression_info, format, compression_info, format); \
423 VAL_EQUAL(compression_info, unit_shift, compression_info, unit_shift); \
424 VAL_EQUAL(compression_info, chunk_shift, compression_info, chunk_shift); \
425 VAL_EQUAL(compression_info, cluster_shift, compression_info, cluster_shift);
427 ALIAS_CHECK("COMPRESSION_INFO", "COMPRESSION_INFORMATION");
432 STR_EQUAL(alt_name_info, fname, alt_name_info, fname);
434 ALIAS_CHECK("ALT_NAME_INFO", "ALT_NAME_INFORMATION");
437 #define TIME_CHECK_NT(sname, stype, tfield) do { \
438 s1 = fnum_find(sname); \
439 if (s1 && memcmp(&s1->stype.out.tfield, &correct_time, sizeof(correct_time)) != 0) { \
440 printf("(%d) handle %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \
441 nt_time_string(mem_ctx, s1->stype.out.tfield), \
442 nt_time_string(mem_ctx, correct_time)); \
445 s1 = fname_find(is_ipc, sname); \
446 if (s1 && memcmp(&s1->stype.out.tfield, &correct_time, sizeof(correct_time)) != 0) { \
447 printf("(%d) path %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \
448 nt_time_string(mem_ctx, s1->stype.out.tfield), \
449 nt_time_string(mem_ctx, correct_time)); \
453 #define TIME_CHECK_DOS(sname, stype, tfield) do { \
454 s1 = fnum_find(sname); \
455 if (s1 && dos_nt_time_cmp(s1->stype.out.tfield, correct_time) != 0) { \
456 printf("(%d) handle %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \
457 timestring(mem_ctx, s1->stype.out.tfield), \
458 nt_time_string(mem_ctx, correct_time)); \
461 s1 = fname_find(is_ipc, sname); \
462 if (s1 && dos_nt_time_cmp(s1->stype.out.tfield, correct_time) != 0) { \
463 printf("(%d) path %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \
464 timestring(mem_ctx, s1->stype.out.tfield), \
465 nt_time_string(mem_ctx, correct_time)); \
470 #define TIME_CHECK_UNX(sname, stype, tfield) do { \
471 s1 = fnum_find(sname); \
472 if (s1 && unx_nt_time_cmp(s1->stype.out.tfield, correct_time) != 0) { \
473 printf("(%d) handle %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \
474 timestring(mem_ctx, s1->stype.out.tfield), \
475 nt_time_string(mem_ctx, correct_time)); \
478 s1 = fname_find(is_ipc, sname); \
479 if (s1 && unx_nt_time_cmp(s1->stype.out.tfield, correct_time) != 0) { \
480 printf("(%d) path %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \
481 timestring(mem_ctx, s1->stype.out.tfield), \
482 nt_time_string(mem_ctx, correct_time)); \
487 /* now check that all the times that are supposed to be equal are correct */
488 s1
= fnum_find("BASIC_INFO");
489 correct_time
= s1
->basic_info
.out
.create_time
;
490 torture_comment(torture
, "create_time: %s\n", nt_time_string(mem_ctx
, correct_time
));
492 TIME_CHECK_NT ("BASIC_INFO", basic_info
, create_time
);
493 TIME_CHECK_NT ("BASIC_INFORMATION", basic_info
, create_time
);
494 TIME_CHECK_DOS("GETATTRE", getattre
, create_time
);
495 TIME_CHECK_DOS("STANDARD", standard
, create_time
);
496 TIME_CHECK_DOS("EA_SIZE", ea_size
, create_time
);
497 TIME_CHECK_NT ("ALL_INFO", all_info
, create_time
);
498 TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information
, create_time
);
500 s1
= fnum_find("BASIC_INFO");
501 correct_time
= s1
->basic_info
.out
.access_time
;
502 torture_comment(torture
, "access_time: %s\n", nt_time_string(mem_ctx
, correct_time
));
504 TIME_CHECK_NT ("BASIC_INFO", basic_info
, access_time
);
505 TIME_CHECK_NT ("BASIC_INFORMATION", basic_info
, access_time
);
506 TIME_CHECK_DOS("GETATTRE", getattre
, access_time
);
507 TIME_CHECK_DOS("STANDARD", standard
, access_time
);
508 TIME_CHECK_DOS("EA_SIZE", ea_size
, access_time
);
509 TIME_CHECK_NT ("ALL_INFO", all_info
, access_time
);
510 TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information
, access_time
);
512 s1
= fnum_find("BASIC_INFO");
513 correct_time
= s1
->basic_info
.out
.write_time
;
514 torture_comment(torture
, "write_time : %s\n", nt_time_string(mem_ctx
, correct_time
));
516 TIME_CHECK_NT ("BASIC_INFO", basic_info
, write_time
);
517 TIME_CHECK_NT ("BASIC_INFORMATION", basic_info
, write_time
);
518 TIME_CHECK_DOS("GETATTR", getattr
, write_time
);
519 TIME_CHECK_DOS("GETATTRE", getattre
, write_time
);
520 TIME_CHECK_DOS("STANDARD", standard
, write_time
);
521 TIME_CHECK_DOS("EA_SIZE", ea_size
, write_time
);
522 TIME_CHECK_NT ("ALL_INFO", all_info
, write_time
);
523 TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information
, write_time
);
525 s1
= fnum_find("BASIC_INFO");
526 correct_time
= s1
->basic_info
.out
.change_time
;
527 torture_comment(torture
, "change_time: %s\n", nt_time_string(mem_ctx
, correct_time
));
529 TIME_CHECK_NT ("BASIC_INFO", basic_info
, change_time
);
530 TIME_CHECK_NT ("BASIC_INFORMATION", basic_info
, change_time
);
531 TIME_CHECK_NT ("ALL_INFO", all_info
, change_time
);
532 TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information
, change_time
);
535 #define SIZE_CHECK(sname, stype, tfield) do { \
536 s1 = fnum_find(sname); \
537 if (s1 && s1->stype.out.tfield != correct_size) { \
538 printf("(%d) handle %s/%s incorrect - %u should be %u\n", __LINE__, #stype, #tfield, \
539 (unsigned int)s1->stype.out.tfield, \
540 (unsigned int)correct_size); \
543 s1 = fname_find(is_ipc, sname); \
544 if (s1 && s1->stype.out.tfield != correct_size) { \
545 printf("(%d) path %s/%s incorrect - %u should be %u\n", __LINE__, #stype, #tfield, \
546 (unsigned int)s1->stype.out.tfield, \
547 (unsigned int)correct_size); \
551 s1
= fnum_find("STANDARD_INFO");
552 correct_size
= s1
->standard_info
.out
.size
;
553 torture_comment(torture
, "size: %u\n", (unsigned int)correct_size
);
555 SIZE_CHECK("GETATTR", getattr
, size
);
556 SIZE_CHECK("GETATTRE", getattre
, size
);
557 SIZE_CHECK("STANDARD", standard
, size
);
558 SIZE_CHECK("EA_SIZE", ea_size
, size
);
559 SIZE_CHECK("STANDARD_INFO", standard_info
, size
);
560 SIZE_CHECK("STANDARD_INFORMATION", standard_info
, size
);
561 SIZE_CHECK("ALL_INFO", all_info
, size
);
562 SIZE_CHECK("ALL_INFORMATION", all_info
, size
);
563 SIZE_CHECK("COMPRESSION_INFO", compression_info
, compressed_size
);
564 SIZE_CHECK("COMPRESSION_INFORMATION", compression_info
, compressed_size
);
565 SIZE_CHECK("NETWORK_OPEN_INFORMATION", network_open_information
, size
);
567 SIZE_CHECK("STREAM_INFO", stream_info
, streams
[0].size
);
568 SIZE_CHECK("STREAM_INFORMATION", stream_info
, streams
[0].size
);
572 s1
= fnum_find("STANDARD_INFO");
573 correct_size
= s1
->standard_info
.out
.alloc_size
;
574 torture_comment(torture
, "alloc_size: %u\n", (unsigned int)correct_size
);
576 SIZE_CHECK("GETATTRE", getattre
, alloc_size
);
577 SIZE_CHECK("STANDARD", standard
, alloc_size
);
578 SIZE_CHECK("EA_SIZE", ea_size
, alloc_size
);
579 SIZE_CHECK("STANDARD_INFO", standard_info
, alloc_size
);
580 SIZE_CHECK("STANDARD_INFORMATION", standard_info
, alloc_size
);
581 SIZE_CHECK("ALL_INFO", all_info
, alloc_size
);
582 SIZE_CHECK("ALL_INFORMATION", all_info
, alloc_size
);
583 SIZE_CHECK("NETWORK_OPEN_INFORMATION", network_open_information
, alloc_size
);
585 SIZE_CHECK("STREAM_INFO", stream_info
, streams
[0].alloc_size
);
586 SIZE_CHECK("STREAM_INFORMATION", stream_info
, streams
[0].alloc_size
);
589 #define ATTRIB_CHECK(sname, stype, tfield) do { \
590 s1 = fnum_find(sname); \
591 if (s1 && s1->stype.out.tfield != correct_attrib) { \
592 printf("(%d) handle %s/%s incorrect - 0x%x should be 0x%x\n", __LINE__, #stype, #tfield, \
593 (unsigned int)s1->stype.out.tfield, \
594 (unsigned int)correct_attrib); \
597 s1 = fname_find(is_ipc, sname); \
598 if (s1 && s1->stype.out.tfield != correct_attrib) { \
599 printf("(%d) path %s/%s incorrect - 0x%x should be 0x%x\n", __LINE__, #stype, #tfield, \
600 (unsigned int)s1->stype.out.tfield, \
601 (unsigned int)correct_attrib); \
605 s1
= fnum_find("BASIC_INFO");
606 correct_attrib
= s1
->basic_info
.out
.attrib
;
607 torture_comment(torture
, "attrib: 0x%x\n", (unsigned int)correct_attrib
);
609 ATTRIB_CHECK("GETATTR", getattr
, attrib
);
611 ATTRIB_CHECK("GETATTRE", getattre
, attrib
);
612 ATTRIB_CHECK("STANDARD", standard
, attrib
);
613 ATTRIB_CHECK("EA_SIZE", ea_size
, attrib
);
615 ATTRIB_CHECK("BASIC_INFO", basic_info
, attrib
);
616 ATTRIB_CHECK("BASIC_INFORMATION", basic_info
, attrib
);
617 ATTRIB_CHECK("ALL_INFO", all_info
, attrib
);
618 ATTRIB_CHECK("ALL_INFORMATION", all_info
, attrib
);
619 ATTRIB_CHECK("NETWORK_OPEN_INFORMATION", network_open_information
, attrib
);
620 ATTRIB_CHECK("ATTRIBUTE_TAG_INFORMATION", attribute_tag_information
, attrib
);
622 correct_name
= fname
;
623 torture_comment(torture
, "name: %s\n", correct_name
);
625 #define NAME_CHECK(sname, stype, tfield, flags) do { \
626 s1 = fnum_find(sname); \
627 if (s1 && (strcmp_safe(s1->stype.out.tfield.s, correct_name) != 0 || \
628 wire_bad_flags(&s1->stype.out.tfield, flags, tree->session->transport))) { \
629 printf("(%d) handle %s/%s incorrect - '%s/%d'\n", __LINE__, #stype, #tfield, \
630 s1->stype.out.tfield.s, s1->stype.out.tfield.private_length); \
633 s1 = fname_find(is_ipc, sname); \
634 if (s1 && (strcmp_safe(s1->stype.out.tfield.s, correct_name) != 0 || \
635 wire_bad_flags(&s1->stype.out.tfield, flags, tree->session->transport))) { \
636 printf("(%d) path %s/%s incorrect - '%s/%d'\n", __LINE__, #stype, #tfield, \
637 s1->stype.out.tfield.s, s1->stype.out.tfield.private_length); \
641 NAME_CHECK("NAME_INFO", name_info
, fname
, STR_UNICODE
);
642 NAME_CHECK("NAME_INFORMATION", name_info
, fname
, STR_UNICODE
);
644 /* the ALL_INFO file name is the full path on the filesystem */
645 s1
= fnum_find("ALL_INFO");
646 if (s1
&& !s1
->all_info
.out
.fname
.s
) {
647 torture_fail(torture
, "ALL_INFO didn't give a filename");
649 if (s1
&& s1
->all_info
.out
.fname
.s
) {
650 char *p
= strrchr(s1
->all_info
.out
.fname
.s
, '\\');
652 printf("Not a full path in all_info/fname? - '%s'\n",
653 s1
->all_info
.out
.fname
.s
);
656 if (strcmp_safe(correct_name
, p
) != 0) {
657 printf("incorrect basename in all_info/fname - '%s'\n",
658 s1
->all_info
.out
.fname
.s
);
662 if (wire_bad_flags(&s1
->all_info
.out
.fname
, STR_UNICODE
, tree
->session
->transport
)) {
663 printf("Should not null terminate all_info/fname\n");
668 s1
= fnum_find("ALT_NAME_INFO");
670 correct_name
= s1
->alt_name_info
.out
.fname
.s
;
674 torture_comment(torture
, "no alternate name information\n");
676 torture_comment(torture
, "alt_name: %s\n", correct_name
);
678 NAME_CHECK("ALT_NAME_INFO", alt_name_info
, fname
, STR_UNICODE
);
679 NAME_CHECK("ALT_NAME_INFORMATION", alt_name_info
, fname
, STR_UNICODE
);
681 /* and make sure we can open by alternate name */
682 smbcli_close(tree
, fnum
);
683 fnum
= smbcli_nt_create_full(tree
, correct_name
, 0,
685 FILE_ATTRIBUTE_NORMAL
,
686 NTCREATEX_SHARE_ACCESS_DELETE
|
687 NTCREATEX_SHARE_ACCESS_READ
|
688 NTCREATEX_SHARE_ACCESS_WRITE
,
689 NTCREATEX_DISP_OVERWRITE_IF
,
692 printf("Unable to open by alt_name - %s\n", smbcli_errstr(tree
));
697 correct_name
= "::$DATA";
698 torture_comment(torture
, "stream_name: %s\n", correct_name
);
700 NAME_CHECK("STREAM_INFO", stream_info
, streams
[0].stream_name
, STR_UNICODE
);
701 NAME_CHECK("STREAM_INFORMATION", stream_info
, streams
[0].stream_name
, STR_UNICODE
);
705 /* make sure the EAs look right */
706 s1
= fnum_find("ALL_EAS");
707 s2
= fnum_find("ALL_INFO");
709 for (i
=0;i
<s1
->all_eas
.out
.num_eas
;i
++) {
710 printf(" flags=%d %s=%*.*s\n",
711 s1
->all_eas
.out
.eas
[i
].flags
,
712 s1
->all_eas
.out
.eas
[i
].name
.s
,
713 (int)s1
->all_eas
.out
.eas
[i
].value
.length
,
714 (int)s1
->all_eas
.out
.eas
[i
].value
.length
,
715 s1
->all_eas
.out
.eas
[i
].value
.data
);
719 if (s1
->all_eas
.out
.num_eas
== 0) {
720 if (s2
->all_info
.out
.ea_size
!= 0) {
721 printf("ERROR: num_eas==0 but fnum all_info.out.ea_size == %d\n",
722 s2
->all_info
.out
.ea_size
);
725 if (s2
->all_info
.out
.ea_size
!=
726 ea_list_size(s1
->all_eas
.out
.num_eas
, s1
->all_eas
.out
.eas
)) {
727 printf("ERROR: ea_list_size=%d != fnum all_info.out.ea_size=%d\n",
728 (int)ea_list_size(s1
->all_eas
.out
.num_eas
, s1
->all_eas
.out
.eas
),
729 (int)s2
->all_info
.out
.ea_size
);
733 s2
= fname_find(is_ipc
, "ALL_EAS");
735 VAL_EQUAL(all_eas
, num_eas
, all_eas
, num_eas
);
736 for (i
=0;i
<s1
->all_eas
.out
.num_eas
;i
++) {
737 VAL_EQUAL(all_eas
, eas
[i
].flags
, all_eas
, eas
[i
].flags
);
738 STR_EQUAL(all_eas
, eas
[i
].name
, all_eas
, eas
[i
].name
);
739 VAL_EQUAL(all_eas
, eas
[i
].value
.length
, all_eas
, eas
[i
].value
.length
);
743 #define VAL_CHECK(sname1, stype1, tfield1, sname2, stype2, tfield2) do { \
744 s1 = fnum_find(sname1); s2 = fnum_find(sname2); \
745 if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \
746 printf("(%d) handle %s/%s != %s/%s - 0x%x vs 0x%x\n", __LINE__, \
747 #stype1, #tfield1, #stype2, #tfield2, \
748 s1->stype1.out.tfield1, s2->stype2.out.tfield2); \
751 s1 = fname_find(is_ipc, sname1); s2 = fname_find(is_ipc, sname2); \
752 if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \
753 printf("(%d) path %s/%s != %s/%s - 0x%x vs 0x%x\n", __LINE__, \
754 #stype1, #tfield1, #stype2, #tfield2, \
755 s1->stype1.out.tfield1, s2->stype2.out.tfield2); \
758 s1 = fnum_find(sname1); s2 = fname_find(is_ipc, sname2); \
759 if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \
760 printf("(%d) handle %s/%s != path %s/%s - 0x%x vs 0x%x\n", __LINE__, \
761 #stype1, #tfield1, #stype2, #tfield2, \
762 s1->stype1.out.tfield1, s2->stype2.out.tfield2); \
765 s1 = fname_find(is_ipc, sname1); s2 = fnum_find(sname2); \
766 if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \
767 printf("(%d) path %s/%s != handle %s/%s - 0x%x vs 0x%x\n", __LINE__, \
768 #stype1, #tfield1, #stype2, #tfield2, \
769 s1->stype1.out.tfield1, s2->stype2.out.tfield2); \
773 VAL_CHECK("STANDARD_INFO", standard_info
, delete_pending
,
774 "ALL_INFO", all_info
, delete_pending
);
775 VAL_CHECK("STANDARD_INFO", standard_info
, directory
,
776 "ALL_INFO", all_info
, directory
);
777 VAL_CHECK("STANDARD_INFO", standard_info
, nlink
,
778 "ALL_INFO", all_info
, nlink
);
779 s1
= fnum_find("BASIC_INFO");
781 if (s1
->basic_info
.out
.attrib
!= FILE_ATTRIBUTE_NORMAL
) {
782 printf("(%d) attrib basic_info/nlink incorrect - %d should be %d\n", __LINE__
, s1
->basic_info
.out
.attrib
, (int)FILE_ATTRIBUTE_NORMAL
);
786 s1
= fnum_find("STANDARD_INFO");
788 if (s1
->standard_info
.out
.nlink
!= 1) {
789 printf("(%d) nlinks standard_info/nlink incorrect - %d should be 1\n", __LINE__
, s1
->standard_info
.out
.nlink
);
792 if (s1
->standard_info
.out
.delete_pending
!= 1) {
793 printf("(%d) nlinks standard_info/delete_pending incorrect - %d should be 1\n", __LINE__
, s1
->standard_info
.out
.delete_pending
);
797 VAL_CHECK("EA_INFO", ea_info
, ea_size
,
798 "ALL_INFO", all_info
, ea_size
);
800 VAL_CHECK("EA_SIZE", ea_size
, ea_size
,
801 "ALL_INFO", all_info
, ea_size
);
804 #define NAME_PATH_CHECK(sname, stype, field) do { \
805 s1 = fname_find(is_ipc, sname); s2 = fnum_find(sname); \
807 VAL_EQUAL(stype, field, stype, field); \
812 s1
= fnum_find("INTERNAL_INFORMATION");
814 torture_comment(torture
, "file_id=%.0f\n", (double)s1
->internal_information
.out
.file_id
);
817 NAME_PATH_CHECK("INTERNAL_INFORMATION", internal_information
, file_id
);
818 NAME_PATH_CHECK("POSITION_INFORMATION", position_information
, position
);
820 printf("fnum pos = %.0f, fname pos = %.0f\n",
821 (double)s2
->position_information
.out
.position
,
822 (double)s1
->position_information
.out
.position
);
824 NAME_PATH_CHECK("MODE_INFORMATION", mode_information
, mode
);
825 NAME_PATH_CHECK("ALIGNMENT_INFORMATION", alignment_information
, alignment_requirement
);
826 NAME_PATH_CHECK("ATTRIBUTE_TAG_INFORMATION", attribute_tag_information
, attrib
);
827 NAME_PATH_CHECK("ATTRIBUTE_TAG_INFORMATION", attribute_tag_information
, reparse_tag
);
830 /* these are expected to differ */
831 NAME_PATH_CHECK("ACCESS_INFORMATION", access_information
, access_flags
);
835 #define UNKNOWN_CHECK(sname, stype, tfield) do { \
836 s1 = fnum_find(sname); \
837 if (s1 && s1->stype.out.tfield != 0) { \
838 printf("(%d) handle %s/%s unknown != 0 (0x%x)\n", __LINE__, \
840 (unsigned int)s1->stype.out.tfield); \
842 s1 = fname_find(is_ipc, sname); \
843 if (s1 && s1->stype.out.tfield != 0) { \
844 printf("(%d) path %s/%s unknown != 0 (0x%x)\n", __LINE__, \
846 (unsigned int)s1->stype.out.tfield); \
849 /* now get a bit fancier .... */
851 /* when we set the delete disposition then the link count should drop
852 to 0 and delete_pending should be 1 */
857 /* basic testing of all RAW_FILEINFO_* calls
858 for each call we test that it succeeds, and where possible test
859 for consistency between the calls.
861 bool torture_raw_qfileinfo(struct torture_context
*torture
,
862 struct smbcli_state
*cli
)
866 const char *fname
= "\\torture_qfileinfo.txt";
868 fnum
= create_complex_file(cli
, torture
, fname
);
870 printf("ERROR: open of %s failed (%s)\n", fname
, smbcli_errstr(cli
->tree
));
874 ret
= torture_raw_qfileinfo_internals(torture
, torture
, cli
->tree
, fnum
, fname
, false /* is_ipc */);
876 smbcli_close(cli
->tree
, fnum
);
877 smbcli_unlink(cli
->tree
, fname
);
882 bool torture_raw_qfileinfo_pipe(struct torture_context
*torture
,
883 struct smbcli_state
*cli
)
887 const char *fname
= "\\lsass";
888 struct dcerpc_pipe
*p
;
889 struct smbcli_tree
*ipc_tree
;
892 if (!(p
= dcerpc_pipe_init(torture
, torture
->ev
))) {
896 status
= dcerpc_pipe_open_smb(p
, cli
->tree
, fname
);
897 torture_assert_ntstatus_ok(torture
, status
, "dcerpc_pipe_open_smb failed");
899 ipc_tree
= dcerpc_smb_tree(p
->conn
);
900 fnum
= dcerpc_smb_fnum(p
->conn
);
902 ret
= torture_raw_qfileinfo_internals(torture
, torture
, ipc_tree
, fnum
, fname
, true /* is_ipc */);