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