0fa9a9e98f21cddb199b0f142dbd66f41f03f378
[rusty/samba.git] / source4 / torture / basic / misc.c
1 /* 
2    Unix SMB/CIFS implementation.
3    SMB torture tester
4    Copyright (C) Andrew Tridgell 1997-2003
5    Copyright (C) Jelmer Vernooij 2006
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "includes.h"
22 #include "libcli/raw/libcliraw.h"
23 #include "libcli/raw/raw_proto.h"
24 #include "system/time.h"
25 #include "system/wait.h"
26 #include "system/filesys.h"
27 #include "libcli/raw/ioctl.h"
28 #include "libcli/libcli.h"
29 #include "lib/events/events.h"
30 #include "libcli/resolve/resolve.h"
31 #include "torture/smbtorture.h"
32 #include "torture/util.h"
33 #include "libcli/smb_composite/smb_composite.h"
34 #include "libcli/composite/composite.h"
35 #include "param/param.h"
36 #include "torture/basic/proto.h"
37
38 extern struct cli_credentials *cmdline_credentials;
39         
40 static bool wait_lock(struct smbcli_state *c, int fnum, uint32_t offset, uint32_t len)
41 {
42         while (NT_STATUS_IS_ERR(smbcli_lock(c->tree, fnum, offset, len, -1, WRITE_LOCK))) {
43                 if (!check_error(__location__, c, ERRDOS, ERRlock, NT_STATUS_LOCK_NOT_GRANTED)) return false;
44         }
45         return true;
46 }
47
48
49 static bool rw_torture(struct torture_context *tctx, struct smbcli_state *c)
50 {
51         const char *lockfname = "\\torture.lck";
52         char *fname;
53         int fnum;
54         int fnum2;
55         pid_t pid2, pid = getpid();
56         int i, j;
57         uint8_t buf[1024];
58         bool correct = true;
59
60         fnum2 = smbcli_open(c->tree, lockfname, O_RDWR | O_CREAT | O_EXCL, 
61                          DENY_NONE);
62         if (fnum2 == -1)
63                 fnum2 = smbcli_open(c->tree, lockfname, O_RDWR, DENY_NONE);
64         if (fnum2 == -1) {
65                 torture_comment(tctx, "open of %s failed (%s)\n", lockfname, smbcli_errstr(c->tree));
66                 return false;
67         }
68
69         generate_random_buffer(buf, sizeof(buf));
70
71         for (i=0;i<torture_numops;i++) {
72                 unsigned int n = (unsigned int)random()%10;
73                 if (i % 10 == 0) {
74                         if (torture_setting_bool(tctx, "progress", true)) {
75                                 torture_comment(tctx, "%d\r", i);
76                                 fflush(stdout);
77                         }
78                 }
79                 asprintf(&fname, "\\torture.%u", n);
80
81                 if (!wait_lock(c, fnum2, n*sizeof(int), sizeof(int))) {
82                         return false;
83                 }
84
85                 fnum = smbcli_open(c->tree, fname, O_RDWR | O_CREAT | O_TRUNC, DENY_ALL);
86                 if (fnum == -1) {
87                         torture_comment(tctx, "open failed (%s)\n", smbcli_errstr(c->tree));
88                         correct = false;
89                         break;
90                 }
91
92                 if (smbcli_write(c->tree, fnum, 0, &pid, 0, sizeof(pid)) != sizeof(pid)) {
93                         torture_comment(tctx, "write failed (%s)\n", smbcli_errstr(c->tree));
94                         correct = false;
95                 }
96
97                 for (j=0;j<50;j++) {
98                         if (smbcli_write(c->tree, fnum, 0, buf, 
99                                       sizeof(pid)+(j*sizeof(buf)), 
100                                       sizeof(buf)) != sizeof(buf)) {
101                                 torture_comment(tctx, "write failed (%s)\n", smbcli_errstr(c->tree));
102                                 correct = false;
103                         }
104                 }
105
106                 pid2 = 0;
107
108                 if (smbcli_read(c->tree, fnum, &pid2, 0, sizeof(pid)) != sizeof(pid)) {
109                         torture_comment(tctx, "read failed (%s)\n", smbcli_errstr(c->tree));
110                         correct = false;
111                 }
112
113                 if (pid2 != pid) {
114                         torture_comment(tctx, "data corruption!\n");
115                         correct = false;
116                 }
117
118                 if (NT_STATUS_IS_ERR(smbcli_close(c->tree, fnum))) {
119                         torture_comment(tctx, "close failed (%s)\n", smbcli_errstr(c->tree));
120                         correct = false;
121                 }
122
123                 if (NT_STATUS_IS_ERR(smbcli_unlink(c->tree, fname))) {
124                         torture_comment(tctx, "unlink failed (%s)\n", smbcli_errstr(c->tree));
125                         correct = false;
126                 }
127
128                 if (NT_STATUS_IS_ERR(smbcli_unlock(c->tree, fnum2, n*sizeof(int), sizeof(int)))) {
129                         torture_comment(tctx, "unlock failed (%s)\n", smbcli_errstr(c->tree));
130                         correct = false;
131                 }
132                 free(fname);
133         }
134
135         smbcli_close(c->tree, fnum2);
136         smbcli_unlink(c->tree, lockfname);
137
138         torture_comment(tctx, "%d\n", i);
139
140         return correct;
141 }
142
143 bool run_torture(struct torture_context *tctx, struct smbcli_state *cli, int dummy)
144 {
145         return rw_torture(tctx, cli);
146 }
147
148
149 /*
150   see how many RPC pipes we can open at once
151 */
152 bool run_pipe_number(struct torture_context *tctx, 
153                                          struct smbcli_state *cli1)
154 {
155         const char *pipe_name = "\\WKSSVC";
156         int fnum;
157         int num_pipes = 0;
158
159         while(1) {
160                 fnum = smbcli_nt_create_full(cli1->tree, pipe_name, 0, SEC_FILE_READ_DATA, FILE_ATTRIBUTE_NORMAL,
161                                    NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE, NTCREATEX_DISP_OPEN_IF, 0, 0);
162
163                 if (fnum == -1) {
164                         torture_comment(tctx, "Open of pipe %s failed with error (%s)\n", pipe_name, smbcli_errstr(cli1->tree));
165                         break;
166                 }
167                 num_pipes++;
168                 if (torture_setting_bool(tctx, "progress", true)) {
169                         torture_comment(tctx, "%d\r", num_pipes);
170                         fflush(stdout);
171                 }
172         }
173
174         torture_comment(tctx, "pipe_number test - we can open %d %s pipes.\n", num_pipes, pipe_name );
175         return true;
176 }
177
178
179
180
181 /*
182   open N connections to the server and just hold them open
183   used for testing performance when there are N idle users
184   already connected
185  */
186 bool torture_holdcon(struct torture_context *tctx)
187 {
188         int i;
189         struct smbcli_state **cli;
190         int num_dead = 0;
191
192         torture_comment(tctx, "Opening %d connections\n", torture_numops);
193         
194         cli = malloc_array_p(struct smbcli_state *, torture_numops);
195
196         for (i=0;i<torture_numops;i++) {
197                 if (!torture_open_connection(&cli[i], tctx, i)) {
198                         return false;
199                 }
200                 if (torture_setting_bool(tctx, "progress", true)) {
201                         torture_comment(tctx, "opened %d connections\r", i);
202                         fflush(stdout);
203                 }
204         }
205
206         torture_comment(tctx, "\nStarting pings\n");
207
208         while (1) {
209                 for (i=0;i<torture_numops;i++) {
210                         NTSTATUS status;
211                         if (cli[i]) {
212                                 status = smbcli_chkpath(cli[i]->tree, "\\");
213                                 if (!NT_STATUS_IS_OK(status)) {
214                                         torture_comment(tctx, "Connection %d is dead\n", i);
215                                         cli[i] = NULL;
216                                         num_dead++;
217                                 }
218                                 usleep(100);
219                         }
220                 }
221
222                 if (num_dead == torture_numops) {
223                         torture_comment(tctx, "All connections dead - finishing\n");
224                         break;
225                 }
226
227                 torture_comment(tctx, ".");
228                 fflush(stdout);
229         }
230
231         return true;
232 }
233
234 /*
235   open a file N times on the server and just hold them open
236   used for testing performance when there are N file handles
237   alopenn
238  */
239 bool torture_holdopen(struct torture_context *tctx,
240                       struct smbcli_state *cli)
241 {
242         int i, fnum;
243         const char *fname = "\\holdopen.dat";
244         NTSTATUS status;
245
246         smbcli_unlink(cli->tree, fname);
247
248         fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
249         if (fnum == -1) {
250                 torture_comment(tctx, "open of %s failed (%s)\n", fname, smbcli_errstr(cli->tree));
251                 return false;
252         }
253
254         smbcli_close(cli->tree, fnum);
255
256         for (i=0;i<torture_numops;i++) {
257                 union smb_open op;
258
259                 op.generic.level = RAW_OPEN_NTCREATEX;
260                 op.ntcreatex.in.root_fid.fnum = 0;
261                 op.ntcreatex.in.flags = 0;
262                 op.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
263                 op.ntcreatex.in.create_options = 0;
264                 op.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
265                 op.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_MASK;
266                 op.ntcreatex.in.alloc_size = 0;
267                 op.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
268                 op.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
269                 op.ntcreatex.in.security_flags = 0;
270                 op.ntcreatex.in.fname = fname;
271                 status = smb_raw_open(cli->tree, tctx, &op);
272                 if (!NT_STATUS_IS_OK(status)) {
273                         torture_warning(tctx, "open %d failed\n", i);
274                         continue;
275                 }
276
277                 if (torture_setting_bool(tctx, "progress", true)) {
278                         torture_comment(tctx, "opened %d file\r", i);
279                         fflush(stdout);
280                 }
281         }
282
283         torture_comment(tctx, "\nStarting pings\n");
284
285         while (1) {
286                 struct smb_echo ec;
287
288                 status = smb_raw_echo(cli->transport, &ec);
289                 torture_comment(tctx, ".");
290                 fflush(stdout);
291                 sleep(15);
292         }
293 }
294
295 /*
296 test how many open files this server supports on the one socket
297 */
298 bool run_maxfidtest(struct torture_context *tctx, struct smbcli_state *cli, int dummy)
299 {
300 #define MAXFID_TEMPLATE "\\maxfid\\fid%d\\maxfid.%d.%d"
301         char *fname;
302         int fnums[0x11000], i;
303         int retries=4, maxfid;
304         bool correct = true;
305
306         if (retries <= 0) {
307                 torture_comment(tctx, "failed to connect\n");
308                 return false;
309         }
310
311         if (smbcli_deltree(cli->tree, "\\maxfid") == -1) {
312                 torture_comment(tctx, "Failed to deltree \\maxfid - %s\n",
313                        smbcli_errstr(cli->tree));
314                 return false;
315         }
316         if (NT_STATUS_IS_ERR(smbcli_mkdir(cli->tree, "\\maxfid"))) {
317                 torture_comment(tctx, "Failed to mkdir \\maxfid, error=%s\n", 
318                        smbcli_errstr(cli->tree));
319                 return false;
320         }
321
322         torture_comment(tctx, "Testing maximum number of open files\n");
323
324         for (i=0; i<0x11000; i++) {
325                 if (i % 1000 == 0) {
326                         asprintf(&fname, "\\maxfid\\fid%d", i/1000);
327                         if (NT_STATUS_IS_ERR(smbcli_mkdir(cli->tree, fname))) {
328                                 torture_comment(tctx, "Failed to mkdir %s, error=%s\n", 
329                                        fname, smbcli_errstr(cli->tree));
330                                 return false;
331                         }
332                         free(fname);
333                 }
334                 asprintf(&fname, MAXFID_TEMPLATE, i/1000, i,(int)getpid());
335                 if ((fnums[i] = smbcli_open(cli->tree, fname, 
336                                         O_RDWR|O_CREAT|O_TRUNC, DENY_NONE)) ==
337                     -1) {
338                         torture_comment(tctx, "open of %s failed (%s)\n", 
339                                fname, smbcli_errstr(cli->tree));
340                         torture_comment(tctx, "maximum fnum is %d\n", i);
341                         break;
342                 }
343                 free(fname);
344                 if (torture_setting_bool(tctx, "progress", true)) {
345                         torture_comment(tctx, "%6d\r", i);
346                         fflush(stdout);
347                 }
348         }
349         torture_comment(tctx, "%6d\n", i);
350         i--;
351
352         maxfid = i;
353
354         torture_comment(tctx, "cleaning up\n");
355         for (i=0;i<maxfid/2;i++) {
356                 asprintf(&fname, MAXFID_TEMPLATE, i/1000, i,(int)getpid());
357                 if (NT_STATUS_IS_ERR(smbcli_close(cli->tree, fnums[i]))) {
358                         torture_comment(tctx, "Close of fnum %d failed - %s\n", fnums[i], smbcli_errstr(cli->tree));
359                 }
360                 if (NT_STATUS_IS_ERR(smbcli_unlink(cli->tree, fname))) {
361                         torture_comment(tctx, "unlink of %s failed (%s)\n", 
362                                fname, smbcli_errstr(cli->tree));
363                         correct = false;
364                 }
365                 free(fname);
366
367                 asprintf(&fname, MAXFID_TEMPLATE, (maxfid-i)/1000, maxfid-i,(int)getpid());
368                 if (NT_STATUS_IS_ERR(smbcli_close(cli->tree, fnums[maxfid-i]))) {
369                         torture_comment(tctx, "Close of fnum %d failed - %s\n", fnums[maxfid-i], smbcli_errstr(cli->tree));
370                 }
371                 if (NT_STATUS_IS_ERR(smbcli_unlink(cli->tree, fname))) {
372                         torture_comment(tctx, "unlink of %s failed (%s)\n", 
373                                fname, smbcli_errstr(cli->tree));
374                         correct = false;
375                 }
376                 free(fname);
377
378                 if (torture_setting_bool(tctx, "progress", true)) {
379                         torture_comment(tctx, "%6d %6d\r", i, maxfid-i);
380                         fflush(stdout);
381                 }
382         }
383         torture_comment(tctx, "%6d\n", 0);
384
385         if (smbcli_deltree(cli->tree, "\\maxfid") == -1) {
386                 torture_comment(tctx, "Failed to deltree \\maxfid - %s\n",
387                        smbcli_errstr(cli->tree));
388                 return false;
389         }
390
391         torture_comment(tctx, "maxfid test finished\n");
392         if (!torture_close_connection(cli)) {
393                 correct = false;
394         }
395         return correct;
396 #undef MAXFID_TEMPLATE
397 }
398
399
400
401 /*
402   sees what IOCTLs are supported
403  */
404 bool torture_ioctl_test(struct torture_context *tctx, 
405                                                 struct smbcli_state *cli)
406 {
407         uint16_t device, function;
408         int fnum;
409         const char *fname = "\\ioctl.dat";
410         NTSTATUS status;
411         union smb_ioctl parms;
412         TALLOC_CTX *mem_ctx;
413
414         mem_ctx = talloc_named_const(tctx, 0, "ioctl_test");
415
416         smbcli_unlink(cli->tree, fname);
417
418         fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
419         if (fnum == -1) {
420                 torture_comment(tctx, "open of %s failed (%s)\n", fname, smbcli_errstr(cli->tree));
421                 return false;
422         }
423
424         parms.ioctl.level = RAW_IOCTL_IOCTL;
425         parms.ioctl.in.file.fnum = fnum;
426         parms.ioctl.in.request = IOCTL_QUERY_JOB_INFO;
427         status = smb_raw_ioctl(cli->tree, mem_ctx, &parms);
428         torture_comment(tctx, "ioctl job info: %s\n", smbcli_errstr(cli->tree));
429
430         for (device=0;device<0x100;device++) {
431                 torture_comment(tctx, "Testing device=0x%x\n", device);
432                 for (function=0;function<0x100;function++) {
433                         parms.ioctl.in.request = (device << 16) | function;
434                         status = smb_raw_ioctl(cli->tree, mem_ctx, &parms);
435
436                         if (NT_STATUS_IS_OK(status)) {
437                                 torture_comment(tctx, "ioctl device=0x%x function=0x%x OK : %d bytes\n", 
438                                         device, function, (int)parms.ioctl.out.blob.length);
439                         }
440                 }
441         }
442
443         return true;
444 }
445
446 static void benchrw_callback(struct smbcli_request *req);
447 enum benchrw_stage {
448         START,
449         OPEN_CONNECTION,
450         CLEANUP_TESTDIR,
451         MK_TESTDIR,
452         OPEN_FILE,
453         INITIAL_WRITE,
454         READ_WRITE_DATA,
455         MAX_OPS_REACHED,
456         ERROR,
457         CLOSE_FILE,
458         CLEANUP,
459         FINISHED
460 };
461
462 struct benchrw_state {
463         struct torture_context *tctx;
464         char *dname;
465         char *fname;
466         uint16_t fnum;
467         int nr;
468         struct smbcli_tree      *cli;           
469         uint8_t *buffer;
470         int writecnt;
471         int readcnt;
472         int completed;
473         int num_parallel_requests;
474         void *req_params;
475         enum benchrw_stage mode;
476         struct params{
477                 struct unclist{
478                         const char *host;
479                         const char *share;
480                 } **unc;
481                 const char *workgroup;
482                 int retry;
483                 unsigned int writeblocks;
484                 unsigned int blocksize;
485                 unsigned int writeratio;
486                 int num_parallel_requests;
487         } *lpcfg_params;
488 };
489
490 /* 
491         init params using lpcfg_parm_xxx
492         return number of unclist entries
493 */
494 static int init_benchrw_params(struct torture_context *tctx,
495                                struct params *lpar)
496 {
497         char **unc_list = NULL;
498         int num_unc_names = 0, conn_index=0, empty_lines=0;
499         const char *p;
500         lpar->retry = torture_setting_int(tctx, "retry",3);
501         lpar->blocksize = torture_setting_int(tctx, "blocksize",65535);
502         lpar->writeblocks = torture_setting_int(tctx, "writeblocks",15);
503         lpar->writeratio = torture_setting_int(tctx, "writeratio",5);
504         lpar->num_parallel_requests = torture_setting_int(
505                 tctx, "parallel_requests", 5);
506         lpar->workgroup = lpcfg_workgroup(tctx->lp_ctx);
507         
508         p = torture_setting_string(tctx, "unclist", NULL);
509         if (p) {
510                 char *h, *s;
511                 unc_list = file_lines_load(p, &num_unc_names, 0, NULL);
512                 if (!unc_list || num_unc_names <= 0) {
513                         torture_comment(tctx, "Failed to load unc names list "
514                                         "from '%s'\n", p);
515                         exit(1);
516                 }
517                 
518                 lpar->unc = talloc_array(tctx, struct unclist *,
519                                          (num_unc_names-empty_lines));
520                 for(conn_index = 0; conn_index < num_unc_names; conn_index++) {
521                         /* ignore empty lines */
522                         if(strlen(unc_list[conn_index % num_unc_names])==0){
523                                 empty_lines++;
524                                 continue;
525                         }
526                         if (!smbcli_parse_unc(
527                                     unc_list[conn_index % num_unc_names],
528                                     NULL, &h, &s)) {
529                                 torture_comment(
530                                         tctx, "Failed to parse UNC "
531                                         "name %s\n",
532                                         unc_list[conn_index % num_unc_names]);
533                                 exit(1);
534                         }
535                         lpar->unc[conn_index-empty_lines] =
536                                 talloc(tctx, struct unclist);
537                         lpar->unc[conn_index-empty_lines]->host = h;
538                         lpar->unc[conn_index-empty_lines]->share = s;   
539                 }
540                 return num_unc_names-empty_lines;
541         }else{
542                 lpar->unc = talloc_array(tctx, struct unclist *, 1);
543                 lpar->unc[0] = talloc(tctx,struct unclist);
544                 lpar->unc[0]->host  = torture_setting_string(tctx, "host",
545                                                              NULL);
546                 lpar->unc[0]->share = torture_setting_string(tctx, "share",
547                                                              NULL);
548                 return 1;
549         }
550 }
551
552 /*
553  Called when the reads & writes are finished. closes the file.
554 */
555 static NTSTATUS benchrw_close(struct torture_context *tctx,
556                               struct smbcli_request *req,
557                               struct benchrw_state *state)
558 {
559         union smb_close close_parms;
560         
561         NT_STATUS_NOT_OK_RETURN(req->status);
562         
563         torture_comment(tctx, "Close file %d (%d)\n",state->nr,state->fnum);
564         close_parms.close.level = RAW_CLOSE_CLOSE;
565         close_parms.close.in.file.fnum = state->fnum ;
566         close_parms.close.in.write_time = 0;
567         state->mode=CLOSE_FILE;
568         
569         req = smb_raw_close_send(state->cli, &close_parms);
570         NT_STATUS_HAVE_NO_MEMORY(req);
571         /*register the callback function!*/
572         req->async.fn = benchrw_callback;
573         req->async.private_data = state;
574         
575         return NT_STATUS_OK;
576 }
577
578 static NTSTATUS benchrw_readwrite(struct torture_context *tctx,
579                                   struct benchrw_state *state);
580 static void benchrw_callback(struct smbcli_request *req);
581
582 static void benchrw_rw_callback(struct smbcli_request *req)
583 {
584         struct benchrw_state *state = req->async.private_data;
585         struct torture_context *tctx = state->tctx;
586
587         if (!NT_STATUS_IS_OK(req->status)) {
588                 state->mode = ERROR;
589                 return;
590         }
591
592         state->completed++;
593         state->num_parallel_requests--;
594
595         if ((state->completed >= torture_numops)
596             && (state->num_parallel_requests == 0)) {
597                 benchrw_callback(req);
598                 talloc_free(req);
599                 return;
600         }
601
602         talloc_free(req);
603
604         if (state->completed + state->num_parallel_requests
605             < torture_numops) {
606                 benchrw_readwrite(tctx, state);
607         }
608 }
609
610 /*
611  Called when the initial write is completed is done. write or read a file.
612 */
613 static NTSTATUS benchrw_readwrite(struct torture_context *tctx,
614                                   struct benchrw_state *state)
615 {
616         struct smbcli_request *req;
617         union smb_read  rd;
618         union smb_write wr;
619         
620         /* randomize between writes and reads*/
621         if (random() % state->lpcfg_params->writeratio == 0) {
622                 torture_comment(tctx, "Callback WRITE file:%d (%d/%d)\n",
623                                 state->nr,state->completed,torture_numops);
624                 wr.generic.level = RAW_WRITE_WRITEX  ;
625                 wr.writex.in.file.fnum  = state->fnum ;
626                 wr.writex.in.offset     = 0;
627                 wr.writex.in.wmode      = 0             ;
628                 wr.writex.in.remaining  = 0;
629                 wr.writex.in.count      = state->lpcfg_params->blocksize;
630                 wr.writex.in.data       = state->buffer;
631                 state->readcnt=0;
632                 req = smb_raw_write_send(state->cli,&wr);
633         }
634         else {
635                 torture_comment(tctx,
636                                 "Callback READ file:%d (%d/%d) Offset:%d\n",
637                                 state->nr,state->completed,torture_numops,
638                                 (state->readcnt*state->lpcfg_params->blocksize));
639                 rd.generic.level = RAW_READ_READX;
640                 rd.readx.in.file.fnum   = state->fnum   ;
641                 rd.readx.in.offset      = state->readcnt*state->lpcfg_params->blocksize;
642                 rd.readx.in.mincnt      = state->lpcfg_params->blocksize;
643                 rd.readx.in.maxcnt      = rd.readx.in.mincnt;
644                 rd.readx.in.remaining   = 0     ;
645                 rd.readx.out.data       = state->buffer;
646                 rd.readx.in.read_for_execute = false;
647                 if(state->readcnt < state->lpcfg_params->writeblocks){
648                         state->readcnt++;       
649                 }else{
650                         /*start reading from beginn of file*/
651                         state->readcnt=0;
652                 }
653                 req = smb_raw_read_send(state->cli,&rd);
654         }
655         state->num_parallel_requests += 1;
656         NT_STATUS_HAVE_NO_MEMORY(req);
657         /*register the callback function!*/
658         req->async.fn = benchrw_rw_callback;
659         req->async.private_data = state;
660         
661         return NT_STATUS_OK;
662 }
663
664 /*
665  Called when the open is done. writes to the file.
666 */
667 static NTSTATUS benchrw_open(struct torture_context *tctx,
668                              struct smbcli_request *req,
669                              struct benchrw_state *state)
670 {
671         union smb_write wr;
672         if(state->mode == OPEN_FILE){
673                 NTSTATUS status;
674                 status = smb_raw_open_recv(req,tctx,(
675                                         union smb_open*)state->req_params);
676                 NT_STATUS_NOT_OK_RETURN(status);
677         
678                 state->fnum = ((union smb_open*)state->req_params)
679                                                 ->openx.out.file.fnum;
680                 torture_comment(tctx, "File opened (%d)\n",state->fnum);
681                 state->mode=INITIAL_WRITE;
682         }
683                 
684         torture_comment(tctx, "Write initial test file:%d (%d/%d)\n",state->nr,
685                 (state->writecnt+1)*state->lpcfg_params->blocksize,
686                 (state->lpcfg_params->writeblocks*state->lpcfg_params->blocksize));
687         wr.generic.level = RAW_WRITE_WRITEX  ;
688         wr.writex.in.file.fnum  = state->fnum ;
689         wr.writex.in.offset     = state->writecnt * 
690                                         state->lpcfg_params->blocksize;
691         wr.writex.in.wmode      = 0             ;
692         wr.writex.in.remaining  = (state->lpcfg_params->writeblocks *
693                                                 state->lpcfg_params->blocksize)-
694                                                 ((state->writecnt+1)*state->
695                                                 lpcfg_params->blocksize);
696         wr.writex.in.count      = state->lpcfg_params->blocksize;
697         wr.writex.in.data       = state->buffer;
698         state->writecnt++;
699         if(state->writecnt == state->lpcfg_params->writeblocks){
700                 state->mode=READ_WRITE_DATA;
701         }
702         req = smb_raw_write_send(state->cli,&wr);
703         NT_STATUS_HAVE_NO_MEMORY(req);
704         
705         /*register the callback function!*/
706         req->async.fn = benchrw_callback;
707         req->async.private_data = state;
708         return NT_STATUS_OK;
709
710
711 /*
712  Called when the mkdir is done. Opens a file.
713 */
714 static NTSTATUS benchrw_mkdir(struct torture_context *tctx,
715                               struct smbcli_request *req,
716                               struct benchrw_state *state)
717 {
718         union smb_open *open_parms;     
719         uint8_t *writedata;     
720                 
721         NT_STATUS_NOT_OK_RETURN(req->status);
722         
723         /* open/create the files */
724         torture_comment(tctx, "Open File %d/%d\n",state->nr+1,
725                         torture_setting_int(tctx, "nprocs", 4));
726         open_parms=talloc_zero(tctx, union smb_open);
727         NT_STATUS_HAVE_NO_MEMORY(open_parms);
728         open_parms->openx.level = RAW_OPEN_OPENX;
729         open_parms->openx.in.flags = 0;
730         open_parms->openx.in.open_mode = OPENX_MODE_ACCESS_RDWR;
731         open_parms->openx.in.search_attrs = 
732                         FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN;
733         open_parms->openx.in.file_attrs = 0;
734         open_parms->openx.in.write_time = 0;
735         open_parms->openx.in.open_func = OPENX_OPEN_FUNC_CREATE;
736         open_parms->openx.in.size = 0;
737         open_parms->openx.in.timeout = 0;
738         open_parms->openx.in.fname = state->fname;
739                 
740         writedata = talloc_size(tctx,state->lpcfg_params->blocksize);
741         NT_STATUS_HAVE_NO_MEMORY(writedata);
742         generate_random_buffer(writedata,state->lpcfg_params->blocksize);
743         state->buffer=writedata;
744         state->writecnt=1;
745         state->readcnt=0;
746         state->req_params=open_parms;           
747         state->mode=OPEN_FILE;  
748                         
749         req = smb_raw_open_send(state->cli,open_parms);
750         NT_STATUS_HAVE_NO_MEMORY(req);
751         
752         /*register the callback function!*/
753         req->async.fn = benchrw_callback;
754         req->async.private_data = state;
755                 
756         return NT_STATUS_OK;
757 }
758
759 /*
760  handler for completion of a sub-request of the bench-rw test
761 */
762 static void benchrw_callback(struct smbcli_request *req)
763 {
764         struct benchrw_state *state = req->async.private_data;
765         struct torture_context *tctx = state->tctx;
766         
767         /*dont send new requests when torture_numops is reached*/
768         if ((state->mode == READ_WRITE_DATA)
769             && (state->completed >= torture_numops)) {
770                 state->mode=MAX_OPS_REACHED;
771         }
772         
773         switch (state->mode) {
774         
775         case MK_TESTDIR:
776                 if (!NT_STATUS_IS_OK(benchrw_mkdir(tctx, req,state))) {
777                         torture_comment(tctx, "Failed to create the test "
778                                         "directory - %s\n", 
779                                         nt_errstr(req->status));
780                         state->mode=ERROR;
781                         return;
782                 }
783                 break;  
784         case OPEN_FILE:
785         case INITIAL_WRITE:
786                 if (!NT_STATUS_IS_OK(benchrw_open(tctx, req,state))){
787                         torture_comment(tctx, "Failed to open/write the "
788                                         "file - %s\n", 
789                                         nt_errstr(req->status));
790                         state->mode=ERROR;
791                         state->readcnt=0;
792                         return;
793                 }
794                 break;
795         case READ_WRITE_DATA:
796                 while (state->num_parallel_requests
797                        < state->lpcfg_params->num_parallel_requests) {
798                         NTSTATUS status;
799                         status = benchrw_readwrite(tctx,state);
800                         if (!NT_STATUS_IS_OK(status)){
801                                 torture_comment(tctx, "Failed to read/write "
802                                                 "the file - %s\n", 
803                                                 nt_errstr(req->status));
804                                 state->mode=ERROR;
805                                 return;
806                         }
807                 }
808                 break;
809         case MAX_OPS_REACHED:
810                 if (!NT_STATUS_IS_OK(benchrw_close(tctx,req,state))){
811                         torture_comment(tctx, "Failed to read/write/close "
812                                         "the file - %s\n", 
813                                         nt_errstr(req->status));
814                         state->mode=ERROR;
815                         return;
816                 }
817                 break;
818         case CLOSE_FILE:
819                 torture_comment(tctx, "File %d closed\n",state->nr);
820                 if (!NT_STATUS_IS_OK(req->status)) {
821                         torture_comment(tctx, "Failed to close the "
822                                         "file - %s\n",
823                                         nt_errstr(req->status));
824                         state->mode=ERROR;
825                         return;
826                 }
827                 state->mode=CLEANUP;
828                 return; 
829         default:
830                 break;
831         }
832         
833 }
834
835 /* open connection async callback function*/
836 static void async_open_callback(struct composite_context *con)
837 {
838         struct benchrw_state *state = con->async.private_data;
839         struct torture_context *tctx = state->tctx;
840         int retry = state->lpcfg_params->retry;
841                 
842         if (NT_STATUS_IS_OK(con->status)) {
843                 state->cli=((struct smb_composite_connect*)
844                                         state->req_params)->out.tree;
845                 state->mode=CLEANUP_TESTDIR;
846         }else{
847                 if(state->writecnt < retry){
848                         torture_comment(tctx, "Failed to open connection: "
849                                         "%d, Retry (%d/%d)\n",
850                                         state->nr,state->writecnt,retry);
851                         state->writecnt++;
852                         state->mode=START;
853                         usleep(1000);   
854                 }else{
855                         torture_comment(tctx, "Failed to open connection "
856                                         "(%d) - %s\n",
857                                         state->nr, nt_errstr(con->status));
858                         state->mode=ERROR;
859                 }
860                 return;
861         }       
862 }
863
864 /*
865  establishs a smbcli_tree from scratch (async)
866 */
867 static struct composite_context *torture_connect_async(
868                                 struct torture_context *tctx,
869                                 struct smb_composite_connect *smb,
870                                 TALLOC_CTX *mem_ctx,
871                                 struct tevent_context *ev,
872                                 const char *host,
873                                 const char *share,
874                                 const char *workgroup)
875 {
876         torture_comment(tctx, "Open Connection to %s/%s\n",host,share);
877         smb->in.dest_host=talloc_strdup(mem_ctx,host);
878         smb->in.service=talloc_strdup(mem_ctx,share);
879         smb->in.dest_ports=lpcfg_smb_ports(tctx->lp_ctx);
880         smb->in.socket_options = lpcfg_socket_options(tctx->lp_ctx);
881         smb->in.called_name = strupper_talloc(mem_ctx, host);
882         smb->in.service_type=NULL;
883         smb->in.credentials=cmdline_credentials;
884         smb->in.fallback_to_anonymous=false;
885         smb->in.gensec_settings = lpcfg_gensec_settings(mem_ctx, tctx->lp_ctx);
886         smb->in.workgroup=workgroup;
887         lpcfg_smbcli_options(tctx->lp_ctx, &smb->in.options);
888         lpcfg_smbcli_session_options(tctx->lp_ctx, &smb->in.session_options);
889         
890         return smb_composite_connect_send(smb,mem_ctx,
891                                           lpcfg_resolve_context(tctx->lp_ctx),ev);
892 }
893
894 bool run_benchrw(struct torture_context *tctx)
895 {
896         struct smb_composite_connect *smb_con;
897         const char *fname = "\\rwtest.dat";
898         struct smbcli_request *req;
899         struct benchrw_state **state;
900         int i , num_unc_names;
901         struct tevent_context   *ev     ;       
902         struct composite_context *req1;
903         struct params lpparams;
904         union smb_mkdir parms;
905         int finished = 0;
906         bool success=true;
907         int torture_nprocs = torture_setting_int(tctx, "nprocs", 4);
908         
909         torture_comment(tctx, "Start BENCH-READWRITE num_ops=%d "
910                         "num_nprocs=%d\n",
911                         torture_numops, torture_nprocs);
912
913         /*init talloc context*/
914         ev = tctx->ev;
915         state = talloc_array(tctx, struct benchrw_state *, torture_nprocs);
916
917         /* init params using lpcfg_parm_xxx */
918         num_unc_names = init_benchrw_params(tctx,&lpparams);
919         
920         /* init private data structs*/
921         for(i = 0; i<torture_nprocs;i++){
922                 state[i]=talloc(tctx,struct benchrw_state);
923                 state[i]->tctx = tctx;
924                 state[i]->completed=0;
925                 state[i]->num_parallel_requests=0;
926                 state[i]->lpcfg_params=&lpparams;
927                 state[i]->nr=i;
928                 state[i]->dname=talloc_asprintf(tctx,"benchrw%d",i);
929                 state[i]->fname=talloc_asprintf(tctx,"%s%s",
930                                                 state[i]->dname,fname); 
931                 state[i]->mode=START;
932                 state[i]->writecnt=0;
933         }
934         
935         torture_comment(tctx, "Starting async requests\n");     
936         while(finished != torture_nprocs){
937                 finished=0;
938                 for(i = 0; i<torture_nprocs;i++){
939                         switch (state[i]->mode){
940                         /*open multiple connections with the same userid */
941                         case START:
942                                 smb_con = talloc(
943                                         tctx,struct smb_composite_connect) ;
944                                 state[i]->req_params=smb_con; 
945                                 state[i]->mode=OPEN_CONNECTION;
946                                 req1 = torture_connect_async(
947                                         tctx, smb_con, tctx,ev,
948                                         lpparams.unc[i % num_unc_names]->host,
949                                         lpparams.unc[i % num_unc_names]->share,
950                                         lpparams.workgroup);
951                                 /* register callback fn + private data */
952                                 req1->async.fn = async_open_callback;
953                                 req1->async.private_data=state[i];
954                                 break;
955                         /*setup test dirs (sync)*/
956                         case CLEANUP_TESTDIR:
957                                 torture_comment(tctx, "Setup test dir %d\n",i);
958                                 smb_raw_exit(state[i]->cli->session);
959                                 if (smbcli_deltree(state[i]->cli, 
960                                                 state[i]->dname) == -1) {
961                                         torture_comment(
962                                                 tctx,
963                                                 "Unable to delete %s - %s\n", 
964                                                 state[i]->dname,
965                                                 smbcli_errstr(state[i]->cli));
966                                         state[i]->mode=ERROR;
967                                         break;
968                                 }
969                                 state[i]->mode=MK_TESTDIR;
970                                 parms.mkdir.level = RAW_MKDIR_MKDIR;
971                                 parms.mkdir.in.path = state[i]->dname;
972                                 req = smb_raw_mkdir_send(state[i]->cli,&parms);
973                                 /* register callback fn + private data */
974                                 req->async.fn = benchrw_callback;
975                                 req->async.private_data=state[i];
976                                 break;
977                         /* error occured , finish */
978                         case ERROR:
979                                 finished++;
980                                 success=false;
981                                 break;
982                         /* cleanup , close connection */
983                         case CLEANUP:
984                                 torture_comment(tctx, "Deleting test dir %s "
985                                                 "%d/%d\n",state[i]->dname,
986                                                 i+1,torture_nprocs);
987                                 smbcli_deltree(state[i]->cli,state[i]->dname);
988                                 if (NT_STATUS_IS_ERR(smb_tree_disconnect(
989                                                              state[i]->cli))) {
990                                         torture_comment(tctx, "ERROR: Tree "
991                                                         "disconnect failed");
992                                         state[i]->mode=ERROR;
993                                         break;
994                                 }
995                                 state[i]->mode=FINISHED;
996                         case FINISHED:
997                                 finished++;
998                                 break;
999                         default:
1000                                 tevent_loop_once(ev);
1001                         }
1002                 }
1003         }
1004                                 
1005         return success; 
1006 }
1007