Merge branch 'upstream'
[jelmer/samba4-debian.git] / source / 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/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"
31
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,
59         },
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
77         },
78         {  .name ="STREAM_INFO",           
79            .level =  RAW_FILEINFO_STREAM_INFO,
80            .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER
81         },
82         {  .name ="COMPRESSION_INFO",        
83            .level =  RAW_FILEINFO_COMPRESSION_INFO,
84            .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER
85         },
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
119         },
120         {  .name ="STREAM_INFORMATION",    
121            .level =  RAW_FILEINFO_STREAM_INFORMATION,
122            .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER
123         },
124         { .name = "COMPRESSION_INFORMATION", 
125           .level =  RAW_FILEINFO_COMPRESSION_INFORMATION,
126           .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER
127         },
128         {  .name ="NETWORK_OPEN_INFORMATION",
129            .level =  RAW_FILEINFO_NETWORK_OPEN_INFORMATION,
130            .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER
131         },
132         { .name = "ATTRIBUTE_TAG_INFORMATION",
133           .level =  RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION,
134           .expected_ipc_fnum_status = NT_STATUS_INVALID_PARAMETER
135         },
136         { NULL }
137 };
138
139 /*
140   compare a dos time (2 second resolution) to a nt time
141 */
142 static int dos_nt_time_cmp(time_t t, NTTIME nt)
143 {
144         time_t t2 = nt_time_to_unix(nt);
145         if (abs(t2 - t) <= 2) return 0;
146         return t2 - t;
147 }
148
149
150 /*
151   find a level in the levels[] table
152 */
153 static union smb_fileinfo *fnum_find(const char *name)
154 {
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;
161                 }
162         }
163         return NULL;
164 }
165
166 /*
167   find a level in the levels[] table
168 */
169 static union smb_fileinfo *fname_find(bool is_ipc, const char *name)
170 {
171         int i;
172         if (is_ipc) {
173                 return NULL;
174         }
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;
180                 }
181         }
182         return NULL;
183 }
184
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)
193
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)
202
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)
210
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
223
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. 
227 */
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)
233 {
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;
243
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);
251                 }
252
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);
258                 }
259         }
260
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;
267                 }
268
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++;
275                         }
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++;
281                         }
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++;
287                         }
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++;
292                         }
293                 }
294                 
295         }
296
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");
302                 }
303         }
304
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);
311                 }
312                 skip_streams = true;
313         }       
314
315
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 */
318
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 */
321
322
323
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)
333
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);
340
341         ALIAS_CHECK("BASIC_INFO", "BASIC_INFORMATION");
342
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);
350
351         ALIAS_CHECK("STANDARD_INFO", "STANDARD_INFORMATION");
352
353 #undef INFO_CHECK
354 #define INFO_CHECK \
355                 VAL_EQUAL(ea_info,  ea_size,     ea_info, ea_size);
356
357         ALIAS_CHECK("EA_INFO", "EA_INFORMATION");
358
359 #undef INFO_CHECK
360 #define INFO_CHECK \
361                 STR_EQUAL(name_info,  fname,   name_info, fname);
362
363         ALIAS_CHECK("NAME_INFO", "NAME_INFORMATION");
364
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);
379
380         ALIAS_CHECK("ALL_INFO", "ALL_INFORMATION");
381
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);
389
390         ALIAS_CHECK("COMPRESSION_INFO", "COMPRESSION_INFORMATION");
391
392
393 #undef INFO_CHECK
394 #define INFO_CHECK \
395                 STR_EQUAL(alt_name_info,  fname,   alt_name_info, fname);
396
397         ALIAS_CHECK("ALT_NAME_INFO", "ALT_NAME_INFORMATION");
398
399
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; \
407         } \
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)
415
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; \
423         } \
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)
431
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; \
440         } \
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
449
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));
454
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);
462
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));
466
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);
474
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));
478
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);
487
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));
491
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);
496
497
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; \
505         } \
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)
513
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);
517         
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);
532         }
533
534
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);
538         
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);
550         }
551
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; \
559         } \
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)
567
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);
571         
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);
577         }
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);
584
585         correct_name = fname;
586         torture_comment(torture, "name: %s\n", correct_name);
587
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; \
595         } \
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)
603
604         NAME_CHECK("NAME_INFO",        name_info, fname, STR_UNICODE);
605         NAME_CHECK("NAME_INFORMATION", name_info, fname, STR_UNICODE);
606
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");
611         }
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;
623                         }
624                 }
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;
628                 }
629         }
630
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);
635                 
636                 NAME_CHECK("ALT_NAME_INFO",        alt_name_info, fname, STR_UNICODE);
637                 NAME_CHECK("ALT_NAME_INFORMATION", alt_name_info, fname, STR_UNICODE);
638                 
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;
652                 }
653                 
654                 if (!skip_streams) {
655                         correct_name = "::$DATA";
656                         torture_comment(torture, "stream_name: %s\n", correct_name);
657                         
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);
660                 }
661         }
662                 
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);
674                 }
675         }
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);
681                         }
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);
688                         }
689                 }
690         }
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);
698                 }
699         }
700
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; \
708         } \
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; \
715         } \
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; \
722         } \
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)
730
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;
742                 }
743         }
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;
749                 }
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;
753                 }
754         }
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);
760         }
761
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); \
766         } \
767 } while (0)
768
769
770         s1 = fnum_find("INTERNAL_INFORMATION");
771         if (s1) {
772                 torture_comment(torture, "file_id=%.0f\n", (double)s1->internal_information.out.file_id);
773         }
774
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 );
781         }
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);
786
787 #if 0
788         /* these are expected to differ */
789         NAME_PATH_CHECK("ACCESS_INFORMATION", access_information, access_flags);
790 #endif
791
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); \
799         } \
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 .... */
808         
809         /* when we set the delete disposition then the link count should drop
810            to 0 and delete_pending should be 1 */
811         
812         return ret;
813 }
814
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. 
818 */
819 bool torture_raw_qfileinfo(struct torture_context *torture, 
820                                                    struct smbcli_state *cli)
821 {
822         int fnum;
823         bool ret;
824         const char *fname = "\\torture_qfileinfo.txt";
825
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;
830         }
831
832         ret = torture_raw_qfileinfo_internals(torture, torture, cli->tree, fnum, fname, false /* is_ipc */);
833         
834         smbcli_close(cli->tree, fnum);
835         smbcli_unlink(cli->tree, fname);
836
837         return ret;
838 }
839
840 bool torture_raw_qfileinfo_pipe(struct torture_context *torture, 
841                                 struct smbcli_state *cli)
842 {
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;
849
850         if (!(p = dcerpc_pipe_init(torture, cli->tree->session->transport->socket->event.ctx,
851                                    lp_iconv_convenience(torture->lp_ctx)))) {
852                 return false;
853         }
854
855         status = dcerpc_pipe_open_smb(p, cli->tree, fname);
856         torture_assert_ntstatus_ok(torture, status, "dcerpc_pipe_open_smb failed");
857
858         ipc_tree = dcerpc_smb_tree(p->conn);
859         fnum = dcerpc_smb_fnum(p->conn);
860
861         ret = torture_raw_qfileinfo_internals(torture, torture, ipc_tree, fnum, fname, true /* is_ipc */);
862         
863         talloc_free(p);
864         return ret;
865 }