dc09e55b63999e8feb3e0fc222f8e15df5d27b45
[kai/samba.git] / source4 / 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                 printf("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                 printf("%d levels failed\n", count);
155                 if (count > 13) {
156                         printf("too many level failures - giving up\n");
157                         return False;
158                 }
159         }
160
161         printf("check for correct aliases\n");
162         s1 = find("SIZE_INFO");
163         s2 = find("SIZE_INFORMATION");
164         if (s1 && s2) {
165                 VAL_EQUAL(size_info, total_alloc_units, size_info, total_alloc_units);
166                 VAL_APPROX_EQUAL(size_info, avail_alloc_units, size_info, avail_alloc_units);
167                 VAL_EQUAL(size_info, sectors_per_unit,  size_info, sectors_per_unit);
168                 VAL_EQUAL(size_info, bytes_per_sector,  size_info, bytes_per_sector);
169         }       
170
171         s1 = find("DEVICE_INFO");
172         s2 = find("DEVICE_INFORMATION");
173         if (s1 && s2) {
174                 VAL_EQUAL(device_info, device_type,     device_info, device_type);
175                 VAL_EQUAL(device_info, characteristics, device_info, characteristics);
176         }       
177
178         s1 = find("VOLUME_INFO");
179         s2 = find("VOLUME_INFORMATION");
180         if (s1 && s2) {
181                 STRUCT_EQUAL(volume_info, create_time,    volume_info, create_time);
182                 VAL_EQUAL   (volume_info, serial_number,  volume_info, serial_number);
183                 STR_EQUAL   (volume_info, volume_name.s,    volume_info, volume_name.s);
184                 printf("volume_info.volume_name = '%s'\n", s1->volume_info.out.volume_name.s);
185         }       
186
187         s1 = find("ATTRIBUTE_INFO");
188         s2 = find("ATTRIBUTE_INFORMATION");
189         if (s1 && s2) {
190                 VAL_EQUAL(attribute_info, fs_attr,    
191                           attribute_info, fs_attr);
192                 VAL_EQUAL(attribute_info, max_file_component_length, 
193                           attribute_info, max_file_component_length);
194                 STR_EQUAL(attribute_info, fs_type.s, attribute_info, fs_type.s);
195                 printf("attribute_info.fs_type = '%s'\n", s1->attribute_info.out.fs_type.s);
196         }       
197
198         printf("check for consistent disk sizes\n");
199         s1 = find("DSKATTR");
200         s2 = find("ALLOCATION");
201         if (s1 && s2) {
202                 double size1, size2;
203                 double scale = s1->dskattr.out.blocks_per_unit * s1->dskattr.out.block_size;
204                 size1 = 1.0 * 
205                         s1->dskattr.out.units_total * 
206                         s1->dskattr.out.blocks_per_unit * 
207                         s1->dskattr.out.block_size / scale;
208                 size2 = 1.0 *
209                         s2->allocation.out.sectors_per_unit *
210                         s2->allocation.out.total_alloc_units *
211                         s2->allocation.out.bytes_per_sector / scale;
212                 if (abs(size1 - size2) > 1) {
213                         printf("Inconsistent total size in DSKATTR and ALLOCATION - size1=%.0f size2=%.0f\n", 
214                                size1, size2);
215                         ret = False;
216                 }
217                 printf("total disk = %.0f MB\n", size1*scale/1.0e6);
218         }
219
220         printf("check consistent free disk space\n");
221         s1 = find("DSKATTR");
222         s2 = find("ALLOCATION");
223         if (s1 && s2) {
224                 double size1, size2;
225                 double scale = s1->dskattr.out.blocks_per_unit * s1->dskattr.out.block_size;
226                 size1 = 1.0 * 
227                         s1->dskattr.out.units_free * 
228                         s1->dskattr.out.blocks_per_unit * 
229                         s1->dskattr.out.block_size / scale;
230                 size2 = 1.0 *
231                         s2->allocation.out.sectors_per_unit *
232                         s2->allocation.out.avail_alloc_units *
233                         s2->allocation.out.bytes_per_sector / scale;
234                 if (abs(size1 - size2) > 1) {
235                         printf("Inconsistent avail size in DSKATTR and ALLOCATION - size1=%.0f size2=%.0f\n", 
236                                size1, size2);
237                         ret = False;
238                 }
239                 printf("free disk = %.0f MB\n", size1*scale/1.0e6);
240         }
241         
242         printf("volume info consistency\n");
243         s1 = find("VOLUME");
244         s2 = find("VOLUME_INFO");
245         if (s1 && s2) {
246                 VAL_EQUAL(volume, serial_number,  volume_info, serial_number);
247                 STR_EQUAL(volume, volume_name.s,  volume_info, volume_name.s);
248         }       
249
250         /* disk size consistency - notice that 'avail_alloc_units' maps to the caller
251            available allocation units, not the total */
252         s1 = find("SIZE_INFO");
253         s2 = find("FULL_SIZE_INFORMATION");
254         if (s1 && s2) {
255                 VAL_EQUAL(size_info, total_alloc_units, full_size_information, total_alloc_units);
256                 VAL_APPROX_EQUAL(size_info, avail_alloc_units, full_size_information, call_avail_alloc_units);
257                 VAL_EQUAL(size_info, sectors_per_unit,  full_size_information, sectors_per_unit);
258                 VAL_EQUAL(size_info, bytes_per_sector,  full_size_information, bytes_per_sector);
259         }       
260
261         printf("check for non-zero unknown fields\n");
262         s1 = find("QUOTA_INFORMATION");
263         if (s1) {
264                 VAL_UNKNOWN(quota_information, unknown[0]);
265                 VAL_UNKNOWN(quota_information, unknown[1]);
266                 VAL_UNKNOWN(quota_information, unknown[2]);
267         }
268
269         s1 = find("OBJECTID_INFORMATION");
270         if (s1) {
271                 VAL_UNKNOWN(objectid_information, unknown[0]);
272                 VAL_UNKNOWN(objectid_information, unknown[1]);
273                 VAL_UNKNOWN(objectid_information, unknown[2]);
274                 VAL_UNKNOWN(objectid_information, unknown[3]);
275                 VAL_UNKNOWN(objectid_information, unknown[4]);
276                 VAL_UNKNOWN(objectid_information, unknown[5]);
277         }
278
279
280 #define STR_CHECK(sname, stype, field, flags) do { \
281         s1 = find(sname); \
282         if (s1) { \
283                 if (s1->stype.out.field.s && wire_bad_flags(&s1->stype.out.field, flags, cli->transport)) { \
284                         printf("(%d) incorrect string termination in %s/%s\n", \
285                                __LINE__, #stype, #field); \
286                         ret = False; \
287                 } \
288         }} while (0)
289
290         printf("check for correct termination\n");
291         
292         STR_CHECK("VOLUME",                volume,         volume_name, 0);
293         STR_CHECK("VOLUME_INFO",           volume_info,    volume_name, STR_UNICODE);
294         STR_CHECK("VOLUME_INFORMATION",    volume_info,    volume_name, STR_UNICODE);
295         STR_CHECK("ATTRIBUTE_INFO",        attribute_info, fs_type, STR_UNICODE);
296         STR_CHECK("ATTRIBUTE_INFORMATION", attribute_info, fs_type, STR_UNICODE);
297
298         return ret;
299 }