1 /*
2    Unix SMB/CIFS implementation.
3 
4    Copyright (C) Andrew Tridgell 2008
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 /*
21   test offline files
22  */
23 
24 #include "includes.h"
25 #include "system/time.h"
26 #include "system/filesys.h"
27 #include "libcli/libcli.h"
28 #include "torture/util.h"
29 #include "lib/events/events.h"
30 #include "libcli/composite/composite.h"
31 #include "libcli/smb_composite/smb_composite.h"
32 #include "torture/raw/proto.h"
33 
34 #define BASEDIR "\\testoffline"
35 
36 static int nconnections;
37 static int numstates;
38 static int num_connected;
39 static int test_failed;
40 extern int torture_numops;
41 extern int torture_entries;
42 static bool test_finished;
43 
44 enum offline_op {OP_LOADFILE, OP_SAVEFILE, OP_SETOFFLINE, OP_GETOFFLINE, OP_ENDOFLIST};
45 
46 static double latencies[OP_ENDOFLIST];
47 static double worst_latencies[OP_ENDOFLIST];
48 
49 #define FILE_SIZE 8192
50 
51 
52 struct offline_state {
53 	struct torture_context *tctx;
54 	struct tevent_context *ev;
55 	struct smbcli_tree *tree;
56 	TALLOC_CTX *mem_ctx;
57 	int client;
58 	int fnum;
59 	uint32_t count;
60 	uint32_t lastcount;
61 	uint32_t fnumber;
62 	uint32_t offline_count;
63 	uint32_t online_count;
64 	char *fname;
65 	struct smb_composite_loadfile *loadfile;
66 	struct smb_composite_savefile *savefile;
67 	struct smbcli_request *req;
68 	enum offline_op op;
69 	struct timeval tv_start;
70 };
71 
72 static void test_offline(struct offline_state *state);
73 
74 
filename(TALLOC_CTX * ctx,int i)75 static char *filename(TALLOC_CTX *ctx, int i)
76 {
77 	char *s = talloc_asprintf(ctx, BASEDIR "\\file%u.dat", i);
78 	return s;
79 }
80 
81 
82 /*
83   called when a loadfile completes
84  */
loadfile_callback(struct composite_context * ctx)85 static void loadfile_callback(struct composite_context *ctx)
86 {
87 	struct offline_state *state = ctx->async.private_data;
88 	NTSTATUS status;
89 	int i;
90 
91 	status = smb_composite_loadfile_recv(ctx, state->mem_ctx);
92 	if (!NT_STATUS_IS_OK(status)) {
93 		printf("Failed to read file '%s' - %s\n",
94 		       state->loadfile->in.fname, nt_errstr(status));
95 		test_failed++;
96 		return;
97 	}
98 
99 	/* check the data is correct */
100 	if (state->loadfile->out.size != FILE_SIZE) {
101 		printf("Wrong file size %u - expected %u\n",
102 		       state->loadfile->out.size, FILE_SIZE);
103 		test_failed++;
104 		return;
105 	}
106 
107 	for (i=0;i<FILE_SIZE;i++) {
108 		if (state->loadfile->out.data[i] != 1+(state->fnumber % 255)) {
109 			printf("Bad data in file %u (got %u expected %u)\n",
110 			       state->fnumber,
111 			       state->loadfile->out.data[i],
112 			       1+(state->fnumber % 255));
113 			test_failed++;
114 			return;
115 		}
116 	}
117 
118 	talloc_steal(state->loadfile, state->loadfile->out.data);
119 
120 	state->count++;
121 	talloc_free(state->loadfile);
122 	state->loadfile = NULL;
123 
124 	if (!test_finished) {
125 		test_offline(state);
126 	}
127 }
128 
129 
130 /*
131   called when a savefile completes
132  */
savefile_callback(struct composite_context * ctx)133 static void savefile_callback(struct composite_context *ctx)
134 {
135 	struct offline_state *state = ctx->async.private_data;
136 	NTSTATUS status;
137 
138 	status = smb_composite_savefile_recv(ctx);
139 	if (!NT_STATUS_IS_OK(status)) {
140 		printf("Failed to save file '%s' - %s\n",
141 		       state->savefile->in.fname, nt_errstr(status));
142 		test_failed++;
143 	}
144 
145 	state->count++;
146 	talloc_free(state->savefile);
147 	state->savefile = NULL;
148 
149 	if (!test_finished) {
150 		test_offline(state);
151 	}
152 }
153 
154 
155 /*
156   called when a setoffline completes
157  */
setoffline_callback(struct smbcli_request * req)158 static void setoffline_callback(struct smbcli_request *req)
159 {
160 	struct offline_state *state = req->async.private_data;
161 	NTSTATUS status;
162 
163 	status = smbcli_request_simple_recv(req);
164 	if (!NT_STATUS_IS_OK(status)) {
165 		printf("Failed to set offline file '%s' - %s\n",
166 		       state->fname, nt_errstr(status));
167 		test_failed++;
168 	}
169 
170 	state->req = NULL;
171 	state->count++;
172 
173 	if (!test_finished) {
174 		test_offline(state);
175 	}
176 }
177 
178 
179 /*
180   called when a getoffline completes
181  */
getoffline_callback(struct smbcli_request * req)182 static void getoffline_callback(struct smbcli_request *req)
183 {
184 	struct offline_state *state = req->async.private_data;
185 	NTSTATUS status;
186 	union smb_fileinfo io;
187 
188 	ZERO_STRUCT(io);
189 
190 	io.getattr.level = RAW_FILEINFO_GETATTR;
191 
192 	status = smb_raw_pathinfo_recv(req, state->mem_ctx, &io);
193 	if (!NT_STATUS_IS_OK(status)) {
194 		printf("Failed to get offline file '%s' - %s\n",
195 		       state->fname, nt_errstr(status));
196 		test_failed++;
197 	}
198 
199 	if (io.getattr.out.attrib & FILE_ATTRIBUTE_OFFLINE) {
200 		state->offline_count++;
201 	} else {
202 		state->online_count++;
203 	}
204 
205 	state->req = NULL;
206 	state->count++;
207 
208 	if (!test_finished) {
209 		test_offline(state);
210 	}
211 }
212 
213 
214 /*
215   send the next offline file fetch request
216 */
test_offline(struct offline_state * state)217 static void test_offline(struct offline_state *state)
218 {
219 	struct composite_context *ctx;
220 	double lat;
221 
222 	lat = timeval_elapsed(&state->tv_start);
223 	if (latencies[state->op] < lat) {
224 		latencies[state->op] = lat;
225 	}
226 
227 	state->op = (enum offline_op) (random() % OP_ENDOFLIST);
228 
229 	state->fnumber = random() % torture_numops;
230 	talloc_free(state->fname);
231 	state->fname = filename(state->mem_ctx, state->fnumber);
232 
233 	state->tv_start = timeval_current();
234 
235 	switch (state->op) {
236 	case OP_LOADFILE:
237 		state->loadfile = talloc_zero(state->mem_ctx, struct smb_composite_loadfile);
238 		state->loadfile->in.fname = state->fname;
239 
240 		ctx = smb_composite_loadfile_send(state->tree, state->loadfile);
241 		if (ctx == NULL) {
242 			printf("Failed to setup loadfile for %s\n", state->fname);
243 			test_failed = true;
244 		}
245 
246 		talloc_steal(state->loadfile, ctx);
247 
248 		ctx->async.fn = loadfile_callback;
249 		ctx->async.private_data = state;
250 		break;
251 
252 	case OP_SAVEFILE:
253 		state->savefile = talloc_zero(state->mem_ctx, struct smb_composite_savefile);
254 
255 		state->savefile->in.fname = state->fname;
256 		state->savefile->in.data  = talloc_size(state->savefile, FILE_SIZE);
257 		state->savefile->in.size  = FILE_SIZE;
258 		memset(state->savefile->in.data, 1+(state->fnumber%255), FILE_SIZE);
259 
260 		ctx = smb_composite_savefile_send(state->tree, state->savefile);
261 		if (ctx == NULL) {
262 			printf("Failed to setup savefile for %s\n", state->fname);
263 			test_failed = true;
264 		}
265 
266 		talloc_steal(state->savefile, ctx);
267 
268 		ctx->async.fn = savefile_callback;
269 		ctx->async.private_data = state;
270 		break;
271 
272 	case OP_SETOFFLINE: {
273 		union smb_setfileinfo io;
274 		ZERO_STRUCT(io);
275 		io.setattr.level = RAW_SFILEINFO_SETATTR;
276 		io.setattr.in.attrib = FILE_ATTRIBUTE_OFFLINE;
277 		io.setattr.in.file.path = state->fname;
278 		/* make the file 1 hour old, to get past mininum age restrictions
279 		   for HSM systems */
280 		io.setattr.in.write_time = time(NULL) - 60*60;
281 
282 		state->req = smb_raw_setpathinfo_send(state->tree, &io);
283 		if (state->req == NULL) {
284 			printf("Failed to setup setoffline for %s\n", state->fname);
285 			test_failed = true;
286 		}
287 
288 		state->req->async.fn = setoffline_callback;
289 		state->req->async.private_data = state;
290 		break;
291 	}
292 
293 	case OP_GETOFFLINE: {
294 		union smb_fileinfo io;
295 		ZERO_STRUCT(io);
296 		io.getattr.level = RAW_FILEINFO_GETATTR;
297 		io.getattr.in.file.path = state->fname;
298 
299 		state->req = smb_raw_pathinfo_send(state->tree, &io);
300 		if (state->req == NULL) {
301 			printf("Failed to setup getoffline for %s\n", state->fname);
302 			test_failed = true;
303 		}
304 
305 		state->req->async.fn = getoffline_callback;
306 		state->req->async.private_data = state;
307 		break;
308 	}
309 
310 	default:
311 		printf("bad operation??\n");
312 		break;
313 	}
314 }
315 
316 
317 
318 
echo_completion(struct smbcli_request * req)319 static void echo_completion(struct smbcli_request *req)
320 {
321 	struct offline_state *state = (struct offline_state *)req->async.private_data;
322 	NTSTATUS status = smbcli_request_simple_recv(req);
323 	if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE) ||
324 	    NT_STATUS_EQUAL(status, NT_STATUS_LOCAL_DISCONNECT) ||
325 	    NT_STATUS_EQUAL(status, NT_STATUS_CONNECTION_RESET)) {
326 		talloc_free(state->tree);
327 		state->tree = NULL;
328 		num_connected--;
329 		DEBUG(0,("lost connection\n"));
330 		test_failed++;
331 	}
332 }
333 
report_rate(struct tevent_context * ev,struct tevent_timer * te,struct timeval t,void * private_data)334 static void report_rate(struct tevent_context *ev, struct tevent_timer *te,
335 			struct timeval t, void *private_data)
336 {
337 	struct offline_state *state = talloc_get_type(private_data,
338 							struct offline_state);
339 	int i;
340 	uint32_t total=0, total_offline=0, total_online=0;
341 	for (i=0;i<numstates;i++) {
342 		total += state[i].count - state[i].lastcount;
343 		if (timeval_elapsed(&state[i].tv_start) > latencies[state[i].op]) {
344 			latencies[state[i].op] = timeval_elapsed(&state[i].tv_start);
345 		}
346 		state[i].lastcount = state[i].count;
347 		total_online += state[i].online_count;
348 		total_offline += state[i].offline_count;
349 	}
350 	printf("ops/s=%4u  offline=%5u online=%4u  set_lat=%.1f/%.1f get_lat=%.1f/%.1f save_lat=%.1f/%.1f load_lat=%.1f/%.1f\n",
351 	       total, total_offline, total_online,
352 	       latencies[OP_SETOFFLINE],
353 	       worst_latencies[OP_SETOFFLINE],
354 	       latencies[OP_GETOFFLINE],
355 	       worst_latencies[OP_GETOFFLINE],
356 	       latencies[OP_SAVEFILE],
357 	       worst_latencies[OP_SAVEFILE],
358 	       latencies[OP_LOADFILE],
359 	       worst_latencies[OP_LOADFILE]);
360 	fflush(stdout);
361 	tevent_add_timer(ev, state, timeval_current_ofs(1, 0), report_rate, state);
362 
363 	for (i=0;i<OP_ENDOFLIST;i++) {
364 		if (latencies[i] > worst_latencies[i]) {
365 			worst_latencies[i] = latencies[i];
366 		}
367 		latencies[i] = 0;
368 	}
369 
370 	/* send an echo on each interface to ensure it stays alive - this helps
371 	   with IP takeover */
372 	for (i=0;i<numstates;i++) {
373 		struct smb_echo p;
374 		struct smbcli_request *req;
375 
376 		if (!state[i].tree) {
377 			continue;
378 		}
379 
380 		p.in.repeat_count = 1;
381 		p.in.size = 0;
382 		p.in.data = NULL;
383 		req = smb_raw_echo_send(state[i].tree->session->transport, &p);
384 		req->async.private_data = &state[i];
385 		req->async.fn      = echo_completion;
386 	}
387 }
388 
389 /*
390    test offline file handling
391 */
torture_test_offline(struct torture_context * torture)392 bool torture_test_offline(struct torture_context *torture)
393 {
394 	bool ret = true;
395 	TALLOC_CTX *mem_ctx = talloc_new(torture);
396 	int i;
397 	int timelimit = torture_setting_int(torture, "timelimit", 10);
398 	struct timeval tv;
399 	struct offline_state *state;
400 	struct smbcli_state *cli;
401 	bool progress;
402 	progress = torture_setting_bool(torture, "progress", true);
403 
404 	nconnections = torture_setting_int(torture, "nprocs", 4);
405 	numstates = nconnections * torture_entries;
406 
407 	state = talloc_zero_array(mem_ctx, struct offline_state, numstates);
408 
409 	printf("Opening %d connections with %d simultaneous operations and %u files\n", nconnections, numstates, torture_numops);
410 	for (i=0;i<nconnections;i++) {
411 		state[i].tctx = torture;
412 		state[i].mem_ctx = talloc_new(state);
413 		state[i].ev = torture->ev;
414 		if (!torture_open_connection_ev(&cli, i, torture, torture->ev)) {
415 			return false;
416 		}
417 		state[i].tree = cli->tree;
418 		state[i].client = i;
419 		/* allow more time for offline files */
420 		state[i].tree->session->transport->options.request_timeout = 200;
421 	}
422 
423 	/* the others are repeats on the earlier connections */
424 	for (i=nconnections;i<numstates;i++) {
425 		state[i].tctx = torture;
426 		state[i].mem_ctx = talloc_new(state);
427 		state[i].ev = torture->ev;
428 		state[i].tree = state[i % nconnections].tree;
429 		state[i].client = i;
430 	}
431 
432 	num_connected = i;
433 
434 	if (!torture_setup_dir(cli, BASEDIR)) {
435 		goto failed;
436 	}
437 
438 	/* pre-create files */
439 	printf("Pre-creating %u files ....\n", torture_numops);
440 	for (i=0;i<torture_numops;i++) {
441 		int fnum;
442 		char *fname = filename(mem_ctx, i);
443 		char buf[FILE_SIZE];
444 		NTSTATUS status;
445 
446 		memset(buf, 1+(i % 255), sizeof(buf));
447 
448 		fnum = smbcli_open(state[0].tree, fname, O_RDWR|O_CREAT, DENY_NONE);
449 		if (fnum == -1) {
450 			printf("Failed to open %s on connection %d\n", fname, i);
451 			goto failed;
452 		}
453 
454 		if (smbcli_write(state[0].tree, fnum, 0, buf, 0, sizeof(buf)) != sizeof(buf)) {
455 			printf("Failed to write file of size %u\n", FILE_SIZE);
456 			goto failed;
457 		}
458 
459 		status = smbcli_close(state[0].tree, fnum);
460 		if (!NT_STATUS_IS_OK(status)) {
461 			printf("Close failed - %s\n", nt_errstr(status));
462 			goto failed;
463 		}
464 
465 		talloc_free(fname);
466 	}
467 
468 	/* start the async ops */
469 	for (i=0;i<numstates;i++) {
470 		state[i].tv_start = timeval_current();
471 		test_offline(&state[i]);
472 	}
473 
474 	tv = timeval_current();
475 
476 	if (progress) {
477 		tevent_add_timer(torture->ev, state, timeval_current_ofs(1, 0), report_rate, state);
478 	}
479 
480 	printf("Running for %d seconds\n", timelimit);
481 	while (timeval_elapsed(&tv) < timelimit) {
482 		tevent_loop_once(torture->ev);
483 
484 		if (test_failed) {
485 			DEBUG(0,("test failed\n"));
486 			goto failed;
487 		}
488 	}
489 
490 	printf("\nWaiting for completion\n");
491 	test_finished = true;
492 	for (i=0;i<numstates;i++) {
493 		while (state[i].loadfile ||
494 		       state[i].savefile ||
495 		       state[i].req) {
496 			tevent_loop_once(torture->ev);
497 		}
498 	}
499 
500 	printf("worst latencies: set_lat=%.1f get_lat=%.1f save_lat=%.1f load_lat=%.1f\n",
501 	       worst_latencies[OP_SETOFFLINE],
502 	       worst_latencies[OP_GETOFFLINE],
503 	       worst_latencies[OP_SAVEFILE],
504 	       worst_latencies[OP_LOADFILE]);
505 
506 	smbcli_deltree(state[0].tree, BASEDIR);
507 	talloc_free(mem_ctx);
508 	printf("\n");
509 	return ret;
510 
511 failed:
512 	talloc_free(mem_ctx);
513 	return false;
514 }
515