Backport FSCTL codes from master
[Samba.git] / source4 / torture / raw / qfileinfo.c
blob2a5a2e77ef789bc52a3df24a57ae5c4a8a1c71d3
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"
29 static struct {
30 const char *name;
31 enum smb_fileinfo_level level;
32 unsigned int only_paths:1;
33 unsigned int only_handles:1;
34 uint32_t capability_mask;
35 unsigned int expected_ipc_access_denied:1;
36 NTSTATUS expected_ipc_fnum_status;
37 NTSTATUS fnum_status, fname_status;
38 union smb_fileinfo fnum_finfo, fname_finfo;
39 } levels[] = {
40 { .name = "GETATTR",
41 .level = RAW_FILEINFO_GETATTR,
42 .only_paths = 1,
43 .only_handles = 0,
44 .expected_ipc_access_denied = 1},
45 { .name ="GETATTRE",
46 .level = RAW_FILEINFO_GETATTRE,
47 .only_paths = 0,
48 .only_handles = 1 },
49 { .name ="STANDARD",
50 .level = RAW_FILEINFO_STANDARD, },
51 { .name ="EA_SIZE",
52 .level = RAW_FILEINFO_EA_SIZE },
53 { .name ="ALL_EAS",
54 .level = RAW_FILEINFO_ALL_EAS,
55 .expected_ipc_fnum_status = NT_STATUS_ACCESS_DENIED,
57 { .name ="IS_NAME_VALID",
58 .level = RAW_FILEINFO_IS_NAME_VALID,
59 .only_paths = 1,
60 .only_handles = 0 },
61 { .name ="BASIC_INFO",
62 .level = RAW_FILEINFO_BASIC_INFO },
63 { .name ="STANDARD_INFO",
64 .level = RAW_FILEINFO_STANDARD_INFO },
65 { .name ="EA_INFO",
66 .level = RAW_FILEINFO_EA_INFO },
67 { .name ="NAME_INFO",
68 .level = RAW_FILEINFO_NAME_INFO },
69 { .name ="ALL_INFO",
70 .level = RAW_FILEINFO_ALL_INFO },
71 { .name ="ALT_NAME_INFO",
72 .level = RAW_FILEINFO_ALT_NAME_INFO,
73 .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER
75 { .name ="STREAM_INFO",
76 .level = RAW_FILEINFO_STREAM_INFO,
77 .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER
79 { .name ="COMPRESSION_INFO",
80 .level = RAW_FILEINFO_COMPRESSION_INFO,
81 .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER
83 { .name ="UNIX_BASIC_INFO",
84 .level = RAW_FILEINFO_UNIX_BASIC,
85 .only_paths = 0,
86 .only_handles = 0,
87 .capability_mask = CAP_UNIX},
88 { .name ="UNIX_LINK_INFO",
89 .level = RAW_FILEINFO_UNIX_LINK,
90 .only_paths = 0,
91 .only_handles = 0,
92 .capability_mask = CAP_UNIX},
93 { .name ="BASIC_INFORMATION",
94 .level = RAW_FILEINFO_BASIC_INFORMATION },
95 { .name ="STANDARD_INFORMATION",
96 .level = RAW_FILEINFO_STANDARD_INFORMATION },
97 { .name ="INTERNAL_INFORMATION",
98 .level = RAW_FILEINFO_INTERNAL_INFORMATION },
99 { .name ="EA_INFORMATION",
100 .level = RAW_FILEINFO_EA_INFORMATION },
101 { .name = "ACCESS_INFORMATION",
102 .level = RAW_FILEINFO_ACCESS_INFORMATION },
103 { .name = "NAME_INFORMATION",
104 .level = RAW_FILEINFO_NAME_INFORMATION },
105 { .name ="POSITION_INFORMATION",
106 .level = RAW_FILEINFO_POSITION_INFORMATION },
107 { .name ="MODE_INFORMATION",
108 .level = RAW_FILEINFO_MODE_INFORMATION },
109 { .name ="ALIGNMENT_INFORMATION",
110 .level = RAW_FILEINFO_ALIGNMENT_INFORMATION },
111 { .name ="ALL_INFORMATION",
112 .level = RAW_FILEINFO_ALL_INFORMATION },
113 { .name ="ALT_NAME_INFORMATION",
114 .level = RAW_FILEINFO_ALT_NAME_INFORMATION,
115 .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER
117 { .name ="STREAM_INFORMATION",
118 .level = RAW_FILEINFO_STREAM_INFORMATION,
119 .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER
121 { .name = "COMPRESSION_INFORMATION",
122 .level = RAW_FILEINFO_COMPRESSION_INFORMATION,
123 .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER
125 { .name ="NETWORK_OPEN_INFORMATION",
126 .level = RAW_FILEINFO_NETWORK_OPEN_INFORMATION,
127 .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER
129 { .name = "ATTRIBUTE_TAG_INFORMATION",
130 .level = RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION,
131 .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER
133 { NULL }
137 compare a dos time (2 second resolution) to a nt time
139 static int dos_nt_time_cmp(time_t t, NTTIME nt)
141 time_t t2 = nt_time_to_unix(nt);
142 if (abs(t2 - t) <= 2) return 0;
143 return t2 > t ? 1 : -1;
148 find a level in the levels[] table
150 static union smb_fileinfo *fnum_find(const char *name)
152 int i;
153 for (i=0; levels[i].name; i++) {
154 if (NT_STATUS_IS_OK(levels[i].fnum_status) &&
155 strcmp(name, levels[i].name) == 0 &&
156 !levels[i].only_paths) {
157 return &levels[i].fnum_finfo;
160 return NULL;
164 find a level in the levels[] table
166 static union smb_fileinfo *fname_find(bool is_ipc, const char *name)
168 int i;
169 if (is_ipc) {
170 return NULL;
172 for (i=0; levels[i].name; i++) {
173 if (NT_STATUS_IS_OK(levels[i].fname_status) &&
174 strcmp(name, levels[i].name) == 0 &&
175 !levels[i].only_handles) {
176 return &levels[i].fname_finfo;
179 return NULL;
182 /* local macros to make the code below more readable */
183 #define VAL_EQUAL(n1, v1, n2, v2) do {if (s1->n1.out.v1 != s2->n2.out.v2) { \
184 printf("%s/%s [%u] != %s/%s [%u] at %s(%d)\n", \
185 #n1, #v1, (unsigned int)s1->n1.out.v1, \
186 #n2, #v2, (unsigned int)s2->n2.out.v2, \
187 __FILE__, __LINE__); \
188 ret = false; \
189 }} while(0)
191 #define STR_EQUAL(n1, v1, n2, v2) do {if (strcmp_safe(s1->n1.out.v1.s, s2->n2.out.v2.s) || \
192 s1->n1.out.v1.private_length != s2->n2.out.v2.private_length) { \
193 printf("%s/%s [%s/%d] != %s/%s [%s/%d] at %s(%d)\n", \
194 #n1, #v1, s1->n1.out.v1.s, s1->n1.out.v1.private_length, \
195 #n2, #v2, s2->n2.out.v2.s, s2->n2.out.v2.private_length, \
196 __FILE__, __LINE__); \
197 ret = false; \
198 }} while(0)
200 #define STRUCT_EQUAL(n1, v1, n2, v2) do {if (memcmp(&s1->n1.out.v1,&s2->n2.out.v2,sizeof(s1->n1.out.v1))) { \
201 printf("%s/%s != %s/%s at %s(%d)\n", \
202 #n1, #v1, \
203 #n2, #v2, \
204 __FILE__, __LINE__); \
205 ret = false; \
206 }} while(0)
208 /* used to find hints on unknown values - and to make sure
209 we zero-fill */
210 #if 0 /* unused */
211 #define VAL_UNKNOWN(n1, v1) do {if (s1->n1.out.v1 != 0) { \
212 printf("%s/%s non-zero unknown - %u (0x%x) at %s(%d)\n", \
213 #n1, #v1, \
214 (unsigned int)s1->n1.out.v1, \
215 (unsigned int)s1->n1.out.v1, \
216 __FILE__, __LINE__); \
217 ret = false; \
218 }} while(0)
219 #endif
221 /* basic testing of all RAW_FILEINFO_* calls
222 for each call we test that it succeeds, and where possible test
223 for consistency between the calls.
225 static bool torture_raw_qfileinfo_internals(struct torture_context *torture,
226 TALLOC_CTX *mem_ctx,
227 struct smbcli_tree *tree,
228 int fnum, const char *fname,
229 bool is_ipc)
231 int i;
232 bool ret = true;
233 int count;
234 union smb_fileinfo *s1, *s2;
235 NTTIME correct_time;
236 uint64_t correct_size;
237 uint32_t correct_attrib;
238 const char *correct_name;
239 bool skip_streams = false;
241 /* scan all the fileinfo and pathinfo levels */
242 for (i=0; levels[i].name; i++) {
243 if (!levels[i].only_paths) {
244 levels[i].fnum_finfo.generic.level = levels[i].level;
245 levels[i].fnum_finfo.generic.in.file.fnum = fnum;
246 levels[i].fnum_status = smb_raw_fileinfo(tree, mem_ctx,
247 &levels[i].fnum_finfo);
250 if (!levels[i].only_handles) {
251 levels[i].fname_finfo.generic.level = levels[i].level;
252 levels[i].fname_finfo.generic.in.file.path = talloc_strdup(mem_ctx, fname);
253 levels[i].fname_status = smb_raw_pathinfo(tree, mem_ctx,
254 &levels[i].fname_finfo);
258 /* check for completely broken levels */
259 for (count=i=0; levels[i].name; i++) {
260 uint32_t cap = tree->session->transport->negotiate.capabilities;
261 /* see if this server claims to support this level */
262 if ((cap & levels[i].capability_mask) != levels[i].capability_mask) {
263 continue;
266 if (is_ipc) {
267 if (levels[i].expected_ipc_access_denied && NT_STATUS_EQUAL(NT_STATUS_ACCESS_DENIED, levels[i].fname_status)) {
268 } else if (!levels[i].only_handles &&
269 NT_STATUS_EQUAL(levels[i].fname_status,
270 NT_STATUS_NOT_SUPPORTED)) {
271 torture_warning(torture, "fname level %s %s",
272 levels[i].name,
273 nt_errstr(levels[i].fname_status));
274 continue;
275 } else if (!levels[i].only_handles && !NT_STATUS_EQUAL(NT_STATUS_INVALID_DEVICE_REQUEST, levels[i].fname_status)) {
276 printf("ERROR: fname level %s failed, expected NT_STATUS_INVALID_DEVICE_REQUEST - %s\n",
277 levels[i].name, nt_errstr(levels[i].fname_status));
278 count++;
280 if (!levels[i].only_paths &&
281 (NT_STATUS_EQUAL(levels[i].fnum_status,
282 NT_STATUS_NOT_SUPPORTED) ||
283 NT_STATUS_EQUAL(levels[i].fnum_status,
284 NT_STATUS_NOT_IMPLEMENTED))) {
285 torture_warning(torture, "fnum level %s %s",
286 levels[i].name,
287 nt_errstr(levels[i].fnum_status));
288 continue;
290 if (!levels[i].only_paths && !NT_STATUS_EQUAL(levels[i].expected_ipc_fnum_status, levels[i].fnum_status)) {
291 printf("ERROR: fnum level %s failed, expected %s - %s\n",
292 levels[i].name, nt_errstr(levels[i].expected_ipc_fnum_status),
293 nt_errstr(levels[i].fnum_status));
294 count++;
296 } else {
297 if (!levels[i].only_paths &&
298 (NT_STATUS_EQUAL(levels[i].fnum_status,
299 NT_STATUS_NOT_SUPPORTED) ||
300 NT_STATUS_EQUAL(levels[i].fnum_status,
301 NT_STATUS_NOT_IMPLEMENTED))) {
302 torture_warning(torture, "fnum level %s %s",
303 levels[i].name,
304 nt_errstr(levels[i].fnum_status));
305 continue;
308 if (!levels[i].only_handles &&
309 (NT_STATUS_EQUAL(levels[i].fname_status,
310 NT_STATUS_NOT_SUPPORTED) ||
311 NT_STATUS_EQUAL(levels[i].fname_status,
312 NT_STATUS_NOT_IMPLEMENTED))) {
313 torture_warning(torture, "fname level %s %s",
314 levels[i].name,
315 nt_errstr(levels[i].fname_status));
316 continue;
319 if (!levels[i].only_paths && !NT_STATUS_IS_OK(levels[i].fnum_status)) {
320 printf("ERROR: fnum level %s failed - %s\n",
321 levels[i].name, nt_errstr(levels[i].fnum_status));
322 count++;
324 if (!levels[i].only_handles && !NT_STATUS_IS_OK(levels[i].fname_status)) {
325 printf("ERROR: fname level %s failed - %s\n",
326 levels[i].name, nt_errstr(levels[i].fname_status));
327 count++;
333 if (count != 0) {
334 ret = false;
335 printf("%d levels failed\n", count);
336 if (count > 35) {
337 torture_fail(torture, "too many level failures - giving up");
341 /* see if we can do streams */
342 s1 = fnum_find("STREAM_INFO");
343 if (!s1 || s1->stream_info.out.num_streams == 0) {
344 if (!is_ipc) {
345 printf("STREAM_INFO broken (%d) - skipping streams checks\n",
346 s1 ? s1->stream_info.out.num_streams : -1);
348 skip_streams = true;
352 /* this code is incredibly repititive but doesn't lend itself to loops, so
353 we use lots of macros to make it less painful */
355 /* first off we check the levels that are supposed to be aliases. It will be quite rare for
356 this code to fail, but we need to check it for completeness */
360 #define ALIAS_CHECK(sname1, sname2) \
361 do { \
362 s1 = fnum_find(sname1); s2 = fnum_find(sname2); \
363 if (s1 && s2) { INFO_CHECK } \
364 s1 = fname_find(is_ipc, sname1); s2 = fname_find(is_ipc, sname2); \
365 if (s1 && s2) { INFO_CHECK } \
366 s1 = fnum_find(sname1); s2 = fname_find(is_ipc, sname2); \
367 if (s1 && s2) { INFO_CHECK } \
368 } while (0)
370 #define INFO_CHECK \
371 STRUCT_EQUAL(basic_info, create_time, basic_info, create_time); \
372 STRUCT_EQUAL(basic_info, access_time, basic_info, access_time); \
373 STRUCT_EQUAL(basic_info, write_time, basic_info, write_time); \
374 STRUCT_EQUAL(basic_info, change_time, basic_info, change_time); \
375 VAL_EQUAL (basic_info, attrib, basic_info, attrib);
377 ALIAS_CHECK("BASIC_INFO", "BASIC_INFORMATION");
379 #undef INFO_CHECK
380 #define INFO_CHECK \
381 VAL_EQUAL(standard_info, alloc_size, standard_info, alloc_size); \
382 VAL_EQUAL(standard_info, size, standard_info, size); \
383 VAL_EQUAL(standard_info, nlink, standard_info, nlink); \
384 VAL_EQUAL(standard_info, delete_pending, standard_info, delete_pending); \
385 VAL_EQUAL(standard_info, directory, standard_info, directory);
387 ALIAS_CHECK("STANDARD_INFO", "STANDARD_INFORMATION");
389 #undef INFO_CHECK
390 #define INFO_CHECK \
391 VAL_EQUAL(ea_info, ea_size, ea_info, ea_size);
393 ALIAS_CHECK("EA_INFO", "EA_INFORMATION");
395 #undef INFO_CHECK
396 #define INFO_CHECK \
397 STR_EQUAL(name_info, fname, name_info, fname);
399 ALIAS_CHECK("NAME_INFO", "NAME_INFORMATION");
401 #undef INFO_CHECK
402 #define INFO_CHECK \
403 STRUCT_EQUAL(all_info, create_time, all_info, create_time); \
404 STRUCT_EQUAL(all_info, access_time, all_info, access_time); \
405 STRUCT_EQUAL(all_info, write_time, all_info, write_time); \
406 STRUCT_EQUAL(all_info, change_time, all_info, change_time); \
407 VAL_EQUAL(all_info, attrib, all_info, attrib); \
408 VAL_EQUAL(all_info, alloc_size, all_info, alloc_size); \
409 VAL_EQUAL(all_info, size, all_info, size); \
410 VAL_EQUAL(all_info, nlink, all_info, nlink); \
411 VAL_EQUAL(all_info, delete_pending, all_info, delete_pending); \
412 VAL_EQUAL(all_info, directory, all_info, directory); \
413 VAL_EQUAL(all_info, ea_size, all_info, ea_size); \
414 STR_EQUAL(all_info, fname, all_info, fname);
416 ALIAS_CHECK("ALL_INFO", "ALL_INFORMATION");
418 #undef INFO_CHECK
419 #define INFO_CHECK \
420 VAL_EQUAL(compression_info, compressed_size,compression_info, compressed_size); \
421 VAL_EQUAL(compression_info, format, compression_info, format); \
422 VAL_EQUAL(compression_info, unit_shift, compression_info, unit_shift); \
423 VAL_EQUAL(compression_info, chunk_shift, compression_info, chunk_shift); \
424 VAL_EQUAL(compression_info, cluster_shift, compression_info, cluster_shift);
426 ALIAS_CHECK("COMPRESSION_INFO", "COMPRESSION_INFORMATION");
429 #undef INFO_CHECK
430 #define INFO_CHECK \
431 STR_EQUAL(alt_name_info, fname, alt_name_info, fname);
433 ALIAS_CHECK("ALT_NAME_INFO", "ALT_NAME_INFORMATION");
436 #define TIME_CHECK_NT(sname, stype, tfield) do { \
437 s1 = fnum_find(sname); \
438 if (s1 && memcmp(&s1->stype.out.tfield, &correct_time, sizeof(correct_time)) != 0) { \
439 printf("(%d) handle %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \
440 nt_time_string(mem_ctx, s1->stype.out.tfield), \
441 nt_time_string(mem_ctx, correct_time)); \
442 ret = false; \
444 s1 = fname_find(is_ipc, sname); \
445 if (s1 && memcmp(&s1->stype.out.tfield, &correct_time, sizeof(correct_time)) != 0) { \
446 printf("(%d) path %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \
447 nt_time_string(mem_ctx, s1->stype.out.tfield), \
448 nt_time_string(mem_ctx, correct_time)); \
449 ret = false; \
450 }} while (0)
452 #define TIME_CHECK_DOS(sname, stype, tfield) do { \
453 s1 = fnum_find(sname); \
454 if (s1 && dos_nt_time_cmp(s1->stype.out.tfield, correct_time) != 0) { \
455 printf("(%d) handle %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \
456 timestring(mem_ctx, s1->stype.out.tfield), \
457 nt_time_string(mem_ctx, correct_time)); \
458 ret = false; \
460 s1 = fname_find(is_ipc, sname); \
461 if (s1 && dos_nt_time_cmp(s1->stype.out.tfield, correct_time) != 0) { \
462 printf("(%d) path %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \
463 timestring(mem_ctx, s1->stype.out.tfield), \
464 nt_time_string(mem_ctx, correct_time)); \
465 ret = false; \
466 }} while (0)
468 #if 0 /* unused */
469 #define TIME_CHECK_UNX(sname, stype, tfield) do { \
470 s1 = fnum_find(sname); \
471 if (s1 && unx_nt_time_cmp(s1->stype.out.tfield, correct_time) != 0) { \
472 printf("(%d) handle %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \
473 timestring(mem_ctx, s1->stype.out.tfield), \
474 nt_time_string(mem_ctx, correct_time)); \
475 ret = false; \
477 s1 = fname_find(is_ipc, sname); \
478 if (s1 && unx_nt_time_cmp(s1->stype.out.tfield, correct_time) != 0) { \
479 printf("(%d) path %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \
480 timestring(mem_ctx, s1->stype.out.tfield), \
481 nt_time_string(mem_ctx, correct_time)); \
482 ret = false; \
483 }} while (0)
484 #endif
486 /* now check that all the times that are supposed to be equal are correct */
487 s1 = fnum_find("BASIC_INFO");
488 correct_time = s1->basic_info.out.create_time;
489 torture_comment(torture, "create_time: %s\n", nt_time_string(mem_ctx, correct_time));
491 TIME_CHECK_NT ("BASIC_INFO", basic_info, create_time);
492 TIME_CHECK_NT ("BASIC_INFORMATION", basic_info, create_time);
493 TIME_CHECK_DOS("GETATTRE", getattre, create_time);
494 TIME_CHECK_DOS("STANDARD", standard, create_time);
495 TIME_CHECK_DOS("EA_SIZE", ea_size, create_time);
496 TIME_CHECK_NT ("ALL_INFO", all_info, create_time);
497 TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information, create_time);
499 s1 = fnum_find("BASIC_INFO");
500 correct_time = s1->basic_info.out.access_time;
501 torture_comment(torture, "access_time: %s\n", nt_time_string(mem_ctx, correct_time));
503 TIME_CHECK_NT ("BASIC_INFO", basic_info, access_time);
504 TIME_CHECK_NT ("BASIC_INFORMATION", basic_info, access_time);
505 TIME_CHECK_DOS("GETATTRE", getattre, access_time);
506 TIME_CHECK_DOS("STANDARD", standard, access_time);
507 TIME_CHECK_DOS("EA_SIZE", ea_size, access_time);
508 TIME_CHECK_NT ("ALL_INFO", all_info, access_time);
509 TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information, access_time);
511 s1 = fnum_find("BASIC_INFO");
512 correct_time = s1->basic_info.out.write_time;
513 torture_comment(torture, "write_time : %s\n", nt_time_string(mem_ctx, correct_time));
515 TIME_CHECK_NT ("BASIC_INFO", basic_info, write_time);
516 TIME_CHECK_NT ("BASIC_INFORMATION", basic_info, write_time);
517 TIME_CHECK_DOS("GETATTR", getattr, write_time);
518 TIME_CHECK_DOS("GETATTRE", getattre, write_time);
519 TIME_CHECK_DOS("STANDARD", standard, write_time);
520 TIME_CHECK_DOS("EA_SIZE", ea_size, write_time);
521 TIME_CHECK_NT ("ALL_INFO", all_info, write_time);
522 TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information, write_time);
524 s1 = fnum_find("BASIC_INFO");
525 correct_time = s1->basic_info.out.change_time;
526 torture_comment(torture, "change_time: %s\n", nt_time_string(mem_ctx, correct_time));
528 TIME_CHECK_NT ("BASIC_INFO", basic_info, change_time);
529 TIME_CHECK_NT ("BASIC_INFORMATION", basic_info, change_time);
530 TIME_CHECK_NT ("ALL_INFO", all_info, change_time);
531 TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information, change_time);
534 #define SIZE_CHECK(sname, stype, tfield) do { \
535 s1 = fnum_find(sname); \
536 if (s1 && s1->stype.out.tfield != correct_size) { \
537 printf("(%d) handle %s/%s incorrect - %u should be %u\n", __LINE__, #stype, #tfield, \
538 (unsigned int)s1->stype.out.tfield, \
539 (unsigned int)correct_size); \
540 ret = false; \
542 s1 = fname_find(is_ipc, sname); \
543 if (s1 && s1->stype.out.tfield != correct_size) { \
544 printf("(%d) path %s/%s incorrect - %u should be %u\n", __LINE__, #stype, #tfield, \
545 (unsigned int)s1->stype.out.tfield, \
546 (unsigned int)correct_size); \
547 ret = false; \
548 }} while (0)
550 s1 = fnum_find("STANDARD_INFO");
551 correct_size = s1->standard_info.out.size;
552 torture_comment(torture, "size: %u\n", (unsigned int)correct_size);
554 SIZE_CHECK("GETATTR", getattr, size);
555 SIZE_CHECK("GETATTRE", getattre, size);
556 SIZE_CHECK("STANDARD", standard, size);
557 SIZE_CHECK("EA_SIZE", ea_size, size);
558 SIZE_CHECK("STANDARD_INFO", standard_info, size);
559 SIZE_CHECK("STANDARD_INFORMATION", standard_info, size);
560 SIZE_CHECK("ALL_INFO", all_info, size);
561 SIZE_CHECK("ALL_INFORMATION", all_info, size);
562 SIZE_CHECK("COMPRESSION_INFO", compression_info, compressed_size);
563 SIZE_CHECK("COMPRESSION_INFORMATION", compression_info, compressed_size);
564 SIZE_CHECK("NETWORK_OPEN_INFORMATION", network_open_information, size);
565 if (!skip_streams) {
566 SIZE_CHECK("STREAM_INFO", stream_info, streams[0].size);
567 SIZE_CHECK("STREAM_INFORMATION", stream_info, streams[0].size);
571 s1 = fnum_find("STANDARD_INFO");
572 correct_size = s1->standard_info.out.alloc_size;
573 torture_comment(torture, "alloc_size: %u\n", (unsigned int)correct_size);
575 SIZE_CHECK("GETATTRE", getattre, alloc_size);
576 SIZE_CHECK("STANDARD", standard, alloc_size);
577 SIZE_CHECK("EA_SIZE", ea_size, alloc_size);
578 SIZE_CHECK("STANDARD_INFO", standard_info, alloc_size);
579 SIZE_CHECK("STANDARD_INFORMATION", standard_info, alloc_size);
580 SIZE_CHECK("ALL_INFO", all_info, alloc_size);
581 SIZE_CHECK("ALL_INFORMATION", all_info, alloc_size);
582 SIZE_CHECK("NETWORK_OPEN_INFORMATION", network_open_information, alloc_size);
583 if (!skip_streams) {
584 SIZE_CHECK("STREAM_INFO", stream_info, streams[0].alloc_size);
585 SIZE_CHECK("STREAM_INFORMATION", stream_info, streams[0].alloc_size);
588 #define ATTRIB_CHECK(sname, stype, tfield) do { \
589 s1 = fnum_find(sname); \
590 if (s1 && s1->stype.out.tfield != correct_attrib) { \
591 printf("(%d) handle %s/%s incorrect - 0x%x should be 0x%x\n", __LINE__, #stype, #tfield, \
592 (unsigned int)s1->stype.out.tfield, \
593 (unsigned int)correct_attrib); \
594 ret = false; \
596 s1 = fname_find(is_ipc, sname); \
597 if (s1 && s1->stype.out.tfield != correct_attrib) { \
598 printf("(%d) path %s/%s incorrect - 0x%x should be 0x%x\n", __LINE__, #stype, #tfield, \
599 (unsigned int)s1->stype.out.tfield, \
600 (unsigned int)correct_attrib); \
601 ret = false; \
602 }} while (0)
604 s1 = fnum_find("BASIC_INFO");
605 correct_attrib = s1->basic_info.out.attrib;
606 torture_comment(torture, "attrib: 0x%x\n", (unsigned int)correct_attrib);
608 ATTRIB_CHECK("GETATTR", getattr, attrib);
609 if (!is_ipc) {
610 ATTRIB_CHECK("GETATTRE", getattre, attrib);
611 ATTRIB_CHECK("STANDARD", standard, attrib);
612 ATTRIB_CHECK("EA_SIZE", ea_size, attrib);
614 ATTRIB_CHECK("BASIC_INFO", basic_info, attrib);
615 ATTRIB_CHECK("BASIC_INFORMATION", basic_info, attrib);
616 ATTRIB_CHECK("ALL_INFO", all_info, attrib);
617 ATTRIB_CHECK("ALL_INFORMATION", all_info, attrib);
618 ATTRIB_CHECK("NETWORK_OPEN_INFORMATION", network_open_information, attrib);
619 ATTRIB_CHECK("ATTRIBUTE_TAG_INFORMATION", attribute_tag_information, attrib);
621 correct_name = fname;
622 torture_comment(torture, "name: %s\n", correct_name);
624 #define NAME_CHECK(sname, stype, tfield, flags) do { \
625 s1 = fnum_find(sname); \
626 if (s1 && (strcmp_safe(s1->stype.out.tfield.s, correct_name) != 0 || \
627 wire_bad_flags(&s1->stype.out.tfield, flags, tree->session->transport))) { \
628 printf("(%d) handle %s/%s incorrect - '%s/%d'\n", __LINE__, #stype, #tfield, \
629 s1->stype.out.tfield.s, s1->stype.out.tfield.private_length); \
630 ret = false; \
632 s1 = fname_find(is_ipc, sname); \
633 if (s1 && (strcmp_safe(s1->stype.out.tfield.s, correct_name) != 0 || \
634 wire_bad_flags(&s1->stype.out.tfield, flags, tree->session->transport))) { \
635 printf("(%d) path %s/%s incorrect - '%s/%d'\n", __LINE__, #stype, #tfield, \
636 s1->stype.out.tfield.s, s1->stype.out.tfield.private_length); \
637 ret = false; \
638 }} while (0)
640 NAME_CHECK("NAME_INFO", name_info, fname, STR_UNICODE);
641 NAME_CHECK("NAME_INFORMATION", name_info, fname, STR_UNICODE);
643 /* the ALL_INFO file name is the full path on the filesystem */
644 s1 = fnum_find("ALL_INFO");
645 if (s1 && !s1->all_info.out.fname.s) {
646 torture_fail(torture, "ALL_INFO didn't give a filename");
648 if (s1 && s1->all_info.out.fname.s) {
649 char *p = strrchr(s1->all_info.out.fname.s, '\\');
650 if (!p) {
651 printf("Not a full path in all_info/fname? - '%s'\n",
652 s1->all_info.out.fname.s);
653 ret = false;
654 } else {
655 if (strcmp_safe(correct_name, p) != 0) {
656 printf("incorrect basename in all_info/fname - '%s'\n",
657 s1->all_info.out.fname.s);
658 ret = false;
661 if (wire_bad_flags(&s1->all_info.out.fname, STR_UNICODE, tree->session->transport)) {
662 printf("Should not null terminate all_info/fname\n");
663 ret = false;
667 s1 = fnum_find("ALT_NAME_INFO");
668 if (s1) {
669 correct_name = s1->alt_name_info.out.fname.s;
672 if (!correct_name) {
673 torture_comment(torture, "no alternate name information\n");
674 } else {
675 torture_comment(torture, "alt_name: %s\n", correct_name);
677 NAME_CHECK("ALT_NAME_INFO", alt_name_info, fname, STR_UNICODE);
678 NAME_CHECK("ALT_NAME_INFORMATION", alt_name_info, fname, STR_UNICODE);
680 /* and make sure we can open by alternate name */
681 smbcli_close(tree, fnum);
682 fnum = smbcli_nt_create_full(tree, correct_name, 0,
683 SEC_RIGHTS_FILE_ALL,
684 FILE_ATTRIBUTE_NORMAL,
685 NTCREATEX_SHARE_ACCESS_DELETE|
686 NTCREATEX_SHARE_ACCESS_READ|
687 NTCREATEX_SHARE_ACCESS_WRITE,
688 NTCREATEX_DISP_OVERWRITE_IF,
689 0, 0);
690 if (fnum == -1) {
691 printf("Unable to open by alt_name - %s\n", smbcli_errstr(tree));
692 ret = false;
695 if (!skip_streams) {
696 correct_name = "::$DATA";
697 torture_comment(torture, "stream_name: %s\n", correct_name);
699 NAME_CHECK("STREAM_INFO", stream_info, streams[0].stream_name, STR_UNICODE);
700 NAME_CHECK("STREAM_INFORMATION", stream_info, streams[0].stream_name, STR_UNICODE);
704 /* make sure the EAs look right */
705 s1 = fnum_find("ALL_EAS");
706 s2 = fnum_find("ALL_INFO");
707 if (s1) {
708 for (i=0;i<s1->all_eas.out.num_eas;i++) {
709 printf(" flags=%d %s=%*.*s\n",
710 s1->all_eas.out.eas[i].flags,
711 s1->all_eas.out.eas[i].name.s,
712 (int)s1->all_eas.out.eas[i].value.length,
713 (int)s1->all_eas.out.eas[i].value.length,
714 s1->all_eas.out.eas[i].value.data);
717 if (s1 && s2) {
718 if (s1->all_eas.out.num_eas == 0) {
719 if (s2->all_info.out.ea_size != 0) {
720 printf("ERROR: num_eas==0 but fnum all_info.out.ea_size == %d\n",
721 s2->all_info.out.ea_size);
723 } else {
724 if (s2->all_info.out.ea_size !=
725 ea_list_size(s1->all_eas.out.num_eas, s1->all_eas.out.eas)) {
726 printf("ERROR: ea_list_size=%d != fnum all_info.out.ea_size=%d\n",
727 (int)ea_list_size(s1->all_eas.out.num_eas, s1->all_eas.out.eas),
728 (int)s2->all_info.out.ea_size);
732 s2 = fname_find(is_ipc, "ALL_EAS");
733 if (s2) {
734 VAL_EQUAL(all_eas, num_eas, all_eas, num_eas);
735 for (i=0;i<s1->all_eas.out.num_eas;i++) {
736 VAL_EQUAL(all_eas, eas[i].flags, all_eas, eas[i].flags);
737 STR_EQUAL(all_eas, eas[i].name, all_eas, eas[i].name);
738 VAL_EQUAL(all_eas, eas[i].value.length, all_eas, eas[i].value.length);
742 #define VAL_CHECK(sname1, stype1, tfield1, sname2, stype2, tfield2) do { \
743 s1 = fnum_find(sname1); s2 = fnum_find(sname2); \
744 if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \
745 printf("(%d) handle %s/%s != %s/%s - 0x%x vs 0x%x\n", __LINE__, \
746 #stype1, #tfield1, #stype2, #tfield2, \
747 s1->stype1.out.tfield1, s2->stype2.out.tfield2); \
748 ret = false; \
750 s1 = fname_find(is_ipc, sname1); s2 = fname_find(is_ipc, sname2); \
751 if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \
752 printf("(%d) path %s/%s != %s/%s - 0x%x vs 0x%x\n", __LINE__, \
753 #stype1, #tfield1, #stype2, #tfield2, \
754 s1->stype1.out.tfield1, s2->stype2.out.tfield2); \
755 ret = false; \
757 s1 = fnum_find(sname1); s2 = fname_find(is_ipc, sname2); \
758 if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \
759 printf("(%d) handle %s/%s != path %s/%s - 0x%x vs 0x%x\n", __LINE__, \
760 #stype1, #tfield1, #stype2, #tfield2, \
761 s1->stype1.out.tfield1, s2->stype2.out.tfield2); \
762 ret = false; \
764 s1 = fname_find(is_ipc, sname1); s2 = fnum_find(sname2); \
765 if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \
766 printf("(%d) path %s/%s != handle %s/%s - 0x%x vs 0x%x\n", __LINE__, \
767 #stype1, #tfield1, #stype2, #tfield2, \
768 s1->stype1.out.tfield1, s2->stype2.out.tfield2); \
769 ret = false; \
770 }} while (0)
772 VAL_CHECK("STANDARD_INFO", standard_info, delete_pending,
773 "ALL_INFO", all_info, delete_pending);
774 VAL_CHECK("STANDARD_INFO", standard_info, directory,
775 "ALL_INFO", all_info, directory);
776 VAL_CHECK("STANDARD_INFO", standard_info, nlink,
777 "ALL_INFO", all_info, nlink);
778 s1 = fnum_find("BASIC_INFO");
779 if (s1 && is_ipc) {
780 if (s1->basic_info.out.attrib != FILE_ATTRIBUTE_NORMAL) {
781 printf("(%d) attrib basic_info/nlink incorrect - %d should be %d\n", __LINE__, s1->basic_info.out.attrib, FILE_ATTRIBUTE_NORMAL);
782 ret = false;
785 s1 = fnum_find("STANDARD_INFO");
786 if (s1 && is_ipc) {
787 if (s1->standard_info.out.nlink != 1) {
788 printf("(%d) nlinks standard_info/nlink incorrect - %d should be 1\n", __LINE__, s1->standard_info.out.nlink);
789 ret = false;
791 if (s1->standard_info.out.delete_pending != 1) {
792 printf("(%d) nlinks standard_info/delete_pending incorrect - %d should be 1\n", __LINE__, s1->standard_info.out.delete_pending);
793 ret = false;
796 VAL_CHECK("EA_INFO", ea_info, ea_size,
797 "ALL_INFO", all_info, ea_size);
798 if (!is_ipc) {
799 VAL_CHECK("EA_SIZE", ea_size, ea_size,
800 "ALL_INFO", all_info, ea_size);
803 #define NAME_PATH_CHECK(sname, stype, field) do { \
804 s1 = fname_find(is_ipc, sname); s2 = fnum_find(sname); \
805 if (s1 && s2) { \
806 VAL_EQUAL(stype, field, stype, field); \
808 } while (0)
811 s1 = fnum_find("INTERNAL_INFORMATION");
812 if (s1) {
813 torture_comment(torture, "file_id=%.0f\n", (double)s1->internal_information.out.file_id);
816 NAME_PATH_CHECK("INTERNAL_INFORMATION", internal_information, file_id);
817 NAME_PATH_CHECK("POSITION_INFORMATION", position_information, position);
818 if (s1 && s2) {
819 printf("fnum pos = %.0f, fname pos = %.0f\n",
820 (double)s2->position_information.out.position,
821 (double)s1->position_information.out.position );
823 NAME_PATH_CHECK("MODE_INFORMATION", mode_information, mode);
824 NAME_PATH_CHECK("ALIGNMENT_INFORMATION", alignment_information, alignment_requirement);
825 NAME_PATH_CHECK("ATTRIBUTE_TAG_INFORMATION", attribute_tag_information, attrib);
826 NAME_PATH_CHECK("ATTRIBUTE_TAG_INFORMATION", attribute_tag_information, reparse_tag);
828 #if 0
829 /* these are expected to differ */
830 NAME_PATH_CHECK("ACCESS_INFORMATION", access_information, access_flags);
831 #endif
833 #if 0 /* unused */
834 #define UNKNOWN_CHECK(sname, stype, tfield) do { \
835 s1 = fnum_find(sname); \
836 if (s1 && s1->stype.out.tfield != 0) { \
837 printf("(%d) handle %s/%s unknown != 0 (0x%x)\n", __LINE__, \
838 #stype, #tfield, \
839 (unsigned int)s1->stype.out.tfield); \
841 s1 = fname_find(is_ipc, sname); \
842 if (s1 && s1->stype.out.tfield != 0) { \
843 printf("(%d) path %s/%s unknown != 0 (0x%x)\n", __LINE__, \
844 #stype, #tfield, \
845 (unsigned int)s1->stype.out.tfield); \
846 }} while (0)
847 #endif
848 /* now get a bit fancier .... */
850 /* when we set the delete disposition then the link count should drop
851 to 0 and delete_pending should be 1 */
853 return ret;
856 /* basic testing of all RAW_FILEINFO_* calls
857 for each call we test that it succeeds, and where possible test
858 for consistency between the calls.
860 bool torture_raw_qfileinfo(struct torture_context *torture,
861 struct smbcli_state *cli)
863 int fnum;
864 bool ret;
865 const char *fname = "\\torture_qfileinfo.txt";
867 fnum = create_complex_file(cli, torture, fname);
868 if (fnum == -1) {
869 printf("ERROR: open of %s failed (%s)\n", fname, smbcli_errstr(cli->tree));
870 return false;
873 ret = torture_raw_qfileinfo_internals(torture, torture, cli->tree, fnum, fname, false /* is_ipc */);
875 smbcli_close(cli->tree, fnum);
876 smbcli_unlink(cli->tree, fname);
878 return ret;
881 bool torture_raw_qfileinfo_pipe(struct torture_context *torture,
882 struct smbcli_state *cli)
884 bool ret = true;
885 int fnum;
886 const char *fname = "\\lsass";
887 struct dcerpc_pipe *p;
888 struct smbcli_tree *ipc_tree;
889 NTSTATUS status;
891 if (!(p = dcerpc_pipe_init(torture, cli->tree->session->transport->socket->event.ctx))) {
892 return false;
895 status = dcerpc_pipe_open_smb(p, cli->tree, fname);
896 torture_assert_ntstatus_ok(torture, status, "dcerpc_pipe_open_smb failed");
898 ipc_tree = dcerpc_smb_tree(p->conn);
899 fnum = dcerpc_smb_fnum(p->conn);
901 ret = torture_raw_qfileinfo_internals(torture, torture, ipc_tree, fnum, fname, true /* is_ipc */);
903 talloc_free(p);
904 return ret;