s3/docs: Raise version number up to 3.5.
[Samba/gebeck_regimport.git] / source4 / torture / raw / qfileinfo.c
blobc7673be52633121714f4f81b3c420de56d9d26c1
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 "torture/torture.h"
23 #include "libcli/raw/libcliraw.h"
24 #include "libcli/raw/raw_proto.h"
25 #include "libcli/libcli.h"
26 #include "torture/util.h"
27 #include "librpc/rpc/dcerpc.h"
28 #include "torture/rpc/rpc.h"
29 #include "torture/raw/proto.h"
30 #include "param/param.h"
32 static struct {
33 const char *name;
34 enum smb_fileinfo_level level;
35 uint_t only_paths:1;
36 uint_t only_handles:1;
37 uint32_t capability_mask;
38 uint_t expected_ipc_access_denied:1;
39 NTSTATUS expected_ipc_fnum_status;
40 NTSTATUS fnum_status, fname_status;
41 union smb_fileinfo fnum_finfo, fname_finfo;
42 } levels[] = {
43 { .name = "GETATTR",
44 .level = RAW_FILEINFO_GETATTR,
45 .only_paths = 1,
46 .only_handles = 0,
47 .expected_ipc_access_denied = 1},
48 { .name ="GETATTRE",
49 .level = RAW_FILEINFO_GETATTRE,
50 .only_paths = 0,
51 .only_handles = 1 },
52 { .name ="STANDARD",
53 .level = RAW_FILEINFO_STANDARD, },
54 { .name ="EA_SIZE",
55 .level = RAW_FILEINFO_EA_SIZE },
56 { .name ="ALL_EAS",
57 .level = RAW_FILEINFO_ALL_EAS,
58 .expected_ipc_fnum_status = NT_STATUS_ACCESS_DENIED,
60 { .name ="IS_NAME_VALID",
61 .level = RAW_FILEINFO_IS_NAME_VALID,
62 .only_paths = 1,
63 .only_handles = 0 },
64 { .name ="BASIC_INFO",
65 .level = RAW_FILEINFO_BASIC_INFO },
66 { .name ="STANDARD_INFO",
67 .level = RAW_FILEINFO_STANDARD_INFO },
68 { .name ="EA_INFO",
69 .level = RAW_FILEINFO_EA_INFO },
70 { .name ="NAME_INFO",
71 .level = RAW_FILEINFO_NAME_INFO },
72 { .name ="ALL_INFO",
73 .level = RAW_FILEINFO_ALL_INFO },
74 { .name ="ALT_NAME_INFO",
75 .level = RAW_FILEINFO_ALT_NAME_INFO,
76 .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER
78 { .name ="STREAM_INFO",
79 .level = RAW_FILEINFO_STREAM_INFO,
80 .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER
82 { .name ="COMPRESSION_INFO",
83 .level = RAW_FILEINFO_COMPRESSION_INFO,
84 .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER
86 { .name ="UNIX_BASIC_INFO",
87 .level = RAW_FILEINFO_UNIX_BASIC,
88 .only_paths = 0,
89 .only_handles = 0,
90 .capability_mask = CAP_UNIX},
91 { .name ="UNIX_LINK_INFO",
92 .level = RAW_FILEINFO_UNIX_LINK,
93 .only_paths = 0,
94 .only_handles = 0,
95 .capability_mask = CAP_UNIX},
96 { .name ="BASIC_INFORMATION",
97 .level = RAW_FILEINFO_BASIC_INFORMATION },
98 { .name ="STANDARD_INFORMATION",
99 .level = RAW_FILEINFO_STANDARD_INFORMATION },
100 { .name ="INTERNAL_INFORMATION",
101 .level = RAW_FILEINFO_INTERNAL_INFORMATION },
102 { .name ="EA_INFORMATION",
103 .level = RAW_FILEINFO_EA_INFORMATION },
104 { .name = "ACCESS_INFORMATION",
105 .level = RAW_FILEINFO_ACCESS_INFORMATION },
106 { .name = "NAME_INFORMATION",
107 .level = RAW_FILEINFO_NAME_INFORMATION },
108 { .name ="POSITION_INFORMATION",
109 .level = RAW_FILEINFO_POSITION_INFORMATION },
110 { .name ="MODE_INFORMATION",
111 .level = RAW_FILEINFO_MODE_INFORMATION },
112 { .name ="ALIGNMENT_INFORMATION",
113 .level = RAW_FILEINFO_ALIGNMENT_INFORMATION },
114 { .name ="ALL_INFORMATION",
115 .level = RAW_FILEINFO_ALL_INFORMATION },
116 { .name ="ALT_NAME_INFORMATION",
117 .level = RAW_FILEINFO_ALT_NAME_INFORMATION,
118 .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER
120 { .name ="STREAM_INFORMATION",
121 .level = RAW_FILEINFO_STREAM_INFORMATION,
122 .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER
124 { .name = "COMPRESSION_INFORMATION",
125 .level = RAW_FILEINFO_COMPRESSION_INFORMATION,
126 .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER
128 { .name ="NETWORK_OPEN_INFORMATION",
129 .level = RAW_FILEINFO_NETWORK_OPEN_INFORMATION,
130 .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER
132 { .name = "ATTRIBUTE_TAG_INFORMATION",
133 .level = RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION,
134 .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER
136 { NULL }
140 compare a dos time (2 second resolution) to a nt time
142 static int dos_nt_time_cmp(time_t t, NTTIME nt)
144 time_t t2 = nt_time_to_unix(nt);
145 if (abs(t2 - t) <= 2) return 0;
146 return t2 - t;
151 find a level in the levels[] table
153 static union smb_fileinfo *fnum_find(const char *name)
155 int i;
156 for (i=0; levels[i].name; i++) {
157 if (NT_STATUS_IS_OK(levels[i].fnum_status) &&
158 strcmp(name, levels[i].name) == 0 &&
159 !levels[i].only_paths) {
160 return &levels[i].fnum_finfo;
163 return NULL;
167 find a level in the levels[] table
169 static union smb_fileinfo *fname_find(bool is_ipc, const char *name)
171 int i;
172 if (is_ipc) {
173 return NULL;
175 for (i=0; levels[i].name; i++) {
176 if (NT_STATUS_IS_OK(levels[i].fname_status) &&
177 strcmp(name, levels[i].name) == 0 &&
178 !levels[i].only_handles) {
179 return &levels[i].fname_finfo;
182 return NULL;
185 /* local macros to make the code below more readable */
186 #define VAL_EQUAL(n1, v1, n2, v2) do {if (s1->n1.out.v1 != s2->n2.out.v2) { \
187 printf("%s/%s [%u] != %s/%s [%u] at %s(%d)\n", \
188 #n1, #v1, (uint_t)s1->n1.out.v1, \
189 #n2, #v2, (uint_t)s2->n2.out.v2, \
190 __FILE__, __LINE__); \
191 ret = false; \
192 }} while(0)
194 #define STR_EQUAL(n1, v1, n2, v2) do {if (strcmp_safe(s1->n1.out.v1.s, s2->n2.out.v2.s) || \
195 s1->n1.out.v1.private_length != s2->n2.out.v2.private_length) { \
196 printf("%s/%s [%s/%d] != %s/%s [%s/%d] at %s(%d)\n", \
197 #n1, #v1, s1->n1.out.v1.s, s1->n1.out.v1.private_length, \
198 #n2, #v2, s2->n2.out.v2.s, s2->n2.out.v2.private_length, \
199 __FILE__, __LINE__); \
200 ret = false; \
201 }} while(0)
203 #define STRUCT_EQUAL(n1, v1, n2, v2) do {if (memcmp(&s1->n1.out.v1,&s2->n2.out.v2,sizeof(s1->n1.out.v1))) { \
204 printf("%s/%s != %s/%s at %s(%d)\n", \
205 #n1, #v1, \
206 #n2, #v2, \
207 __FILE__, __LINE__); \
208 ret = false; \
209 }} while(0)
211 /* used to find hints on unknown values - and to make sure
212 we zero-fill */
213 #if 0 /* unused */
214 #define VAL_UNKNOWN(n1, v1) do {if (s1->n1.out.v1 != 0) { \
215 printf("%s/%s non-zero unknown - %u (0x%x) at %s(%d)\n", \
216 #n1, #v1, \
217 (uint_t)s1->n1.out.v1, \
218 (uint_t)s1->n1.out.v1, \
219 __FILE__, __LINE__); \
220 ret = false; \
221 }} while(0)
222 #endif
224 /* basic testing of all RAW_FILEINFO_* calls
225 for each call we test that it succeeds, and where possible test
226 for consistency between the calls.
228 static bool torture_raw_qfileinfo_internals(struct torture_context *torture,
229 TALLOC_CTX *mem_ctx,
230 struct smbcli_tree *tree,
231 int fnum, const char *fname,
232 bool is_ipc)
234 int i;
235 bool ret = true;
236 int count;
237 union smb_fileinfo *s1, *s2;
238 NTTIME correct_time;
239 uint64_t correct_size;
240 uint32_t correct_attrib;
241 const char *correct_name;
242 bool skip_streams = false;
244 /* scan all the fileinfo and pathinfo levels */
245 for (i=0; levels[i].name; i++) {
246 if (!levels[i].only_paths) {
247 levels[i].fnum_finfo.generic.level = levels[i].level;
248 levels[i].fnum_finfo.generic.in.file.fnum = fnum;
249 levels[i].fnum_status = smb_raw_fileinfo(tree, mem_ctx,
250 &levels[i].fnum_finfo);
253 if (!levels[i].only_handles) {
254 levels[i].fname_finfo.generic.level = levels[i].level;
255 levels[i].fname_finfo.generic.in.file.path = talloc_strdup(mem_ctx, fname);
256 levels[i].fname_status = smb_raw_pathinfo(tree, mem_ctx,
257 &levels[i].fname_finfo);
261 /* check for completely broken levels */
262 for (count=i=0; levels[i].name; i++) {
263 uint32_t cap = tree->session->transport->negotiate.capabilities;
264 /* see if this server claims to support this level */
265 if ((cap & levels[i].capability_mask) != levels[i].capability_mask) {
266 continue;
269 if (is_ipc) {
270 if (levels[i].expected_ipc_access_denied && NT_STATUS_EQUAL(NT_STATUS_ACCESS_DENIED, levels[i].fname_status)) {
271 } else if (!levels[i].only_handles && !NT_STATUS_EQUAL(NT_STATUS_INVALID_DEVICE_REQUEST, levels[i].fname_status)) {
272 printf("ERROR: fname level %s failed, expected NT_STATUS_INVALID_DEVICE_REQUEST - %s\n",
273 levels[i].name, nt_errstr(levels[i].fname_status));
274 count++;
276 if (!levels[i].only_paths && !NT_STATUS_EQUAL(levels[i].expected_ipc_fnum_status, levels[i].fnum_status)) {
277 printf("ERROR: fnum level %s failed, expected %s - %s\n",
278 levels[i].name, nt_errstr(levels[i].expected_ipc_fnum_status),
279 nt_errstr(levels[i].fnum_status));
280 count++;
282 } else {
283 if (!levels[i].only_paths && !NT_STATUS_IS_OK(levels[i].fnum_status)) {
284 printf("ERROR: fnum level %s failed - %s\n",
285 levels[i].name, nt_errstr(levels[i].fnum_status));
286 count++;
288 if (!levels[i].only_handles && !NT_STATUS_IS_OK(levels[i].fname_status)) {
289 printf("ERROR: fname level %s failed - %s\n",
290 levels[i].name, nt_errstr(levels[i].fname_status));
291 count++;
297 if (count != 0) {
298 ret = false;
299 printf("%d levels failed\n", count);
300 if (count > 35) {
301 torture_fail(torture, "too many level failures - giving up");
305 /* see if we can do streams */
306 s1 = fnum_find("STREAM_INFO");
307 if (!s1 || s1->stream_info.out.num_streams == 0) {
308 if (!is_ipc) {
309 printf("STREAM_INFO broken (%d) - skipping streams checks\n",
310 s1 ? s1->stream_info.out.num_streams : -1);
312 skip_streams = true;
316 /* this code is incredibly repititive but doesn't lend itself to loops, so
317 we use lots of macros to make it less painful */
319 /* first off we check the levels that are supposed to be aliases. It will be quite rare for
320 this code to fail, but we need to check it for completeness */
324 #define ALIAS_CHECK(sname1, sname2) \
325 do { \
326 s1 = fnum_find(sname1); s2 = fnum_find(sname2); \
327 if (s1 && s2) { INFO_CHECK } \
328 s1 = fname_find(is_ipc, sname1); s2 = fname_find(is_ipc, sname2); \
329 if (s1 && s2) { INFO_CHECK } \
330 s1 = fnum_find(sname1); s2 = fname_find(is_ipc, sname2); \
331 if (s1 && s2) { INFO_CHECK } \
332 } while (0)
334 #define INFO_CHECK \
335 STRUCT_EQUAL(basic_info, create_time, basic_info, create_time); \
336 STRUCT_EQUAL(basic_info, access_time, basic_info, access_time); \
337 STRUCT_EQUAL(basic_info, write_time, basic_info, write_time); \
338 STRUCT_EQUAL(basic_info, change_time, basic_info, change_time); \
339 VAL_EQUAL (basic_info, attrib, basic_info, attrib);
341 ALIAS_CHECK("BASIC_INFO", "BASIC_INFORMATION");
343 #undef INFO_CHECK
344 #define INFO_CHECK \
345 VAL_EQUAL(standard_info, alloc_size, standard_info, alloc_size); \
346 VAL_EQUAL(standard_info, size, standard_info, size); \
347 VAL_EQUAL(standard_info, nlink, standard_info, nlink); \
348 VAL_EQUAL(standard_info, delete_pending, standard_info, delete_pending); \
349 VAL_EQUAL(standard_info, directory, standard_info, directory);
351 ALIAS_CHECK("STANDARD_INFO", "STANDARD_INFORMATION");
353 #undef INFO_CHECK
354 #define INFO_CHECK \
355 VAL_EQUAL(ea_info, ea_size, ea_info, ea_size);
357 ALIAS_CHECK("EA_INFO", "EA_INFORMATION");
359 #undef INFO_CHECK
360 #define INFO_CHECK \
361 STR_EQUAL(name_info, fname, name_info, fname);
363 ALIAS_CHECK("NAME_INFO", "NAME_INFORMATION");
365 #undef INFO_CHECK
366 #define INFO_CHECK \
367 STRUCT_EQUAL(all_info, create_time, all_info, create_time); \
368 STRUCT_EQUAL(all_info, access_time, all_info, access_time); \
369 STRUCT_EQUAL(all_info, write_time, all_info, write_time); \
370 STRUCT_EQUAL(all_info, change_time, all_info, change_time); \
371 VAL_EQUAL(all_info, attrib, all_info, attrib); \
372 VAL_EQUAL(all_info, alloc_size, all_info, alloc_size); \
373 VAL_EQUAL(all_info, size, all_info, size); \
374 VAL_EQUAL(all_info, nlink, all_info, nlink); \
375 VAL_EQUAL(all_info, delete_pending, all_info, delete_pending); \
376 VAL_EQUAL(all_info, directory, all_info, directory); \
377 VAL_EQUAL(all_info, ea_size, all_info, ea_size); \
378 STR_EQUAL(all_info, fname, all_info, fname);
380 ALIAS_CHECK("ALL_INFO", "ALL_INFORMATION");
382 #undef INFO_CHECK
383 #define INFO_CHECK \
384 VAL_EQUAL(compression_info, compressed_size,compression_info, compressed_size); \
385 VAL_EQUAL(compression_info, format, compression_info, format); \
386 VAL_EQUAL(compression_info, unit_shift, compression_info, unit_shift); \
387 VAL_EQUAL(compression_info, chunk_shift, compression_info, chunk_shift); \
388 VAL_EQUAL(compression_info, cluster_shift, compression_info, cluster_shift);
390 ALIAS_CHECK("COMPRESSION_INFO", "COMPRESSION_INFORMATION");
393 #undef INFO_CHECK
394 #define INFO_CHECK \
395 STR_EQUAL(alt_name_info, fname, alt_name_info, fname);
397 ALIAS_CHECK("ALT_NAME_INFO", "ALT_NAME_INFORMATION");
400 #define TIME_CHECK_NT(sname, stype, tfield) do { \
401 s1 = fnum_find(sname); \
402 if (s1 && memcmp(&s1->stype.out.tfield, &correct_time, sizeof(correct_time)) != 0) { \
403 printf("(%d) handle %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \
404 nt_time_string(mem_ctx, s1->stype.out.tfield), \
405 nt_time_string(mem_ctx, correct_time)); \
406 ret = false; \
408 s1 = fname_find(is_ipc, sname); \
409 if (s1 && memcmp(&s1->stype.out.tfield, &correct_time, sizeof(correct_time)) != 0) { \
410 printf("(%d) path %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \
411 nt_time_string(mem_ctx, s1->stype.out.tfield), \
412 nt_time_string(mem_ctx, correct_time)); \
413 ret = false; \
414 }} while (0)
416 #define TIME_CHECK_DOS(sname, stype, tfield) do { \
417 s1 = fnum_find(sname); \
418 if (s1 && dos_nt_time_cmp(s1->stype.out.tfield, correct_time) != 0) { \
419 printf("(%d) handle %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \
420 timestring(mem_ctx, s1->stype.out.tfield), \
421 nt_time_string(mem_ctx, correct_time)); \
422 ret = false; \
424 s1 = fname_find(is_ipc, sname); \
425 if (s1 && dos_nt_time_cmp(s1->stype.out.tfield, correct_time) != 0) { \
426 printf("(%d) path %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \
427 timestring(mem_ctx, s1->stype.out.tfield), \
428 nt_time_string(mem_ctx, correct_time)); \
429 ret = false; \
430 }} while (0)
432 #if 0 /* unused */
433 #define TIME_CHECK_UNX(sname, stype, tfield) do { \
434 s1 = fnum_find(sname); \
435 if (s1 && unx_nt_time_cmp(s1->stype.out.tfield, correct_time) != 0) { \
436 printf("(%d) handle %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \
437 timestring(mem_ctx, s1->stype.out.tfield), \
438 nt_time_string(mem_ctx, correct_time)); \
439 ret = false; \
441 s1 = fname_find(is_ipc, sname); \
442 if (s1 && unx_nt_time_cmp(s1->stype.out.tfield, correct_time) != 0) { \
443 printf("(%d) path %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield, \
444 timestring(mem_ctx, s1->stype.out.tfield), \
445 nt_time_string(mem_ctx, correct_time)); \
446 ret = false; \
447 }} while (0)
448 #endif
450 /* now check that all the times that are supposed to be equal are correct */
451 s1 = fnum_find("BASIC_INFO");
452 correct_time = s1->basic_info.out.create_time;
453 torture_comment(torture, "create_time: %s\n", nt_time_string(mem_ctx, correct_time));
455 TIME_CHECK_NT ("BASIC_INFO", basic_info, create_time);
456 TIME_CHECK_NT ("BASIC_INFORMATION", basic_info, create_time);
457 TIME_CHECK_DOS("GETATTRE", getattre, create_time);
458 TIME_CHECK_DOS("STANDARD", standard, create_time);
459 TIME_CHECK_DOS("EA_SIZE", ea_size, create_time);
460 TIME_CHECK_NT ("ALL_INFO", all_info, create_time);
461 TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information, create_time);
463 s1 = fnum_find("BASIC_INFO");
464 correct_time = s1->basic_info.out.access_time;
465 torture_comment(torture, "access_time: %s\n", nt_time_string(mem_ctx, correct_time));
467 TIME_CHECK_NT ("BASIC_INFO", basic_info, access_time);
468 TIME_CHECK_NT ("BASIC_INFORMATION", basic_info, access_time);
469 TIME_CHECK_DOS("GETATTRE", getattre, access_time);
470 TIME_CHECK_DOS("STANDARD", standard, access_time);
471 TIME_CHECK_DOS("EA_SIZE", ea_size, access_time);
472 TIME_CHECK_NT ("ALL_INFO", all_info, access_time);
473 TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information, access_time);
475 s1 = fnum_find("BASIC_INFO");
476 correct_time = s1->basic_info.out.write_time;
477 torture_comment(torture, "write_time : %s\n", nt_time_string(mem_ctx, correct_time));
479 TIME_CHECK_NT ("BASIC_INFO", basic_info, write_time);
480 TIME_CHECK_NT ("BASIC_INFORMATION", basic_info, write_time);
481 TIME_CHECK_DOS("GETATTR", getattr, write_time);
482 TIME_CHECK_DOS("GETATTRE", getattre, write_time);
483 TIME_CHECK_DOS("STANDARD", standard, write_time);
484 TIME_CHECK_DOS("EA_SIZE", ea_size, write_time);
485 TIME_CHECK_NT ("ALL_INFO", all_info, write_time);
486 TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information, write_time);
488 s1 = fnum_find("BASIC_INFO");
489 correct_time = s1->basic_info.out.change_time;
490 torture_comment(torture, "change_time: %s\n", nt_time_string(mem_ctx, correct_time));
492 TIME_CHECK_NT ("BASIC_INFO", basic_info, change_time);
493 TIME_CHECK_NT ("BASIC_INFORMATION", basic_info, change_time);
494 TIME_CHECK_NT ("ALL_INFO", all_info, change_time);
495 TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information, change_time);
498 #define SIZE_CHECK(sname, stype, tfield) do { \
499 s1 = fnum_find(sname); \
500 if (s1 && s1->stype.out.tfield != correct_size) { \
501 printf("(%d) handle %s/%s incorrect - %u should be %u\n", __LINE__, #stype, #tfield, \
502 (uint_t)s1->stype.out.tfield, \
503 (uint_t)correct_size); \
504 ret = false; \
506 s1 = fname_find(is_ipc, sname); \
507 if (s1 && s1->stype.out.tfield != correct_size) { \
508 printf("(%d) path %s/%s incorrect - %u should be %u\n", __LINE__, #stype, #tfield, \
509 (uint_t)s1->stype.out.tfield, \
510 (uint_t)correct_size); \
511 ret = false; \
512 }} while (0)
514 s1 = fnum_find("STANDARD_INFO");
515 correct_size = s1->standard_info.out.size;
516 torture_comment(torture, "size: %u\n", (uint_t)correct_size);
518 SIZE_CHECK("GETATTR", getattr, size);
519 SIZE_CHECK("GETATTRE", getattre, size);
520 SIZE_CHECK("STANDARD", standard, size);
521 SIZE_CHECK("EA_SIZE", ea_size, size);
522 SIZE_CHECK("STANDARD_INFO", standard_info, size);
523 SIZE_CHECK("STANDARD_INFORMATION", standard_info, size);
524 SIZE_CHECK("ALL_INFO", all_info, size);
525 SIZE_CHECK("ALL_INFORMATION", all_info, size);
526 SIZE_CHECK("COMPRESSION_INFO", compression_info, compressed_size);
527 SIZE_CHECK("COMPRESSION_INFORMATION", compression_info, compressed_size);
528 SIZE_CHECK("NETWORK_OPEN_INFORMATION", network_open_information, size);
529 if (!skip_streams) {
530 SIZE_CHECK("STREAM_INFO", stream_info, streams[0].size);
531 SIZE_CHECK("STREAM_INFORMATION", stream_info, streams[0].size);
535 s1 = fnum_find("STANDARD_INFO");
536 correct_size = s1->standard_info.out.alloc_size;
537 torture_comment(torture, "alloc_size: %u\n", (uint_t)correct_size);
539 SIZE_CHECK("GETATTRE", getattre, alloc_size);
540 SIZE_CHECK("STANDARD", standard, alloc_size);
541 SIZE_CHECK("EA_SIZE", ea_size, alloc_size);
542 SIZE_CHECK("STANDARD_INFO", standard_info, alloc_size);
543 SIZE_CHECK("STANDARD_INFORMATION", standard_info, alloc_size);
544 SIZE_CHECK("ALL_INFO", all_info, alloc_size);
545 SIZE_CHECK("ALL_INFORMATION", all_info, alloc_size);
546 SIZE_CHECK("NETWORK_OPEN_INFORMATION", network_open_information, alloc_size);
547 if (!skip_streams) {
548 SIZE_CHECK("STREAM_INFO", stream_info, streams[0].alloc_size);
549 SIZE_CHECK("STREAM_INFORMATION", stream_info, streams[0].alloc_size);
552 #define ATTRIB_CHECK(sname, stype, tfield) do { \
553 s1 = fnum_find(sname); \
554 if (s1 && s1->stype.out.tfield != correct_attrib) { \
555 printf("(%d) handle %s/%s incorrect - 0x%x should be 0x%x\n", __LINE__, #stype, #tfield, \
556 (uint_t)s1->stype.out.tfield, \
557 (uint_t)correct_attrib); \
558 ret = false; \
560 s1 = fname_find(is_ipc, sname); \
561 if (s1 && s1->stype.out.tfield != correct_attrib) { \
562 printf("(%d) path %s/%s incorrect - 0x%x should be 0x%x\n", __LINE__, #stype, #tfield, \
563 (uint_t)s1->stype.out.tfield, \
564 (uint_t)correct_attrib); \
565 ret = false; \
566 }} while (0)
568 s1 = fnum_find("BASIC_INFO");
569 correct_attrib = s1->basic_info.out.attrib;
570 torture_comment(torture, "attrib: 0x%x\n", (uint_t)correct_attrib);
572 ATTRIB_CHECK("GETATTR", getattr, attrib);
573 if (!is_ipc) {
574 ATTRIB_CHECK("GETATTRE", getattre, attrib);
575 ATTRIB_CHECK("STANDARD", standard, attrib);
576 ATTRIB_CHECK("EA_SIZE", ea_size, attrib);
578 ATTRIB_CHECK("BASIC_INFO", basic_info, attrib);
579 ATTRIB_CHECK("BASIC_INFORMATION", basic_info, attrib);
580 ATTRIB_CHECK("ALL_INFO", all_info, attrib);
581 ATTRIB_CHECK("ALL_INFORMATION", all_info, attrib);
582 ATTRIB_CHECK("NETWORK_OPEN_INFORMATION", network_open_information, attrib);
583 ATTRIB_CHECK("ATTRIBUTE_TAG_INFORMATION", attribute_tag_information, attrib);
585 correct_name = fname;
586 torture_comment(torture, "name: %s\n", correct_name);
588 #define NAME_CHECK(sname, stype, tfield, flags) do { \
589 s1 = fnum_find(sname); \
590 if (s1 && (strcmp_safe(s1->stype.out.tfield.s, correct_name) != 0 || \
591 wire_bad_flags(&s1->stype.out.tfield, flags, tree->session->transport))) { \
592 printf("(%d) handle %s/%s incorrect - '%s/%d'\n", __LINE__, #stype, #tfield, \
593 s1->stype.out.tfield.s, s1->stype.out.tfield.private_length); \
594 ret = false; \
596 s1 = fname_find(is_ipc, sname); \
597 if (s1 && (strcmp_safe(s1->stype.out.tfield.s, correct_name) != 0 || \
598 wire_bad_flags(&s1->stype.out.tfield, flags, tree->session->transport))) { \
599 printf("(%d) path %s/%s incorrect - '%s/%d'\n", __LINE__, #stype, #tfield, \
600 s1->stype.out.tfield.s, s1->stype.out.tfield.private_length); \
601 ret = false; \
602 }} while (0)
604 NAME_CHECK("NAME_INFO", name_info, fname, STR_UNICODE);
605 NAME_CHECK("NAME_INFORMATION", name_info, fname, STR_UNICODE);
607 /* the ALL_INFO file name is the full path on the filesystem */
608 s1 = fnum_find("ALL_INFO");
609 if (s1 && !s1->all_info.out.fname.s) {
610 torture_fail(torture, "ALL_INFO didn't give a filename");
612 if (s1 && s1->all_info.out.fname.s) {
613 char *p = strrchr(s1->all_info.out.fname.s, '\\');
614 if (!p) {
615 printf("Not a full path in all_info/fname? - '%s'\n",
616 s1->all_info.out.fname.s);
617 ret = false;
618 } else {
619 if (strcmp_safe(correct_name, p) != 0) {
620 printf("incorrect basename in all_info/fname - '%s'\n",
621 s1->all_info.out.fname.s);
622 ret = false;
625 if (wire_bad_flags(&s1->all_info.out.fname, STR_UNICODE, tree->session->transport)) {
626 printf("Should not null terminate all_info/fname\n");
627 ret = false;
631 s1 = fnum_find("ALT_NAME_INFO");
632 if (s1) {
633 correct_name = s1->alt_name_info.out.fname.s;
634 torture_comment(torture, "alt_name: %s\n", correct_name);
636 NAME_CHECK("ALT_NAME_INFO", alt_name_info, fname, STR_UNICODE);
637 NAME_CHECK("ALT_NAME_INFORMATION", alt_name_info, fname, STR_UNICODE);
639 /* and make sure we can open by alternate name */
640 smbcli_close(tree, fnum);
641 fnum = smbcli_nt_create_full(tree, correct_name, 0,
642 SEC_RIGHTS_FILE_ALL,
643 FILE_ATTRIBUTE_NORMAL,
644 NTCREATEX_SHARE_ACCESS_DELETE|
645 NTCREATEX_SHARE_ACCESS_READ|
646 NTCREATEX_SHARE_ACCESS_WRITE,
647 NTCREATEX_DISP_OVERWRITE_IF,
648 0, 0);
649 if (fnum == -1) {
650 printf("Unable to open by alt_name - %s\n", smbcli_errstr(tree));
651 ret = false;
654 if (!skip_streams) {
655 correct_name = "::$DATA";
656 torture_comment(torture, "stream_name: %s\n", correct_name);
658 NAME_CHECK("STREAM_INFO", stream_info, streams[0].stream_name, STR_UNICODE);
659 NAME_CHECK("STREAM_INFORMATION", stream_info, streams[0].stream_name, STR_UNICODE);
663 /* make sure the EAs look right */
664 s1 = fnum_find("ALL_EAS");
665 s2 = fnum_find("ALL_INFO");
666 if (s1) {
667 for (i=0;i<s1->all_eas.out.num_eas;i++) {
668 printf(" flags=%d %s=%*.*s\n",
669 s1->all_eas.out.eas[i].flags,
670 s1->all_eas.out.eas[i].name.s,
671 (int)s1->all_eas.out.eas[i].value.length,
672 (int)s1->all_eas.out.eas[i].value.length,
673 s1->all_eas.out.eas[i].value.data);
676 if (s1 && s2) {
677 if (s1->all_eas.out.num_eas == 0) {
678 if (s2->all_info.out.ea_size != 0) {
679 printf("ERROR: num_eas==0 but fnum all_info.out.ea_size == %d\n",
680 s2->all_info.out.ea_size);
682 } else {
683 if (s2->all_info.out.ea_size !=
684 ea_list_size(s1->all_eas.out.num_eas, s1->all_eas.out.eas)) {
685 printf("ERROR: ea_list_size=%d != fnum all_info.out.ea_size=%d\n",
686 (int)ea_list_size(s1->all_eas.out.num_eas, s1->all_eas.out.eas),
687 (int)s2->all_info.out.ea_size);
691 s2 = fname_find(is_ipc, "ALL_EAS");
692 if (s2) {
693 VAL_EQUAL(all_eas, num_eas, all_eas, num_eas);
694 for (i=0;i<s1->all_eas.out.num_eas;i++) {
695 VAL_EQUAL(all_eas, eas[i].flags, all_eas, eas[i].flags);
696 STR_EQUAL(all_eas, eas[i].name, all_eas, eas[i].name);
697 VAL_EQUAL(all_eas, eas[i].value.length, all_eas, eas[i].value.length);
701 #define VAL_CHECK(sname1, stype1, tfield1, sname2, stype2, tfield2) do { \
702 s1 = fnum_find(sname1); s2 = fnum_find(sname2); \
703 if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \
704 printf("(%d) handle %s/%s != %s/%s - 0x%x vs 0x%x\n", __LINE__, \
705 #stype1, #tfield1, #stype2, #tfield2, \
706 s1->stype1.out.tfield1, s2->stype2.out.tfield2); \
707 ret = false; \
709 s1 = fname_find(is_ipc, sname1); s2 = fname_find(is_ipc, sname2); \
710 if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \
711 printf("(%d) path %s/%s != %s/%s - 0x%x vs 0x%x\n", __LINE__, \
712 #stype1, #tfield1, #stype2, #tfield2, \
713 s1->stype1.out.tfield1, s2->stype2.out.tfield2); \
714 ret = false; \
716 s1 = fnum_find(sname1); s2 = fname_find(is_ipc, sname2); \
717 if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \
718 printf("(%d) handle %s/%s != path %s/%s - 0x%x vs 0x%x\n", __LINE__, \
719 #stype1, #tfield1, #stype2, #tfield2, \
720 s1->stype1.out.tfield1, s2->stype2.out.tfield2); \
721 ret = false; \
723 s1 = fname_find(is_ipc, sname1); s2 = fnum_find(sname2); \
724 if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \
725 printf("(%d) path %s/%s != handle %s/%s - 0x%x vs 0x%x\n", __LINE__, \
726 #stype1, #tfield1, #stype2, #tfield2, \
727 s1->stype1.out.tfield1, s2->stype2.out.tfield2); \
728 ret = false; \
729 }} while (0)
731 VAL_CHECK("STANDARD_INFO", standard_info, delete_pending,
732 "ALL_INFO", all_info, delete_pending);
733 VAL_CHECK("STANDARD_INFO", standard_info, directory,
734 "ALL_INFO", all_info, directory);
735 VAL_CHECK("STANDARD_INFO", standard_info, nlink,
736 "ALL_INFO", all_info, nlink);
737 s1 = fnum_find("BASIC_INFO");
738 if (s1 && is_ipc) {
739 if (s1->basic_info.out.attrib != FILE_ATTRIBUTE_NORMAL) {
740 printf("(%d) attrib basic_info/nlink incorrect - %d should be %d\n", __LINE__, s1->basic_info.out.attrib, FILE_ATTRIBUTE_NORMAL);
741 ret = false;
744 s1 = fnum_find("STANDARD_INFO");
745 if (s1 && is_ipc) {
746 if (s1->standard_info.out.nlink != 1) {
747 printf("(%d) nlinks standard_info/nlink incorrect - %d should be 1\n", __LINE__, s1->standard_info.out.nlink);
748 ret = false;
750 if (s1->standard_info.out.delete_pending != 1) {
751 printf("(%d) nlinks standard_info/delete_pending incorrect - %d should be 1\n", __LINE__, s1->standard_info.out.delete_pending);
752 ret = false;
755 VAL_CHECK("EA_INFO", ea_info, ea_size,
756 "ALL_INFO", all_info, ea_size);
757 if (!is_ipc) {
758 VAL_CHECK("EA_SIZE", ea_size, ea_size,
759 "ALL_INFO", all_info, ea_size);
762 #define NAME_PATH_CHECK(sname, stype, field) do { \
763 s1 = fname_find(is_ipc, sname); s2 = fnum_find(sname); \
764 if (s1 && s2) { \
765 VAL_EQUAL(stype, field, stype, field); \
767 } while (0)
770 s1 = fnum_find("INTERNAL_INFORMATION");
771 if (s1) {
772 torture_comment(torture, "file_id=%.0f\n", (double)s1->internal_information.out.file_id);
775 NAME_PATH_CHECK("INTERNAL_INFORMATION", internal_information, file_id);
776 NAME_PATH_CHECK("POSITION_INFORMATION", position_information, position);
777 if (s1 && s2) {
778 printf("fnum pos = %.0f, fname pos = %.0f\n",
779 (double)s2->position_information.out.position,
780 (double)s1->position_information.out.position );
782 NAME_PATH_CHECK("MODE_INFORMATION", mode_information, mode);
783 NAME_PATH_CHECK("ALIGNMENT_INFORMATION", alignment_information, alignment_requirement);
784 NAME_PATH_CHECK("ATTRIBUTE_TAG_INFORMATION", attribute_tag_information, attrib);
785 NAME_PATH_CHECK("ATTRIBUTE_TAG_INFORMATION", attribute_tag_information, reparse_tag);
787 #if 0
788 /* these are expected to differ */
789 NAME_PATH_CHECK("ACCESS_INFORMATION", access_information, access_flags);
790 #endif
792 #if 0 /* unused */
793 #define UNKNOWN_CHECK(sname, stype, tfield) do { \
794 s1 = fnum_find(sname); \
795 if (s1 && s1->stype.out.tfield != 0) { \
796 printf("(%d) handle %s/%s unknown != 0 (0x%x)\n", __LINE__, \
797 #stype, #tfield, \
798 (uint_t)s1->stype.out.tfield); \
800 s1 = fname_find(is_ipc, sname); \
801 if (s1 && s1->stype.out.tfield != 0) { \
802 printf("(%d) path %s/%s unknown != 0 (0x%x)\n", __LINE__, \
803 #stype, #tfield, \
804 (uint_t)s1->stype.out.tfield); \
805 }} while (0)
806 #endif
807 /* now get a bit fancier .... */
809 /* when we set the delete disposition then the link count should drop
810 to 0 and delete_pending should be 1 */
812 return ret;
815 /* basic testing of all RAW_FILEINFO_* calls
816 for each call we test that it succeeds, and where possible test
817 for consistency between the calls.
819 bool torture_raw_qfileinfo(struct torture_context *torture,
820 struct smbcli_state *cli)
822 int fnum;
823 bool ret;
824 const char *fname = "\\torture_qfileinfo.txt";
826 fnum = create_complex_file(cli, torture, fname);
827 if (fnum == -1) {
828 printf("ERROR: open of %s failed (%s)\n", fname, smbcli_errstr(cli->tree));
829 return false;
832 ret = torture_raw_qfileinfo_internals(torture, torture, cli->tree, fnum, fname, false /* is_ipc */);
834 smbcli_close(cli->tree, fnum);
835 smbcli_unlink(cli->tree, fname);
837 return ret;
840 bool torture_raw_qfileinfo_pipe(struct torture_context *torture,
841 struct smbcli_state *cli)
843 bool ret = true;
844 int fnum;
845 const char *fname = "\\lsass";
846 struct dcerpc_pipe *p;
847 struct smbcli_tree *ipc_tree;
848 NTSTATUS status;
850 if (!(p = dcerpc_pipe_init(torture, cli->tree->session->transport->socket->event.ctx,
851 lp_iconv_convenience(torture->lp_ctx)))) {
852 return false;
855 status = dcerpc_pipe_open_smb(p, cli->tree, fname);
856 torture_assert_ntstatus_ok(torture, status, "dcerpc_pipe_open_smb failed");
858 ipc_tree = dcerpc_smb_tree(p->conn);
859 fnum = dcerpc_smb_fnum(p->conn);
861 ret = torture_raw_qfileinfo_internals(torture, torture, ipc_tree, fnum, fname, true /* is_ipc */);
863 talloc_free(p);
864 return ret;