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