WHATSNEW: Update changes since rc3.
[Samba.git] / source4 / torture / raw / qfileinfo.c
blob4f6d8d56b9b3836ff58ce9fec68d39d2d0492012
1 /*
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/>.
21 #include "includes.h"
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"
30 static struct {
31 const char *name;
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;
40 } levels[] = {
41 { .name = "GETATTR",
42 .level = RAW_FILEINFO_GETATTR,
43 .only_paths = 1,
44 .only_handles = 0,
45 .expected_ipc_access_denied = 1},
46 { .name ="GETATTRE",
47 .level = RAW_FILEINFO_GETATTRE,
48 .only_paths = 0,
49 .only_handles = 1 },
50 { .name ="STANDARD",
51 .level = RAW_FILEINFO_STANDARD, },
52 { .name ="EA_SIZE",
53 .level = RAW_FILEINFO_EA_SIZE },
54 { .name ="ALL_EAS",
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,
60 .only_paths = 1,
61 .only_handles = 0 },
62 { .name ="BASIC_INFO",
63 .level = RAW_FILEINFO_BASIC_INFO },
64 { .name ="STANDARD_INFO",
65 .level = RAW_FILEINFO_STANDARD_INFO },
66 { .name ="EA_INFO",
67 .level = RAW_FILEINFO_EA_INFO },
68 { .name ="NAME_INFO",
69 .level = RAW_FILEINFO_NAME_INFO },
70 { .name ="ALL_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,
86 .only_paths = 0,
87 .only_handles = 0,
88 .capability_mask = CAP_UNIX},
89 { .name ="UNIX_LINK_INFO",
90 .level = RAW_FILEINFO_UNIX_LINK,
91 .only_paths = 0,
92 .only_handles = 0,
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
134 { NULL }
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)
153 int i;
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;
161 return NULL;
165 find a level in the levels[] table
167 static union smb_fileinfo *fname_find(bool is_ipc, const char *name)
169 int i;
170 if (is_ipc) {
171 return NULL;
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;
180 return NULL;
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__); \
189 ret = false; \
190 }} while(0)
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__); \
198 ret = false; \
199 }} while(0)
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", \
203 #n1, #v1, \
204 #n2, #v2, \
205 __FILE__, __LINE__); \
206 ret = false; \
207 }} while(0)
209 /* used to find hints on unknown values - and to make sure
210 we zero-fill */
211 #if 0 /* unused */
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", \
214 #n1, #v1, \
215 (unsigned int)s1->n1.out.v1, \
216 (unsigned int)s1->n1.out.v1, \
217 __FILE__, __LINE__); \
218 ret = false; \
219 }} while(0)
220 #endif
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,
227 TALLOC_CTX *mem_ctx,
228 struct smbcli_tree *tree,
229 int fnum, const char *fname,
230 bool is_ipc)
232 int i;
233 bool ret = true;
234 int count;
235 union smb_fileinfo *s1, *s2;
236 NTTIME correct_time;
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) {
264 continue;
267 if (is_ipc) {
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",
273 levels[i].name,
274 nt_errstr(levels[i].fname_status));
275 continue;
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));
279 count++;
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",
287 levels[i].name,
288 nt_errstr(levels[i].fnum_status));
289 continue;
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));
295 count++;
297 } else {
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",
304 levels[i].name,
305 nt_errstr(levels[i].fnum_status));
306 continue;
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",
315 levels[i].name,
316 nt_errstr(levels[i].fname_status));
317 continue;
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));
323 count++;
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));
328 count++;
334 if (count != 0) {
335 ret = false;
336 printf("%d levels failed\n", count);
337 if (count > 35) {
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) {
345 if (!is_ipc) {
346 printf("STREAM_INFO broken (%d) - skipping streams checks\n",
347 s1 ? s1->stream_info.out.num_streams : -1);
349 skip_streams = true;
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) \
362 do { \
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 } \
369 } while (0)
371 #define 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");
380 #undef INFO_CHECK
381 #define INFO_CHECK \
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");
390 #undef INFO_CHECK
391 #define INFO_CHECK \
392 VAL_EQUAL(ea_info, ea_size, ea_info, ea_size);
394 ALIAS_CHECK("EA_INFO", "EA_INFORMATION");
396 #undef INFO_CHECK
397 #define INFO_CHECK \
398 STR_EQUAL(name_info, fname, name_info, fname);
400 ALIAS_CHECK("NAME_INFO", "NAME_INFORMATION");
402 #undef INFO_CHECK
403 #define INFO_CHECK \
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");
419 #undef INFO_CHECK
420 #define INFO_CHECK \
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");
430 #undef INFO_CHECK
431 #define INFO_CHECK \
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)); \
443 ret = false; \
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)); \
450 ret = false; \
451 }} while (0)
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)); \
459 ret = false; \
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)); \
466 ret = false; \
467 }} while (0)
469 #if 0 /* unused */
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)); \
476 ret = false; \
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)); \
483 ret = false; \
484 }} while (0)
485 #endif
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); \
541 ret = false; \
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); \
548 ret = false; \
549 }} while (0)
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);
566 if (!skip_streams) {
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);
584 if (!skip_streams) {
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); \
595 ret = false; \
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); \
602 ret = false; \
603 }} while (0)
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);
610 if (!is_ipc) {
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); \
631 ret = false; \
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); \
638 ret = false; \
639 }} while (0)
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, '\\');
651 if (!p) {
652 printf("Not a full path in all_info/fname? - '%s'\n",
653 s1->all_info.out.fname.s);
654 ret = false;
655 } else {
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);
659 ret = false;
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");
664 ret = false;
668 s1 = fnum_find("ALT_NAME_INFO");
669 if (s1) {
670 correct_name = s1->alt_name_info.out.fname.s;
673 if (!correct_name) {
674 torture_comment(torture, "no alternate name information\n");
675 } else {
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,
684 SEC_RIGHTS_FILE_ALL,
685 FILE_ATTRIBUTE_NORMAL,
686 NTCREATEX_SHARE_ACCESS_DELETE|
687 NTCREATEX_SHARE_ACCESS_READ|
688 NTCREATEX_SHARE_ACCESS_WRITE,
689 NTCREATEX_DISP_OVERWRITE_IF,
690 0, 0);
691 if (fnum == -1) {
692 printf("Unable to open by alt_name - %s\n", smbcli_errstr(tree));
693 ret = false;
696 if (!skip_streams) {
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");
708 if (s1) {
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);
718 if (s1 && s2) {
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);
724 } else {
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");
734 if (s2) {
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); \
749 ret = false; \
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); \
756 ret = false; \
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); \
763 ret = false; \
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); \
770 ret = false; \
771 }} while (0)
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");
780 if (s1 && is_ipc) {
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);
783 ret = false;
786 s1 = fnum_find("STANDARD_INFO");
787 if (s1 && is_ipc) {
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);
790 ret = false;
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);
794 ret = false;
797 VAL_CHECK("EA_INFO", ea_info, ea_size,
798 "ALL_INFO", all_info, ea_size);
799 if (!is_ipc) {
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); \
806 if (s1 && s2) { \
807 VAL_EQUAL(stype, field, stype, field); \
809 } while (0)
812 s1 = fnum_find("INTERNAL_INFORMATION");
813 if (s1) {
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);
819 if (s1 && s2) {
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);
829 #if 0
830 /* these are expected to differ */
831 NAME_PATH_CHECK("ACCESS_INFORMATION", access_information, access_flags);
832 #endif
834 #if 0 /* unused */
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__, \
839 #stype, #tfield, \
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__, \
845 #stype, #tfield, \
846 (unsigned int)s1->stype.out.tfield); \
847 }} while (0)
848 #endif
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 */
854 return ret;
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)
864 int fnum;
865 bool ret;
866 const char *fname = "\\torture_qfileinfo.txt";
868 fnum = create_complex_file(cli, torture, fname);
869 if (fnum == -1) {
870 printf("ERROR: open of %s failed (%s)\n", fname, smbcli_errstr(cli->tree));
871 return false;
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);
879 return ret;
882 bool torture_raw_qfileinfo_pipe(struct torture_context *torture,
883 struct smbcli_state *cli)
885 bool ret = true;
886 int fnum;
887 const char *fname = "\\lsass";
888 struct dcerpc_pipe *p;
889 struct smbcli_tree *ipc_tree;
890 NTSTATUS status;
892 if (!(p = dcerpc_pipe_init(torture, torture->ev))) {
893 return false;
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 */);
904 talloc_free(p);
905 return ret;