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