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