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