57a2ac69c04c45dc7e30de6722b42d819f7eb6a6
[samba.git] / source4 / torture / raw / qfileinfo.c
1 /* 
2    Unix SMB/CIFS implementation.
3    RAW_FILEINFO_* individual test suite
4    Copyright (C) Andrew Tridgell 2003
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include "includes.h"
22 #include "torture/torture.h"
23 #include "libcli/raw/libcliraw.h"
24
25 static struct {
26         const char *name;
27         enum smb_fileinfo_level level;
28         uint_t only_paths:1;
29         uint_t only_handles:1;
30         uint32_t capability_mask;
31         NTSTATUS fnum_status, fname_status;
32         union smb_fileinfo fnum_finfo, fname_finfo;
33 } levels[] = {
34         { "GETATTR",                   RAW_FILEINFO_GETATTR,           1, 0, },
35         { "GETATTRE",                  RAW_FILEINFO_GETATTRE,          0, 1, },
36         { "STANDARD",                  RAW_FILEINFO_STANDARD, },
37         { "EA_SIZE",                   RAW_FILEINFO_EA_SIZE, },
38         { "ALL_EAS",                   RAW_FILEINFO_ALL_EAS, },
39         { "IS_NAME_VALID",             RAW_FILEINFO_IS_NAME_VALID,     1, 0, },
40         { "BASIC_INFO",                RAW_FILEINFO_BASIC_INFO, },
41         { "STANDARD_INFO",             RAW_FILEINFO_STANDARD_INFO, },
42         { "EA_INFO",                   RAW_FILEINFO_EA_INFO, },
43         { "NAME_INFO",                 RAW_FILEINFO_NAME_INFO, },
44         { "ALL_INFO",                  RAW_FILEINFO_ALL_INFO, },
45         { "ALT_NAME_INFO",             RAW_FILEINFO_ALT_NAME_INFO, },
46         { "STREAM_INFO",               RAW_FILEINFO_STREAM_INFO, },
47         { "COMPRESSION_INFO",          RAW_FILEINFO_COMPRESSION_INFO, },
48         { "UNIX_BASIC_INFO",           RAW_FILEINFO_UNIX_BASIC, 0, 0, CAP_UNIX},
49         { "UNIX_LINK_INFO",            RAW_FILEINFO_UNIX_LINK, 0, 0, CAP_UNIX},
50         { "BASIC_INFORMATION",         RAW_FILEINFO_BASIC_INFORMATION, },
51         { "STANDARD_INFORMATION",      RAW_FILEINFO_STANDARD_INFORMATION, },
52         { "INTERNAL_INFORMATION",      RAW_FILEINFO_INTERNAL_INFORMATION, },
53         { "EA_INFORMATION",            RAW_FILEINFO_EA_INFORMATION, },
54         { "ACCESS_INFORMATION",        RAW_FILEINFO_ACCESS_INFORMATION, },
55         { "NAME_INFORMATION",          RAW_FILEINFO_NAME_INFORMATION, },
56         { "POSITION_INFORMATION",      RAW_FILEINFO_POSITION_INFORMATION, },
57         { "MODE_INFORMATION",          RAW_FILEINFO_MODE_INFORMATION, },
58         { "ALIGNMENT_INFORMATION",     RAW_FILEINFO_ALIGNMENT_INFORMATION, },
59         { "ALL_INFORMATION",           RAW_FILEINFO_ALL_INFORMATION, },
60         { "ALT_NAME_INFORMATION",      RAW_FILEINFO_ALT_NAME_INFORMATION, },
61         { "STREAM_INFORMATION",        RAW_FILEINFO_STREAM_INFORMATION, },
62         { "COMPRESSION_INFORMATION",   RAW_FILEINFO_COMPRESSION_INFORMATION, },
63         { "NETWORK_OPEN_INFORMATION",  RAW_FILEINFO_NETWORK_OPEN_INFORMATION, },
64         { "ATTRIBUTE_TAG_INFORMATION", RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION, },
65         { NULL, }
66 };
67
68 /*
69   compare a dos time (2 second resolution) to a nt time
70 */
71 static int dos_nt_time_cmp(time_t t, NTTIME nt)
72 {
73         time_t t2 = nt_time_to_unix(nt);
74         if (ABS(t2 - t) <= 2) return 0;
75         return t2 - t;
76 }
77
78
79 /*
80   find a level in the levels[] table
81 */
82 static union smb_fileinfo *fnum_find(const char *name)
83 {
84         int i;
85         for (i=0; levels[i].name; i++) {
86                 if (NT_STATUS_IS_OK(levels[i].fnum_status) &&
87                     strcmp(name, levels[i].name) == 0 && 
88                     !levels[i].only_paths) {
89                         return &levels[i].fnum_finfo;
90                 }
91         }
92         return NULL;
93 }
94
95 /*
96   find a level in the levels[] table
97 */
98 static union smb_fileinfo *fname_find(const char *name)
99 {
100         int i;
101         for (i=0; levels[i].name; i++) {
102                 if (NT_STATUS_IS_OK(levels[i].fname_status) &&
103                     strcmp(name, levels[i].name) == 0 && 
104                     !levels[i].only_handles) {
105                         return &levels[i].fname_finfo;
106                 }
107         }
108         return NULL;
109 }
110
111 /* local macros to make the code below more readable */
112 #define VAL_EQUAL(n1, v1, n2, v2) do {if (s1->n1.out.v1 != s2->n2.out.v2) { \
113         printf("%s/%s [%u] != %s/%s [%u] at %s(%d)\n", \
114                #n1, #v1, (uint_t)s1->n1.out.v1, \
115                #n2, #v2, (uint_t)s2->n2.out.v2, \
116                __FILE__, __LINE__); \
117         ret = False; \
118 }} while(0)
119
120 #define STR_EQUAL(n1, v1, n2, v2) do {if (strcmp_safe(s1->n1.out.v1.s, s2->n2.out.v2.s) || \
121                                           s1->n1.out.v1.private_length != s2->n2.out.v2.private_length) { \
122         printf("%s/%s [%s/%d] != %s/%s [%s/%d] at %s(%d)\n", \
123                #n1, #v1, s1->n1.out.v1.s, s1->n1.out.v1.private_length, \
124                #n2, #v2, s2->n2.out.v2.s, s2->n2.out.v2.private_length, \
125                __FILE__, __LINE__); \
126         ret = False; \
127 }} while(0)
128
129 #define STRUCT_EQUAL(n1, v1, n2, v2) do {if (memcmp(&s1->n1.out.v1,&s2->n2.out.v2,sizeof(s1->n1.out.v1))) { \
130         printf("%s/%s != %s/%s at %s(%d)\n", \
131                #n1, #v1, \
132                #n2, #v2, \
133                __FILE__, __LINE__); \
134         ret = False; \
135 }} while(0)
136
137 /* used to find hints on unknown values - and to make sure 
138    we zero-fill */
139 #define VAL_UNKNOWN(n1, v1) do {if (s1->n1.out.v1 != 0) { \
140         printf("%s/%s non-zero unknown - %u (0x%x) at %s(%d)\n", \
141                #n1, #v1, \
142                (uint_t)s1->n1.out.v1, \
143                (uint_t)s1->n1.out.v1, \
144                __FILE__, __LINE__); \
145         ret = False; \
146 }} while(0)
147
148 /* basic testing of all RAW_FILEINFO_* calls 
149    for each call we test that it succeeds, and where possible test 
150    for consistency between the calls. 
151 */
152 BOOL torture_raw_qfileinfo(void)
153 {
154         struct smbcli_state *cli;
155         int i;
156         BOOL ret = True;
157         int count;
158         union smb_fileinfo *s1, *s2;    
159         TALLOC_CTX *mem_ctx;
160         int fnum;
161         const char *fname = "\\torture_qfileinfo.txt";
162         NTTIME correct_time;
163         uint64_t correct_size;
164         uint32_t correct_attrib;
165         const char *correct_name;
166         BOOL skip_streams = False;
167
168         if (!torture_open_connection(&cli)) {
169                 return False;
170         }
171
172         mem_ctx = talloc_init("torture_qfileinfo");
173
174         fnum = create_complex_file(cli, mem_ctx, fname);
175         if (fnum == -1) {
176                 printf("ERROR: open of %s failed (%s)\n", fname, smbcli_errstr(cli->tree));
177                 ret = False;
178                 goto done;
179         }
180         
181         
182         /* scan all the fileinfo and pathinfo levels */
183         for (i=0; levels[i].name; i++) {
184                 if (!levels[i].only_paths) {
185                         levels[i].fnum_finfo.generic.level = levels[i].level;
186                         levels[i].fnum_finfo.generic.in.fnum = fnum;
187                         levels[i].fnum_status = smb_raw_fileinfo(cli->tree, mem_ctx, 
188                                                                  &levels[i].fnum_finfo);
189                 }
190
191                 if (!levels[i].only_handles) {
192                         levels[i].fname_finfo.generic.level = levels[i].level;
193                         levels[i].fname_finfo.generic.in.fname = talloc_strdup(mem_ctx, fname);
194                         levels[i].fname_status = smb_raw_pathinfo(cli->tree, mem_ctx, 
195                                                                   &levels[i].fname_finfo);
196                 }
197         }
198
199         /* check for completely broken levels */
200         for (count=i=0; levels[i].name; i++) {
201                 uint32_t cap = cli->transport->negotiate.capabilities;
202                 /* see if this server claims to support this level */
203                 if ((cap & levels[i].capability_mask) != levels[i].capability_mask) {
204                         continue;
205                 }
206
207                 if (!levels[i].only_paths && !NT_STATUS_IS_OK(levels[i].fnum_status)) {
208                         printf("ERROR: level %s failed - %s\n", 
209                                levels[i].name, nt_errstr(levels[i].fnum_status));
210                         count++;
211                 }
212                 if (!levels[i].only_handles && !NT_STATUS_IS_OK(levels[i].fname_status)) {
213                         printf("ERROR: level %s failed - %s\n", 
214                                levels[i].name, nt_errstr(levels[i].fname_status));
215                         count++;
216                 }
217         }
218
219         if (count != 0) {
220                 ret = False;
221                 printf("%d levels failed\n", count);
222                 if (count > 35) {
223                         printf("too many level failures - giving up\n");
224                         goto done;
225                 }
226         }
227
228         /* see if we can do streams */
229         s1 = fnum_find("STREAM_INFO");
230         if (!s1 || s1->stream_info.out.num_streams == 0) {
231                 printf("STREAM_INFO broken (%d) - skipping streams checks\n",
232                        s1 ? s1->stream_info.out.num_streams : -1);
233                 skip_streams = True;
234         }       
235
236
237         /* this code is incredibly repititive but doesn't lend itself to loops, so
238            we use lots of macros to make it less painful */
239
240         /* first off we check the levels that are supposed to be aliases. It will be quite rare for 
241            this code to fail, but we need to check it for completeness */
242
243
244
245 #define ALIAS_CHECK(sname1, sname2) \
246         do { \
247                 s1 = fnum_find(sname1);  s2 = fnum_find(sname2); \
248                 if (s1 && s2) { INFO_CHECK } \
249                 s1 = fname_find(sname1); s2 = fname_find(sname2); \
250                 if (s1 && s2) { INFO_CHECK } \
251                 s1 = fnum_find(sname1);  s2 = fname_find(sname2); \
252                 if (s1 && s2) { INFO_CHECK } \
253         } while (0)
254
255 #define INFO_CHECK \
256                 STRUCT_EQUAL(basic_info,  create_time, basic_info, create_time); \
257                 STRUCT_EQUAL(basic_info,  access_time, basic_info, access_time); \
258                 STRUCT_EQUAL(basic_info,  write_time,  basic_info, write_time); \
259                 STRUCT_EQUAL(basic_info,  change_time, basic_info, change_time); \
260                 VAL_EQUAL   (basic_info,  attrib,      basic_info, attrib);
261
262         ALIAS_CHECK("BASIC_INFO", "BASIC_INFORMATION");
263
264 #undef INFO_CHECK
265 #define INFO_CHECK \
266                 VAL_EQUAL(standard_info,  alloc_size,     standard_info, alloc_size); \
267                 VAL_EQUAL(standard_info,  size,           standard_info, size); \
268                 VAL_EQUAL(standard_info,  nlink,          standard_info, nlink); \
269                 VAL_EQUAL(standard_info,  delete_pending, standard_info, delete_pending); \
270                 VAL_EQUAL(standard_info,  directory,      standard_info, directory);
271
272         ALIAS_CHECK("STANDARD_INFO", "STANDARD_INFORMATION");
273
274 #undef INFO_CHECK
275 #define INFO_CHECK \
276                 VAL_EQUAL(ea_info,  ea_size,     ea_info, ea_size);
277
278         ALIAS_CHECK("EA_INFO", "EA_INFORMATION");
279
280 #undef INFO_CHECK
281 #define INFO_CHECK \
282                 STR_EQUAL(name_info,  fname,   name_info, fname);
283
284         ALIAS_CHECK("NAME_INFO", "NAME_INFORMATION");
285
286 #undef INFO_CHECK
287 #define INFO_CHECK \
288                 STRUCT_EQUAL(all_info,  create_time,           all_info, create_time); \
289                 STRUCT_EQUAL(all_info,  access_time,           all_info, access_time); \
290                 STRUCT_EQUAL(all_info,  write_time,            all_info, write_time); \
291                 STRUCT_EQUAL(all_info,  change_time,           all_info, change_time); \
292                    VAL_EQUAL(all_info,  attrib,                all_info, attrib); \
293                    VAL_EQUAL(all_info,  alloc_size,            all_info, alloc_size); \
294                    VAL_EQUAL(all_info,  size,                  all_info, size); \
295                    VAL_EQUAL(all_info,  nlink,                 all_info, nlink); \
296                    VAL_EQUAL(all_info,  delete_pending,        all_info, delete_pending); \
297                    VAL_EQUAL(all_info,  directory,             all_info, directory); \
298                    VAL_EQUAL(all_info,  ea_size,               all_info, ea_size); \
299                    STR_EQUAL(all_info,  fname,                 all_info, fname);
300
301         ALIAS_CHECK("ALL_INFO", "ALL_INFORMATION");
302
303 #undef INFO_CHECK
304 #define INFO_CHECK \
305                 VAL_EQUAL(compression_info, compressed_size,compression_info, compressed_size); \
306                 VAL_EQUAL(compression_info, format,         compression_info, format); \
307                 VAL_EQUAL(compression_info, unit_shift,     compression_info, unit_shift); \
308                 VAL_EQUAL(compression_info, chunk_shift,    compression_info, chunk_shift); \
309                 VAL_EQUAL(compression_info, cluster_shift,  compression_info, cluster_shift);
310
311         ALIAS_CHECK("COMPRESSION_INFO", "COMPRESSION_INFORMATION");
312
313
314 #undef INFO_CHECK
315 #define INFO_CHECK \
316                 STR_EQUAL(alt_name_info,  fname,   alt_name_info, fname);
317
318         ALIAS_CHECK("ALT_NAME_INFO", "ALT_NAME_INFORMATION");
319
320
321 #define TIME_CHECK_NT(sname, stype, tfield) do { \
322         s1 = fnum_find(sname); \
323         if (s1 && memcmp(&s1->stype.out.tfield, &correct_time, sizeof(correct_time)) != 0) { \
324                 printf("(%d) handle %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield,  \
325                        nt_time_string(mem_ctx, s1->stype.out.tfield), \
326                        nt_time_string(mem_ctx, correct_time)); \
327                 ret = False; \
328         } \
329         s1 = fname_find(sname); \
330         if (s1 && memcmp(&s1->stype.out.tfield, &correct_time, sizeof(correct_time)) != 0) { \
331                 printf("(%d) path %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield,  \
332                        nt_time_string(mem_ctx, s1->stype.out.tfield), \
333                        nt_time_string(mem_ctx, correct_time)); \
334                 ret = False; \
335         }} while (0)
336
337 #define TIME_CHECK_DOS(sname, stype, tfield) do { \
338         s1 = fnum_find(sname); \
339         if (s1 && dos_nt_time_cmp(s1->stype.out.tfield, correct_time) != 0) { \
340                 printf("(%d) handle %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield,  \
341                        timestring(mem_ctx, s1->stype.out.tfield), \
342                        nt_time_string(mem_ctx, correct_time)); \
343                 ret = False; \
344         } \
345         s1 = fname_find(sname); \
346         if (s1 && dos_nt_time_cmp(s1->stype.out.tfield, correct_time) != 0) { \
347                 printf("(%d) path %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield,  \
348                        timestring(mem_ctx, s1->stype.out.tfield), \
349                        nt_time_string(mem_ctx, correct_time)); \
350                 ret = False; \
351         }} while (0)
352
353 #define TIME_CHECK_UNX(sname, stype, tfield) do { \
354         s1 = fnum_find(sname); \
355         if (s1 && unx_nt_time_cmp(s1->stype.out.tfield, correct_time) != 0) { \
356                 printf("(%d) handle %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield,  \
357                        timestring(mem_ctx, s1->stype.out.tfield), \
358                        nt_time_string(mem_ctx, correct_time)); \
359                 ret = False; \
360         } \
361         s1 = fname_find(sname); \
362         if (s1 && unx_nt_time_cmp(s1->stype.out.tfield, correct_time) != 0) { \
363                 printf("(%d) path %s/%s incorrect - %s should be %s\n", __LINE__, #stype, #tfield,  \
364                        timestring(mem_ctx, s1->stype.out.tfield), \
365                        nt_time_string(mem_ctx, correct_time)); \
366                 ret = False; \
367         }} while (0)
368
369         /* now check that all the times that are supposed to be equal are correct */
370         s1 = fnum_find("BASIC_INFO");
371         correct_time = s1->basic_info.out.create_time;
372         printf("create_time: %s\n", nt_time_string(mem_ctx, correct_time));
373
374         TIME_CHECK_NT ("BASIC_INFO",               basic_info, create_time);
375         TIME_CHECK_NT ("BASIC_INFORMATION",        basic_info, create_time);
376         TIME_CHECK_DOS("GETATTRE",                 getattre,   create_time);
377         TIME_CHECK_DOS("STANDARD",                 standard,   create_time);
378         TIME_CHECK_DOS("EA_SIZE",                  ea_size,    create_time);
379         TIME_CHECK_NT ("ALL_INFO",                 all_info,   create_time);
380         TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information, create_time);
381
382         s1 = fnum_find("BASIC_INFO");
383         correct_time = s1->basic_info.out.access_time;
384         printf("access_time: %s\n", nt_time_string(mem_ctx, correct_time));
385
386         TIME_CHECK_NT ("BASIC_INFO",               basic_info, access_time);
387         TIME_CHECK_NT ("BASIC_INFORMATION",        basic_info, access_time);
388         TIME_CHECK_DOS("GETATTRE",                 getattre,   access_time);
389         TIME_CHECK_DOS("STANDARD",                 standard,   access_time);
390         TIME_CHECK_DOS("EA_SIZE",                  ea_size,    access_time);
391         TIME_CHECK_NT ("ALL_INFO",                 all_info,   access_time);
392         TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information, access_time);
393
394         s1 = fnum_find("BASIC_INFO");
395         correct_time = s1->basic_info.out.write_time;
396         printf("write_time : %s\n", nt_time_string(mem_ctx, correct_time));
397
398         TIME_CHECK_NT ("BASIC_INFO",               basic_info, write_time);
399         TIME_CHECK_NT ("BASIC_INFORMATION",        basic_info, write_time);
400         TIME_CHECK_DOS("GETATTR",                  getattr,    write_time);
401         TIME_CHECK_DOS("GETATTRE",                 getattre,   write_time);
402         TIME_CHECK_DOS("STANDARD",                 standard,   write_time);
403         TIME_CHECK_DOS("EA_SIZE",                  ea_size,    write_time);
404         TIME_CHECK_NT ("ALL_INFO",                 all_info,   write_time);
405         TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information, write_time);
406
407         s1 = fnum_find("BASIC_INFO");
408         correct_time = s1->basic_info.out.change_time;
409         printf("change_time: %s\n", nt_time_string(mem_ctx, correct_time));
410
411         TIME_CHECK_NT ("BASIC_INFO",               basic_info, change_time);
412         TIME_CHECK_NT ("BASIC_INFORMATION",        basic_info, change_time);
413         TIME_CHECK_NT ("ALL_INFO",                 all_info,   change_time);
414         TIME_CHECK_NT ("NETWORK_OPEN_INFORMATION", network_open_information, change_time);
415
416
417 #define SIZE_CHECK(sname, stype, tfield) do { \
418         s1 = fnum_find(sname); \
419         if (s1 && s1->stype.out.tfield != correct_size) { \
420                 printf("(%d) handle %s/%s incorrect - %u should be %u\n", __LINE__, #stype, #tfield,  \
421                        (uint_t)s1->stype.out.tfield, \
422                        (uint_t)correct_size); \
423                 ret = False; \
424         } \
425         s1 = fname_find(sname); \
426         if (s1 && s1->stype.out.tfield != correct_size) { \
427                 printf("(%d) path %s/%s incorrect - %u should be %u\n", __LINE__, #stype, #tfield,  \
428                        (uint_t)s1->stype.out.tfield, \
429                        (uint_t)correct_size); \
430                 ret = False; \
431         }} while (0)
432
433         s1 = fnum_find("STANDARD_INFO");
434         correct_size = s1->standard_info.out.size;
435         printf("size: %u\n", (uint_t)correct_size);
436         
437         SIZE_CHECK("GETATTR",                  getattr,                  size);
438         SIZE_CHECK("GETATTRE",                 getattre,                 size);
439         SIZE_CHECK("STANDARD",                 standard,                 size);
440         SIZE_CHECK("EA_SIZE",                  ea_size,                  size);
441         SIZE_CHECK("STANDARD_INFO",            standard_info,            size);
442         SIZE_CHECK("STANDARD_INFORMATION",     standard_info,            size);
443         SIZE_CHECK("ALL_INFO",                 all_info,                 size);
444         SIZE_CHECK("ALL_INFORMATION",          all_info,                 size);
445         SIZE_CHECK("COMPRESSION_INFO",         compression_info,         compressed_size);
446         SIZE_CHECK("COMPRESSION_INFORMATION",  compression_info,         compressed_size);
447         SIZE_CHECK("NETWORK_OPEN_INFORMATION", network_open_information, size);
448         if (!skip_streams) {
449                 SIZE_CHECK("STREAM_INFO",              stream_info,   streams[0].size);
450                 SIZE_CHECK("STREAM_INFORMATION",       stream_info,   streams[0].size);
451         }
452
453
454         s1 = fnum_find("STANDARD_INFO");
455         correct_size = s1->standard_info.out.alloc_size;
456         printf("alloc_size: %u\n", (uint_t)correct_size);
457         
458         SIZE_CHECK("GETATTRE",                 getattre,                 alloc_size);
459         SIZE_CHECK("STANDARD",                 standard,                 alloc_size);
460         SIZE_CHECK("EA_SIZE",                  ea_size,                  alloc_size);
461         SIZE_CHECK("STANDARD_INFO",            standard_info,            alloc_size);
462         SIZE_CHECK("STANDARD_INFORMATION",     standard_info,            alloc_size);
463         SIZE_CHECK("ALL_INFO",                 all_info,                 alloc_size);
464         SIZE_CHECK("ALL_INFORMATION",          all_info,                 alloc_size);
465         SIZE_CHECK("NETWORK_OPEN_INFORMATION", network_open_information, alloc_size);
466         if (!skip_streams) {
467                 SIZE_CHECK("STREAM_INFO",              stream_info,   streams[0].alloc_size);
468                 SIZE_CHECK("STREAM_INFORMATION",       stream_info,   streams[0].alloc_size);
469         }
470
471 #define ATTRIB_CHECK(sname, stype, tfield) do { \
472         s1 = fnum_find(sname); \
473         if (s1 && s1->stype.out.tfield != correct_attrib) { \
474                 printf("(%d) handle %s/%s incorrect - 0x%x should be 0x%x\n", __LINE__, #stype, #tfield,  \
475                        (uint_t)s1->stype.out.tfield, \
476                        (uint_t)correct_attrib); \
477                 ret = False; \
478         } \
479         s1 = fname_find(sname); \
480         if (s1 && s1->stype.out.tfield != correct_attrib) { \
481                 printf("(%d) path %s/%s incorrect - 0x%x should be 0x%x\n", __LINE__, #stype, #tfield,  \
482                        (uint_t)s1->stype.out.tfield, \
483                        (uint_t)correct_attrib); \
484                 ret = False; \
485         }} while (0)
486
487         s1 = fnum_find("BASIC_INFO");
488         correct_attrib = s1->basic_info.out.attrib;
489         printf("attrib: 0x%x\n", (uint_t)correct_attrib);
490         
491         ATTRIB_CHECK("GETATTR",                   getattr,                   attrib);
492         ATTRIB_CHECK("GETATTRE",                  getattre,                  attrib);
493         ATTRIB_CHECK("STANDARD",                  standard,                  attrib);
494         ATTRIB_CHECK("BASIC_INFO",                basic_info,                attrib);
495         ATTRIB_CHECK("BASIC_INFORMATION",         basic_info,                attrib);
496         ATTRIB_CHECK("EA_SIZE",                   ea_size,                   attrib);
497         ATTRIB_CHECK("ALL_INFO",                  all_info,                  attrib);
498         ATTRIB_CHECK("ALL_INFORMATION",           all_info,                  attrib);
499         ATTRIB_CHECK("NETWORK_OPEN_INFORMATION",  network_open_information,  attrib);
500         ATTRIB_CHECK("ATTRIBUTE_TAG_INFORMATION", attribute_tag_information, attrib);
501
502         correct_name = fname;
503         printf("name: %s\n", correct_name);
504
505 #define NAME_CHECK(sname, stype, tfield, flags) do { \
506         s1 = fnum_find(sname); \
507         if (s1 && (strcmp_safe(s1->stype.out.tfield.s, correct_name) != 0 || \
508                         wire_bad_flags(&s1->stype.out.tfield, flags, cli))) { \
509                 printf("(%d) handle %s/%s incorrect - '%s/%d'\n", __LINE__, #stype, #tfield,  \
510                        s1->stype.out.tfield.s, s1->stype.out.tfield.private_length); \
511                 ret = False; \
512         } \
513         s1 = fname_find(sname); \
514         if (s1 && (strcmp_safe(s1->stype.out.tfield.s, correct_name) != 0 || \
515                         wire_bad_flags(&s1->stype.out.tfield, flags, cli))) { \
516                 printf("(%d) path %s/%s incorrect - '%s/%d'\n", __LINE__, #stype, #tfield,  \
517                        s1->stype.out.tfield.s, s1->stype.out.tfield.private_length); \
518                 ret = False; \
519         }} while (0)
520
521         NAME_CHECK("NAME_INFO",        name_info, fname, STR_UNICODE);
522         NAME_CHECK("NAME_INFORMATION", name_info, fname, STR_UNICODE);
523
524         /* the ALL_INFO file name is the full path on the filesystem */
525         s1 = fnum_find("ALL_INFO");
526         if (s1 && !s1->all_info.out.fname.s) {
527                 printf("ALL_INFO didn't give a filename\n");
528                 ret = False;
529         }
530         if (s1 && s1->all_info.out.fname.s) {
531                 char *p = strrchr(s1->all_info.out.fname.s, '\\');
532                 if (!p) {
533                         printf("Not a full path in all_info/fname? - '%s'\n", 
534                                s1->all_info.out.fname.s);
535                         ret = False;
536                 } else {
537                         if (strcmp_safe(correct_name, p) != 0) {
538                                 printf("incorrect basename in all_info/fname - '%s'\n",
539                                        s1->all_info.out.fname.s);
540                                 ret = False;
541                         }
542                 }
543                 if (wire_bad_flags(&s1->all_info.out.fname, STR_UNICODE, cli)) {
544                         printf("Should not null terminate all_info/fname\n");
545                         ret = False;
546                 }
547         }
548
549         s1 = fnum_find("ALT_NAME_INFO");
550         correct_name = s1->alt_name_info.out.fname.s;
551         printf("alt_name: %s\n", correct_name);
552
553         NAME_CHECK("ALT_NAME_INFO",        alt_name_info, fname, STR_UNICODE);
554         NAME_CHECK("ALT_NAME_INFORMATION", alt_name_info, fname, STR_UNICODE);
555
556         /* and make sure we can open by alternate name */
557         smbcli_close(cli->tree, fnum);
558         fnum = smbcli_nt_create_full(cli->tree, correct_name, 0, 
559                                      SEC_RIGHTS_FILE_ALL,
560                                      FILE_ATTRIBUTE_NORMAL,
561                                      NTCREATEX_SHARE_ACCESS_DELETE|
562                                      NTCREATEX_SHARE_ACCESS_READ|
563                                      NTCREATEX_SHARE_ACCESS_WRITE, 
564                                      NTCREATEX_DISP_OVERWRITE_IF, 
565                                      0, 0);
566         if (fnum == -1) {
567                 printf("Unable to open by alt_name - %s\n", smbcli_errstr(cli->tree));
568                 ret = False;
569         }
570
571         if (!skip_streams) {
572                 correct_name = "::$DATA";
573                 printf("stream_name: %s\n", correct_name);
574
575                 NAME_CHECK("STREAM_INFO",        stream_info, streams[0].stream_name, STR_UNICODE);
576                 NAME_CHECK("STREAM_INFORMATION", stream_info, streams[0].stream_name, STR_UNICODE);
577         }
578
579         /* make sure the EAs look right */
580         s1 = fnum_find("ALL_EAS");
581         s2 = fnum_find("ALL_INFO");
582         if (s1) {
583                 for (i=0;i<s1->all_eas.out.num_eas;i++) {
584                         printf("  flags=%d %s=%*.*s\n", 
585                                s1->all_eas.out.eas[i].flags,
586                                s1->all_eas.out.eas[i].name.s,
587                                (int)s1->all_eas.out.eas[i].value.length,
588                                (int)s1->all_eas.out.eas[i].value.length,
589                                s1->all_eas.out.eas[i].value.data);
590                 }
591         }
592         if (s1 && s2) {
593                 if (s1->all_eas.out.num_eas == 0) {
594                         if (s2->all_info.out.ea_size != 0) {
595                                 printf("ERROR: num_eas==0 but fnum all_info.out.ea_size == %d\n",
596                                        s2->all_info.out.ea_size);
597                         }
598                 } else {
599                         if (s2->all_info.out.ea_size != 
600                             ea_list_size(s1->all_eas.out.num_eas, s1->all_eas.out.eas)) {
601                                 printf("ERROR: ea_list_size=%d != fnum all_info.out.ea_size=%d\n",
602                                        ea_list_size(s1->all_eas.out.num_eas, s1->all_eas.out.eas),
603                                        s2->all_info.out.ea_size);
604                         }
605                 }
606         }
607         s2 = fname_find("ALL_EAS");
608         if (s2) {
609                 VAL_EQUAL(all_eas, num_eas, all_eas, num_eas);
610                 for (i=0;i<s1->all_eas.out.num_eas;i++) {
611                         VAL_EQUAL(all_eas, eas[i].flags, all_eas, eas[i].flags);
612                         STR_EQUAL(all_eas, eas[i].name, all_eas, eas[i].name);
613                         VAL_EQUAL(all_eas, eas[i].value.length, all_eas, eas[i].value.length);
614                 }
615         }
616
617 #define VAL_CHECK(sname1, stype1, tfield1, sname2, stype2, tfield2) do { \
618         s1 = fnum_find(sname1); s2 = fnum_find(sname2); \
619         if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \
620                 printf("(%d) handle %s/%s != %s/%s - 0x%x vs 0x%x\n", __LINE__, \
621                        #stype1, #tfield1, #stype2, #tfield2,  \
622                        s1->stype1.out.tfield1, s2->stype2.out.tfield2); \
623                 ret = False; \
624         } \
625         s1 = fname_find(sname1); s2 = fname_find(sname2); \
626         if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \
627                 printf("(%d) path %s/%s != %s/%s - 0x%x vs 0x%x\n", __LINE__, \
628                        #stype1, #tfield1, #stype2, #tfield2,  \
629                        s1->stype1.out.tfield1, s2->stype2.out.tfield2); \
630                 ret = False; \
631         } \
632         s1 = fnum_find(sname1); s2 = fname_find(sname2); \
633         if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \
634                 printf("(%d) handle %s/%s != path %s/%s - 0x%x vs 0x%x\n", __LINE__, \
635                        #stype1, #tfield1, #stype2, #tfield2,  \
636                        s1->stype1.out.tfield1, s2->stype2.out.tfield2); \
637                 ret = False; \
638         } \
639         s1 = fname_find(sname1); s2 = fnum_find(sname2); \
640         if (s1 && s2 && s1->stype1.out.tfield1 != s2->stype2.out.tfield2) { \
641                 printf("(%d) path %s/%s != handle %s/%s - 0x%x vs 0x%x\n", __LINE__, \
642                        #stype1, #tfield1, #stype2, #tfield2,  \
643                        s1->stype1.out.tfield1, s2->stype2.out.tfield2); \
644                 ret = False; \
645         }} while (0)
646
647         VAL_CHECK("STANDARD_INFO", standard_info, delete_pending, 
648                   "ALL_INFO",      all_info,      delete_pending);
649         VAL_CHECK("STANDARD_INFO", standard_info, directory, 
650                   "ALL_INFO",      all_info,      directory);
651         VAL_CHECK("STANDARD_INFO", standard_info, nlink, 
652                   "ALL_INFO",      all_info,      nlink);
653         VAL_CHECK("EA_INFO",       ea_info,       ea_size, 
654                   "ALL_INFO",      all_info,      ea_size);
655         VAL_CHECK("EA_SIZE",       ea_size,       ea_size, 
656                   "ALL_INFO",      all_info,      ea_size);
657
658
659 #define NAME_PATH_CHECK(sname, stype, field) do { \
660         s1 = fname_find(sname); s2 = fnum_find(sname); \
661         if (s1 && s2) { \
662                 VAL_EQUAL(stype, field, stype, field); \
663         } \
664 } while (0)
665
666
667         s1 = fnum_find("INTERNAL_INFORMATION");
668         if (s1) {
669                 printf("file_id=%.0f\n", (double)s1->internal_information.out.file_id);
670         }
671
672         NAME_PATH_CHECK("INTERNAL_INFORMATION", internal_information, file_id);
673         NAME_PATH_CHECK("POSITION_INFORMATION", position_information, position);
674         if (s1 && s2) {
675                 printf("fnum pos = %.0f, fname pos = %.0f\n",
676                        (double)s2->position_information.out.position,
677                        (double)s1->position_information.out.position );
678         }
679         NAME_PATH_CHECK("MODE_INFORMATION", mode_information, mode);
680         NAME_PATH_CHECK("ALIGNMENT_INFORMATION", alignment_information, alignment_requirement);
681         NAME_PATH_CHECK("ATTRIBUTE_TAG_INFORMATION", attribute_tag_information, attrib);
682         NAME_PATH_CHECK("ATTRIBUTE_TAG_INFORMATION", attribute_tag_information, reparse_tag);
683
684 #if 0
685         /* these are expected to differ */
686         NAME_PATH_CHECK("ACCESS_INFORMATION", access_information, access_flags);
687 #endif
688
689 #define UNKNOWN_CHECK(sname, stype, tfield) do { \
690         s1 = fnum_find(sname); \
691         if (s1 && s1->stype.out.tfield != 0) { \
692                 printf("(%d) handle %s/%s unknown != 0 (0x%x)\n", __LINE__, \
693                        #stype, #tfield, \
694                        (uint_t)s1->stype.out.tfield); \
695         } \
696         s1 = fname_find(sname); \
697         if (s1 && s1->stype.out.tfield != 0) { \
698                 printf("(%d) path %s/%s unknown != 0 (0x%x)\n", __LINE__, \
699                        #stype, #tfield, \
700                        (uint_t)s1->stype.out.tfield); \
701         }} while (0)
702
703         /* now get a bit fancier .... */
704         
705         /* when we set the delete disposition then the link count should drop
706            to 0 and delete_pending should be 1 */
707         
708
709 done:
710         smbcli_close(cli->tree, fnum);
711         smbcli_unlink(cli->tree, fname);
712
713         torture_close_connection(cli);
714         talloc_free(mem_ctx);
715         return ret;
716 }