Register types rather than constructors, display structs as classes.
[jelmer/samba4-debian.git] / source / torture / raw / qfsinfo.c
1 /* 
2    Unix SMB/CIFS implementation.
3    RAW_QFS_* 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 3 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, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "includes.h"
21 #include "torture/torture.h"
22 #include "libcli/raw/libcliraw.h"
23 #include "libcli/libcli.h"
24 #include "torture/util.h"
25
26
27 static struct {
28         const char *name;
29         enum smb_fsinfo_level level;
30         uint32_t capability_mask;
31         NTSTATUS status;
32         union smb_fsinfo fsinfo;
33 } levels[] = {
34         {"DSKATTR",               RAW_QFS_DSKATTR, },
35         {"ALLOCATION",            RAW_QFS_ALLOCATION, },
36         {"VOLUME",                RAW_QFS_VOLUME, },
37         {"VOLUME_INFO",           RAW_QFS_VOLUME_INFO, },
38         {"SIZE_INFO",             RAW_QFS_SIZE_INFO, },
39         {"DEVICE_INFO",           RAW_QFS_DEVICE_INFO, },
40         {"ATTRIBUTE_INFO",        RAW_QFS_ATTRIBUTE_INFO, },
41         {"UNIX_INFO",             RAW_QFS_UNIX_INFO, CAP_UNIX},
42         {"VOLUME_INFORMATION",    RAW_QFS_VOLUME_INFORMATION, },
43         {"SIZE_INFORMATION",      RAW_QFS_SIZE_INFORMATION, },
44         {"DEVICE_INFORMATION",    RAW_QFS_DEVICE_INFORMATION, },
45         {"ATTRIBUTE_INFORMATION", RAW_QFS_ATTRIBUTE_INFORMATION, },
46         {"QUOTA_INFORMATION",     RAW_QFS_QUOTA_INFORMATION, },
47         {"FULL_SIZE_INFORMATION", RAW_QFS_FULL_SIZE_INFORMATION, },
48 #if 0
49         /* w2k3 seems to no longer support this */
50         {"OBJECTID_INFORMATION",  RAW_QFS_OBJECTID_INFORMATION, },
51 #endif
52         { NULL, }
53 };
54
55
56 /*
57   find a level in the levels[] table
58 */
59 static union smb_fsinfo *find(const char *name)
60 {
61         int i;
62         for (i=0; levels[i].name; i++) {
63                 if (strcmp(name, levels[i].name) == 0 &&
64                     NT_STATUS_IS_OK(levels[i].status)) {
65                         return &levels[i].fsinfo;
66                 }
67         }
68         return NULL;
69 }
70
71 /* local macros to make the code below more readable */
72 #define VAL_EQUAL(n1, v1, n2, v2) do {if (s1->n1.out.v1 != s2->n2.out.v2) { \
73         printf("%s/%s [%u] != %s/%s [%u] at %s(%d)\n", \
74                #n1, #v1, (uint_t)s1->n1.out.v1, \
75                #n2, #v2, (uint_t)s2->n2.out.v2, \
76                __FILE__, __LINE__); \
77         ret = false; \
78 }} while(0)
79
80 #define VAL_APPROX_EQUAL(n1, v1, n2, v2) do {if (abs((int)(s1->n1.out.v1) - (int)(s2->n2.out.v2)) > 0.1*s1->n1.out.v1) { \
81         printf("%s/%s [%u] != %s/%s [%u] at %s(%d)\n", \
82                #n1, #v1, (uint_t)s1->n1.out.v1, \
83                #n2, #v2, (uint_t)s2->n2.out.v2, \
84                __FILE__, __LINE__); \
85         ret = false; \
86 }} while(0)
87
88 #define STR_EQUAL(n1, v1, n2, v2) do { \
89        if (strcmp_safe(s1->n1.out.v1, s2->n2.out.v2)) { \
90          printf("%s/%s [%s] != %s/%s [%s] at %s(%d)\n", \
91                #n1, #v1, s1->n1.out.v1, \
92                #n2, #v2, s2->n2.out.v2, \
93                __FILE__, __LINE__); \
94         ret = false; \
95 }} while(0)
96
97 #define STRUCT_EQUAL(n1, v1, n2, v2) do {if (memcmp(&s1->n1.out.v1,&s2->n2.out.v2,sizeof(s1->n1.out.v1))) { \
98         printf("%s/%s != %s/%s at %s(%d)\n", \
99                #n1, #v1, \
100                #n2, #v2, \
101                __FILE__, __LINE__); \
102         ret = false; \
103 }} while(0)
104
105 /* used to find hints on unknown values - and to make sure 
106    we zero-fill */
107 #define VAL_UNKNOWN(n1, v1) do {if (s1->n1.out.v1 != 0) { \
108         printf("%s/%s non-zero unknown - %u (0x%x) at %s(%d)\n", \
109                #n1, #v1, \
110                (uint_t)s1->n1.out.v1, \
111                (uint_t)s1->n1.out.v1, \
112                __FILE__, __LINE__); \
113         ret = false; \
114 }} while(0)
115
116 /* basic testing of all RAW_QFS_* calls 
117    for each call we test that it succeeds, and where possible test 
118    for consistency between the calls. 
119
120    Some of the consistency tests assume that the target filesystem is
121    quiescent, which is sometimes hard to achieve
122 */
123 bool torture_raw_qfsinfo(struct torture_context *torture, 
124                          struct smbcli_state *cli)
125 {
126         int i;
127         bool ret = true;
128         int count;
129         union smb_fsinfo *s1, *s2;      
130
131         /* scan all the levels, pulling the results */
132         for (i=0; levels[i].name; i++) {
133                 torture_comment(torture, "Running level %s\n", levels[i].name);
134                 levels[i].fsinfo.generic.level = levels[i].level;
135                 levels[i].status = smb_raw_fsinfo(cli->tree, torture, &levels[i].fsinfo);
136         }
137
138         /* check for completely broken levels */
139         for (count=i=0; levels[i].name; i++) {
140                 uint32_t cap = cli->transport->negotiate.capabilities;
141                 /* see if this server claims to support this level */
142                 if ((cap & levels[i].capability_mask) != levels[i].capability_mask) {
143                         continue;
144                 }
145                 
146                 if (!NT_STATUS_IS_OK(levels[i].status)) {
147                         printf("ERROR: level %s failed - %s\n", 
148                                levels[i].name, nt_errstr(levels[i].status));
149                         count++;
150                 }
151         }
152
153         if (count != 0) {
154                 torture_comment(torture, "%d levels failed\n", count);
155                 torture_assert(torture, count > 13, "too many level failures - giving up");
156         }
157
158         torture_comment(torture, "check for correct aliases\n");
159         s1 = find("SIZE_INFO");
160         s2 = find("SIZE_INFORMATION");
161         if (s1 && s2) {
162                 VAL_EQUAL(size_info, total_alloc_units, size_info, total_alloc_units);
163                 VAL_APPROX_EQUAL(size_info, avail_alloc_units, size_info, avail_alloc_units);
164                 VAL_EQUAL(size_info, sectors_per_unit,  size_info, sectors_per_unit);
165                 VAL_EQUAL(size_info, bytes_per_sector,  size_info, bytes_per_sector);
166         }       
167
168         s1 = find("DEVICE_INFO");
169         s2 = find("DEVICE_INFORMATION");
170         if (s1 && s2) {
171                 VAL_EQUAL(device_info, device_type,     device_info, device_type);
172                 VAL_EQUAL(device_info, characteristics, device_info, characteristics);
173         }       
174
175         s1 = find("VOLUME_INFO");
176         s2 = find("VOLUME_INFORMATION");
177         if (s1 && s2) {
178                 STRUCT_EQUAL(volume_info, create_time,    volume_info, create_time);
179                 VAL_EQUAL   (volume_info, serial_number,  volume_info, serial_number);
180                 STR_EQUAL   (volume_info, volume_name.s,    volume_info, volume_name.s);
181                 torture_comment(torture, "volume_info.volume_name = '%s'\n", s1->volume_info.out.volume_name.s);
182         }       
183
184         s1 = find("ATTRIBUTE_INFO");
185         s2 = find("ATTRIBUTE_INFORMATION");
186         if (s1 && s2) {
187                 VAL_EQUAL(attribute_info, fs_attr,    
188                           attribute_info, fs_attr);
189                 VAL_EQUAL(attribute_info, max_file_component_length, 
190                           attribute_info, max_file_component_length);
191                 STR_EQUAL(attribute_info, fs_type.s, attribute_info, fs_type.s);
192                 torture_comment(torture, "attribute_info.fs_type = '%s'\n", s1->attribute_info.out.fs_type.s);
193         }       
194
195         torture_comment(torture, "check for consistent disk sizes\n");
196         s1 = find("DSKATTR");
197         s2 = find("ALLOCATION");
198         if (s1 && s2) {
199                 double size1, size2;
200                 double scale = s1->dskattr.out.blocks_per_unit * s1->dskattr.out.block_size;
201                 size1 = 1.0 * 
202                         s1->dskattr.out.units_total * 
203                         s1->dskattr.out.blocks_per_unit * 
204                         s1->dskattr.out.block_size / scale;
205                 size2 = 1.0 *
206                         s2->allocation.out.sectors_per_unit *
207                         s2->allocation.out.total_alloc_units *
208                         s2->allocation.out.bytes_per_sector / scale;
209                 if (abs(size1 - size2) > 1) {
210                         printf("Inconsistent total size in DSKATTR and ALLOCATION - size1=%.0f size2=%.0f\n", 
211                                size1, size2);
212                         ret = false;
213                 }
214                 torture_comment(torture, "total disk = %.0f MB\n", size1*scale/1.0e6);
215         }
216
217         torture_comment(torture, "check consistent free disk space\n");
218         s1 = find("DSKATTR");
219         s2 = find("ALLOCATION");
220         if (s1 && s2) {
221                 double size1, size2;
222                 double scale = s1->dskattr.out.blocks_per_unit * s1->dskattr.out.block_size;
223                 size1 = 1.0 * 
224                         s1->dskattr.out.units_free * 
225                         s1->dskattr.out.blocks_per_unit * 
226                         s1->dskattr.out.block_size / scale;
227                 size2 = 1.0 *
228                         s2->allocation.out.sectors_per_unit *
229                         s2->allocation.out.avail_alloc_units *
230                         s2->allocation.out.bytes_per_sector / scale;
231                 if (abs(size1 - size2) > 1) {
232                         printf("Inconsistent avail size in DSKATTR and ALLOCATION - size1=%.0f size2=%.0f\n", 
233                                size1, size2);
234                         ret = false;
235                 }
236                 torture_comment(torture, "free disk = %.0f MB\n", size1*scale/1.0e6);
237         }
238         
239         torture_comment(torture, "volume info consistency\n");
240         s1 = find("VOLUME");
241         s2 = find("VOLUME_INFO");
242         if (s1 && s2) {
243                 VAL_EQUAL(volume, serial_number,  volume_info, serial_number);
244                 STR_EQUAL(volume, volume_name.s,  volume_info, volume_name.s);
245         }       
246
247         /* disk size consistency - notice that 'avail_alloc_units' maps to the caller
248            available allocation units, not the total */
249         s1 = find("SIZE_INFO");
250         s2 = find("FULL_SIZE_INFORMATION");
251         if (s1 && s2) {
252                 VAL_EQUAL(size_info, total_alloc_units, full_size_information, total_alloc_units);
253                 VAL_APPROX_EQUAL(size_info, avail_alloc_units, full_size_information, call_avail_alloc_units);
254                 VAL_EQUAL(size_info, sectors_per_unit,  full_size_information, sectors_per_unit);
255                 VAL_EQUAL(size_info, bytes_per_sector,  full_size_information, bytes_per_sector);
256         }       
257
258         printf("check for non-zero unknown fields\n");
259         s1 = find("QUOTA_INFORMATION");
260         if (s1) {
261                 VAL_UNKNOWN(quota_information, unknown[0]);
262                 VAL_UNKNOWN(quota_information, unknown[1]);
263                 VAL_UNKNOWN(quota_information, unknown[2]);
264         }
265
266         s1 = find("OBJECTID_INFORMATION");
267         if (s1) {
268                 VAL_UNKNOWN(objectid_information, unknown[0]);
269                 VAL_UNKNOWN(objectid_information, unknown[1]);
270                 VAL_UNKNOWN(objectid_information, unknown[2]);
271                 VAL_UNKNOWN(objectid_information, unknown[3]);
272                 VAL_UNKNOWN(objectid_information, unknown[4]);
273                 VAL_UNKNOWN(objectid_information, unknown[5]);
274         }
275
276
277 #define STR_CHECK(sname, stype, field, flags) do { \
278         s1 = find(sname); \
279         if (s1) { \
280                 if (s1->stype.out.field.s && wire_bad_flags(&s1->stype.out.field, flags, cli->transport)) { \
281                         printf("(%d) incorrect string termination in %s/%s\n", \
282                                __LINE__, #stype, #field); \
283                         ret = false; \
284                 } \
285         }} while (0)
286
287         torture_comment(torture, "check for correct termination\n");
288         
289         STR_CHECK("VOLUME",                volume,         volume_name, 0);
290         STR_CHECK("VOLUME_INFO",           volume_info,    volume_name, STR_UNICODE);
291         STR_CHECK("VOLUME_INFORMATION",    volume_info,    volume_name, STR_UNICODE);
292         STR_CHECK("ATTRIBUTE_INFO",        attribute_info, fs_type, STR_UNICODE);
293         STR_CHECK("ATTRIBUTE_INFORMATION", attribute_info, fs_type, STR_UNICODE);
294
295         return ret;
296 }