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