982f348cd5acadce5cf6a40f35e361bb6456b1db
[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 {
125         struct smbcli_state *cli;
126         int i;
127         BOOL ret = True;
128         int count;
129         union smb_fsinfo *s1, *s2;      
130         TALLOC_CTX *mem_ctx;
131
132         if (!torture_open_connection(&cli, 0)) {
133                 return False;
134         }
135
136         mem_ctx = talloc_init("torture_qfsinfo");
137         
138         /* scan all the levels, pulling the results */
139         for (i=0; levels[i].name; i++) {
140                 printf("Running level %s\n", levels[i].name);
141                 levels[i].fsinfo.generic.level = levels[i].level;
142                 levels[i].status = smb_raw_fsinfo(cli->tree, mem_ctx, &levels[i].fsinfo);
143         }
144
145         /* check for completely broken levels */
146         for (count=i=0; levels[i].name; i++) {
147                 uint32_t cap = cli->transport->negotiate.capabilities;
148                 /* see if this server claims to support this level */
149                 if ((cap & levels[i].capability_mask) != levels[i].capability_mask) {
150                         continue;
151                 }
152                 
153                 if (!NT_STATUS_IS_OK(levels[i].status)) {
154                         printf("ERROR: level %s failed - %s\n", 
155                                levels[i].name, nt_errstr(levels[i].status));
156                         count++;
157                 }
158         }
159
160         if (count != 0) {
161                 ret = False;
162                 printf("%d levels failed\n", count);
163                 if (count > 13) {
164                         printf("too many level failures - giving up\n");
165                         goto done;
166                 }
167         }
168
169         printf("check for correct aliases\n");
170         s1 = find("SIZE_INFO");
171         s2 = find("SIZE_INFORMATION");
172         if (s1 && s2) {
173                 VAL_EQUAL(size_info, total_alloc_units, size_info, total_alloc_units);
174                 VAL_APPROX_EQUAL(size_info, avail_alloc_units, size_info, avail_alloc_units);
175                 VAL_EQUAL(size_info, sectors_per_unit,  size_info, sectors_per_unit);
176                 VAL_EQUAL(size_info, bytes_per_sector,  size_info, bytes_per_sector);
177         }       
178
179         s1 = find("DEVICE_INFO");
180         s2 = find("DEVICE_INFORMATION");
181         if (s1 && s2) {
182                 VAL_EQUAL(device_info, device_type,     device_info, device_type);
183                 VAL_EQUAL(device_info, characteristics, device_info, characteristics);
184         }       
185
186         s1 = find("VOLUME_INFO");
187         s2 = find("VOLUME_INFORMATION");
188         if (s1 && s2) {
189                 STRUCT_EQUAL(volume_info, create_time,    volume_info, create_time);
190                 VAL_EQUAL   (volume_info, serial_number,  volume_info, serial_number);
191                 STR_EQUAL   (volume_info, volume_name.s,    volume_info, volume_name.s);
192                 printf("volume_info.volume_name = '%s'\n", s1->volume_info.out.volume_name.s);
193         }       
194
195         s1 = find("ATTRIBUTE_INFO");
196         s2 = find("ATTRIBUTE_INFORMATION");
197         if (s1 && s2) {
198                 VAL_EQUAL(attribute_info, fs_attr,    
199                           attribute_info, fs_attr);
200                 VAL_EQUAL(attribute_info, max_file_component_length, 
201                           attribute_info, max_file_component_length);
202                 STR_EQUAL(attribute_info, fs_type.s, attribute_info, fs_type.s);
203                 printf("attribute_info.fs_type = '%s'\n", s1->attribute_info.out.fs_type.s);
204         }       
205
206         printf("check for consistent disk sizes\n");
207         s1 = find("DSKATTR");
208         s2 = find("ALLOCATION");
209         if (s1 && s2) {
210                 double size1, size2;
211                 double scale = s1->dskattr.out.blocks_per_unit * s1->dskattr.out.block_size;
212                 size1 = 1.0 * 
213                         s1->dskattr.out.units_total * 
214                         s1->dskattr.out.blocks_per_unit * 
215                         s1->dskattr.out.block_size / scale;
216                 size2 = 1.0 *
217                         s2->allocation.out.sectors_per_unit *
218                         s2->allocation.out.total_alloc_units *
219                         s2->allocation.out.bytes_per_sector / scale;
220                 if (abs(size1 - size2) > 1) {
221                         printf("Inconsistent total size in DSKATTR and ALLOCATION - size1=%.0f size2=%.0f\n", 
222                                size1, size2);
223                         ret = False;
224                 }
225                 printf("total disk = %.0f MB\n", size1*scale/1.0e6);
226         }
227
228         printf("check consistent free disk space\n");
229         s1 = find("DSKATTR");
230         s2 = find("ALLOCATION");
231         if (s1 && s2) {
232                 double size1, size2;
233                 double scale = s1->dskattr.out.blocks_per_unit * s1->dskattr.out.block_size;
234                 size1 = 1.0 * 
235                         s1->dskattr.out.units_free * 
236                         s1->dskattr.out.blocks_per_unit * 
237                         s1->dskattr.out.block_size / scale;
238                 size2 = 1.0 *
239                         s2->allocation.out.sectors_per_unit *
240                         s2->allocation.out.avail_alloc_units *
241                         s2->allocation.out.bytes_per_sector / scale;
242                 if (abs(size1 - size2) > 1) {
243                         printf("Inconsistent avail size in DSKATTR and ALLOCATION - size1=%.0f size2=%.0f\n", 
244                                size1, size2);
245                         ret = False;
246                 }
247                 printf("free disk = %.0f MB\n", size1*scale/1.0e6);
248         }
249         
250         printf("volume info consistency\n");
251         s1 = find("VOLUME");
252         s2 = find("VOLUME_INFO");
253         if (s1 && s2) {
254                 VAL_EQUAL(volume, serial_number,  volume_info, serial_number);
255                 STR_EQUAL(volume, volume_name.s,  volume_info, volume_name.s);
256         }       
257
258         /* disk size consistency - notice that 'avail_alloc_units' maps to the caller
259            available allocation units, not the total */
260         s1 = find("SIZE_INFO");
261         s2 = find("FULL_SIZE_INFORMATION");
262         if (s1 && s2) {
263                 VAL_EQUAL(size_info, total_alloc_units, full_size_information, total_alloc_units);
264                 VAL_APPROX_EQUAL(size_info, avail_alloc_units, full_size_information, call_avail_alloc_units);
265                 VAL_EQUAL(size_info, sectors_per_unit,  full_size_information, sectors_per_unit);
266                 VAL_EQUAL(size_info, bytes_per_sector,  full_size_information, bytes_per_sector);
267         }       
268
269         printf("check for non-zero unknown fields\n");
270         s1 = find("QUOTA_INFORMATION");
271         if (s1) {
272                 VAL_UNKNOWN(quota_information, unknown[0]);
273                 VAL_UNKNOWN(quota_information, unknown[1]);
274                 VAL_UNKNOWN(quota_information, unknown[2]);
275         }
276
277         s1 = find("OBJECTID_INFORMATION");
278         if (s1) {
279                 VAL_UNKNOWN(objectid_information, unknown[0]);
280                 VAL_UNKNOWN(objectid_information, unknown[1]);
281                 VAL_UNKNOWN(objectid_information, unknown[2]);
282                 VAL_UNKNOWN(objectid_information, unknown[3]);
283                 VAL_UNKNOWN(objectid_information, unknown[4]);
284                 VAL_UNKNOWN(objectid_information, unknown[5]);
285         }
286
287
288 #define STR_CHECK(sname, stype, field, flags) do { \
289         s1 = find(sname); \
290         if (s1) { \
291                 if (s1->stype.out.field.s && wire_bad_flags(&s1->stype.out.field, flags, cli->transport)) { \
292                         printf("(%d) incorrect string termination in %s/%s\n", \
293                                __LINE__, #stype, #field); \
294                         ret = False; \
295                 } \
296         }} while (0)
297
298         printf("check for correct termination\n");
299         
300         STR_CHECK("VOLUME",                volume,         volume_name, 0);
301         STR_CHECK("VOLUME_INFO",           volume_info,    volume_name, STR_UNICODE);
302         STR_CHECK("VOLUME_INFORMATION",    volume_info,    volume_name, STR_UNICODE);
303         STR_CHECK("ATTRIBUTE_INFO",        attribute_info, fs_type, STR_UNICODE);
304         STR_CHECK("ATTRIBUTE_INFORMATION", attribute_info, fs_type, STR_UNICODE);
305
306 done:
307         torture_close_connection(cli);
308         talloc_free(mem_ctx);
309         return ret;
310 }