0f7787a92faf65de54360476aeaa8d716cde7168
[gd/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 "torture/torture.h"
26 #include "libcli/raw/libcliraw.h"
27 #include "system/time.h"
28 #include "system/filesys.h"
29 #include "libcli/libcli.h"
30 #include "torture/util.h"
31 #include "lib/events/events.h"
32 #include "lib/cmdline/popt_common.h"
33 #include "libcli/composite/composite.h"
34 #include "libcli/smb_composite/smb_composite.h"
35 #include "libcli/resolve/resolve.h"
36 #include "param/param.h"
37
38 #define BASEDIR "\\testoffline"
39
40 static int nconnections;
41 static int numstates;
42 static int num_connected;
43 static int test_failed;
44 extern int torture_numops;
45 extern int torture_entries;
46 static bool test_finished;
47
48 enum offline_op {OP_LOADFILE, OP_SAVEFILE, OP_SETOFFLINE, OP_GETOFFLINE, OP_ENDOFLIST};
49
50 static double latencies[OP_ENDOFLIST];
51 static double worst_latencies[OP_ENDOFLIST];
52
53 #define FILE_SIZE 8192
54
55
56 struct offline_state {
57         struct torture_context *tctx;
58         struct event_context *ev;
59         struct smbcli_tree *tree;
60         TALLOC_CTX *mem_ctx;
61         int client;
62         int fnum;
63         uint32_t count;
64         uint32_t lastcount;
65         uint32_t fnumber;
66         uint32_t offline_count;
67         uint32_t online_count;
68         char *fname;
69         struct smb_composite_loadfile *loadfile;
70         struct smb_composite_savefile *savefile;
71         struct smbcli_request *req;
72         enum offline_op op;
73         struct timeval tv_start;
74 };
75
76 static void test_offline(struct offline_state *state);
77
78
79 static char *filename(TALLOC_CTX *ctx, int i)
80 {
81         char *s = talloc_asprintf(ctx, BASEDIR "\\file%u.dat", i);
82         return s;
83 }
84
85
86 /*
87   called when a loadfile completes
88  */
89 static void loadfile_callback(struct composite_context *ctx) 
90 {
91         struct offline_state *state = ctx->async.private_data;
92         NTSTATUS status;
93         int i;
94
95         status = smb_composite_loadfile_recv(ctx, state->mem_ctx);
96         if (!NT_STATUS_IS_OK(status)) {
97                 printf("Failed to read file '%s' - %s\n", 
98                        state->loadfile->in.fname, nt_errstr(status));
99                 test_failed++;
100         }
101
102         /* check the data is correct */
103         if (state->loadfile->out.size != FILE_SIZE) {
104                 printf("Wrong file size %u - expected %u\n", 
105                        state->loadfile->out.size, FILE_SIZE);
106                 test_failed++;
107                 return;
108         }
109
110         for (i=0;i<FILE_SIZE;i++) {
111                 if (state->loadfile->out.data[i] != 1+(state->fnumber % 255)) {
112                         printf("Bad data in file %u (got %u expected %u)\n", 
113                                state->fnumber, 
114                                state->loadfile->out.data[i],
115                                1+(state->fnumber % 255));
116                         test_failed++;
117                         return;
118                 }
119         }
120         
121         talloc_steal(state->loadfile, state->loadfile->out.data);
122
123         state->count++;
124         talloc_free(state->loadfile);
125         state->loadfile = NULL;
126
127         if (!test_finished) {
128                 test_offline(state);
129         }
130 }
131
132
133 /*
134   called when a savefile completes
135  */
136 static void savefile_callback(struct composite_context *ctx) 
137 {
138         struct offline_state *state = ctx->async.private_data;
139         NTSTATUS status;
140
141         status = smb_composite_savefile_recv(ctx);
142         if (!NT_STATUS_IS_OK(status)) {
143                 printf("Failed to save file '%s' - %s\n", 
144                        state->savefile->in.fname, nt_errstr(status));
145                 test_failed++;
146         }
147
148         state->count++;
149         talloc_free(state->savefile);
150         state->savefile = NULL;
151
152         if (!test_finished) {
153                 test_offline(state);
154         }
155 }
156
157
158 /*
159   called when a setoffline completes
160  */
161 static void setoffline_callback(struct smbcli_request *req) 
162 {
163         struct offline_state *state = req->async.private;
164         NTSTATUS status;
165
166         status = smbcli_request_simple_recv(req);
167         if (!NT_STATUS_IS_OK(status)) {
168                 printf("Failed to set offline file '%s' - %s\n", 
169                        state->fname, nt_errstr(status));
170                 test_failed++;
171         }
172
173         state->req = NULL;
174         state->count++;
175
176         if (!test_finished) {
177                 test_offline(state);
178         }
179 }
180
181
182 /*
183   called when a getoffline completes
184  */
185 static void getoffline_callback(struct smbcli_request *req) 
186 {
187         struct offline_state *state = req->async.private;
188         NTSTATUS status;
189         union smb_fileinfo io;
190
191         io.getattr.level = RAW_FILEINFO_GETATTR;
192         
193         status = smb_raw_pathinfo_recv(req, state->mem_ctx, &io);
194         if (!NT_STATUS_IS_OK(status)) {
195                 printf("Failed to get offline file '%s' - %s\n", 
196                        state->fname, nt_errstr(status));
197                 test_failed++;
198         }
199
200         if (io.getattr.out.attrib & FILE_ATTRIBUTE_OFFLINE) {
201                 state->offline_count++;
202         } else {
203                 state->online_count++;
204         }
205
206         state->req = NULL;
207         state->count++;
208
209         if (!test_finished) {
210                 test_offline(state);
211         }
212 }
213
214
215 /*
216   send the next offline file fetch request
217 */
218 static void test_offline(struct offline_state *state)
219 {
220         struct composite_context *ctx;
221         double lat;
222
223         lat = timeval_elapsed(&state->tv_start);
224         if (latencies[state->op] < lat) {
225                 latencies[state->op] = lat;
226         }
227
228         state->op = (enum offline_op) (random() % OP_ENDOFLIST);
229         
230         state->fnumber = random() % torture_numops;
231         talloc_free(state->fname);
232         state->fname = filename(state->mem_ctx, state->fnumber);
233
234         state->tv_start = timeval_current();
235
236         switch (state->op) {
237         case OP_LOADFILE:
238                 state->loadfile = talloc_zero(state->mem_ctx, struct smb_composite_loadfile);
239                 state->loadfile->in.fname = state->fname;
240         
241                 ctx = smb_composite_loadfile_send(state->tree, state->loadfile);
242                 if (ctx == NULL) {
243                         printf("Failed to setup loadfile for %s\n", state->fname);
244                         test_failed = true;
245                 }
246
247                 talloc_steal(state->loadfile, ctx);
248
249                 ctx->async.fn = loadfile_callback;
250                 ctx->async.private_data = state;
251                 break;
252
253         case OP_SAVEFILE:
254                 state->savefile = talloc_zero(state->mem_ctx, struct smb_composite_savefile);
255
256                 state->savefile->in.fname = state->fname;
257                 state->savefile->in.data  = talloc_size(state->savefile, FILE_SIZE);
258                 state->savefile->in.size  = FILE_SIZE;
259                 memset(state->savefile->in.data, state->fnumber, FILE_SIZE);
260         
261                 ctx = smb_composite_savefile_send(state->tree, state->savefile);
262                 if (ctx == NULL) {
263                         printf("Failed to setup savefile for %s\n", state->fname);
264                         test_failed = true;
265                 }
266
267                 talloc_steal(state->savefile, ctx);
268
269                 ctx->async.fn = savefile_callback;
270                 ctx->async.private_data = state;
271                 break;
272
273         case OP_SETOFFLINE: {
274                 union smb_setfileinfo io;
275                 ZERO_STRUCT(io);
276                 io.setattr.level = RAW_SFILEINFO_SETATTR;
277                 io.setattr.in.attrib = FILE_ATTRIBUTE_OFFLINE;
278                 io.setattr.in.file.path = state->fname;
279                 /* make the file 1 hour old, to get past mininum age restrictions 
280                    for HSM systems */
281                 io.setattr.in.write_time = time(NULL) - 60*60;
282
283                 state->req = smb_raw_setpathinfo_send(state->tree, &io);
284                 if (state->req == NULL) {
285                         printf("Failed to setup setoffline for %s\n", state->fname);
286                         test_failed = true;
287                 }
288                 
289                 state->req->async.fn = setoffline_callback;
290                 state->req->async.private = state;
291                 break;
292         }
293
294         case OP_GETOFFLINE: {
295                 union smb_fileinfo io;
296                 ZERO_STRUCT(io);
297                 io.getattr.level = RAW_FILEINFO_GETATTR;
298                 io.getattr.in.file.path = state->fname;
299
300                 state->req = smb_raw_pathinfo_send(state->tree, &io);
301                 if (state->req == NULL) {
302                         printf("Failed to setup getoffline for %s\n", state->fname);
303                         test_failed = true;
304                 }
305                 
306                 state->req->async.fn = getoffline_callback;
307                 state->req->async.private = state;
308                 break;
309         }
310
311         default:
312                 printf("bad operation??\n");
313                 break;
314         }
315 }
316
317
318
319
320 static void echo_completion(struct smbcli_request *req)
321 {
322         struct offline_state *state = (struct offline_state *)req->async.private;
323         NTSTATUS status = smbcli_request_simple_recv(req);
324         if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE) ||
325             NT_STATUS_EQUAL(status, NT_STATUS_LOCAL_DISCONNECT)) {
326                 talloc_free(state->tree);
327                 state->tree = NULL;
328                 num_connected--;        
329                 DEBUG(0,("lost connection\n"));
330                 test_failed++;
331         }
332 }
333
334 static void report_rate(struct event_context *ev, struct timed_event *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         event_add_timed(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 = &state[i];
385                 req->async.fn      = echo_completion;
386         }
387 }
388
389 /* 
390    test offline file handling
391 */
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                 event_add_timed(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                 event_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                         event_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 }