summaryrefslogtreecommitdiffstats
path: root/source4/torture/basic/misc.c
diff options
context:
space:
mode:
Diffstat (limited to 'source4/torture/basic/misc.c')
-rw-r--r--source4/torture/basic/misc.c855
1 files changed, 855 insertions, 0 deletions
diff --git a/source4/torture/basic/misc.c b/source4/torture/basic/misc.c
new file mode 100644
index 00000000000..c00372b805f
--- /dev/null
+++ b/source4/torture/basic/misc.c
@@ -0,0 +1,855 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB torture tester
+ Copyright (C) Andrew Tridgell 1997-2003
+ Copyright (C) Jelmer Vernooij 2006
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "system/time.h"
+#include "system/wait.h"
+#include "system/filesys.h"
+#include "libcli/raw/ioctl.h"
+#include "libcli/libcli.h"
+#include "lib/events/events.h"
+#include "libcli/resolve/resolve.h"
+#include "auth/credentials/credentials.h"
+#include "librpc/gen_ndr/ndr_nbt.h"
+#include "torture/torture.h"
+#include "torture/util.h"
+#include "libcli/smb_composite/smb_composite.h"
+#include "libcli/composite/composite.h"
+
+extern struct cli_credentials *cmdline_credentials;
+static void benchrw_callback(struct smbcli_request *req);
+enum benchrw_stage {
+ START,
+ OPEN_CONNECTION,
+ CLEANUP_TESTDIR,
+ MK_TESTDIR,
+ OPEN_FILE,
+ INITIAL_WRITE,
+ READ_WRITE_DATA,
+ MAX_OPS_REACHED,
+ ERROR,
+ CLOSE_FILE,
+ CLEANUP,
+ FINISHED
+};
+
+struct benchrw_state{
+ struct torture_context *tctx;
+ char *dname;
+ char *fname;
+ uint16_t fnum;
+ int nr;
+ struct smbcli_tree *cli;
+ uint8_t *buffer;
+ int writecnt;
+ int readcnt;
+ int completed;
+ TALLOC_CTX *mem_ctx;
+ void *req_params;
+ enum benchrw_stage mode;
+ struct params{
+ struct unclist{
+ const char *host;
+ const char *share;
+ } **unc;
+ const char *workgroup;
+ int retry;
+ unsigned int writeblocks;
+ unsigned int blocksize;
+ unsigned int writeratio;
+ } *lp_params;
+ };
+
+static BOOL wait_lock(struct smbcli_state *c, int fnum, uint32_t offset, uint32_t len)
+{
+ while (NT_STATUS_IS_ERR(smbcli_lock(c->tree, fnum, offset, len, -1, WRITE_LOCK))) {
+ if (!check_error(__location__, c, ERRDOS, ERRlock, NT_STATUS_LOCK_NOT_GRANTED)) return False;
+ }
+ return True;
+}
+
+
+static BOOL rw_torture(struct torture_context *tctx, struct smbcli_state *c)
+{
+ const char *lockfname = "\\torture.lck";
+ char *fname;
+ int fnum;
+ int fnum2;
+ pid_t pid2, pid = getpid();
+ int i, j;
+ uint8_t buf[1024];
+ BOOL correct = True;
+
+ fnum2 = smbcli_open(c->tree, lockfname, O_RDWR | O_CREAT | O_EXCL,
+ DENY_NONE);
+ if (fnum2 == -1)
+ fnum2 = smbcli_open(c->tree, lockfname, O_RDWR, DENY_NONE);
+ if (fnum2 == -1) {
+ torture_comment(tctx, "open of %s failed (%s)\n", lockfname, smbcli_errstr(c->tree));
+ return False;
+ }
+
+
+ for (i=0;i<torture_numops;i++) {
+ uint_t n = (uint_t)random()%10;
+ if (i % 10 == 0) {
+ torture_comment(tctx, "%d\r", i); fflush(stdout);
+ }
+ asprintf(&fname, "\\torture.%u", n);
+
+ if (!wait_lock(c, fnum2, n*sizeof(int), sizeof(int))) {
+ return False;
+ }
+
+ fnum = smbcli_open(c->tree, fname, O_RDWR | O_CREAT | O_TRUNC, DENY_ALL);
+ if (fnum == -1) {
+ torture_comment(tctx, "open failed (%s)\n", smbcli_errstr(c->tree));
+ correct = False;
+ break;
+ }
+
+ if (smbcli_write(c->tree, fnum, 0, &pid, 0, sizeof(pid)) != sizeof(pid)) {
+ torture_comment(tctx, "write failed (%s)\n", smbcli_errstr(c->tree));
+ correct = False;
+ }
+
+ for (j=0;j<50;j++) {
+ if (smbcli_write(c->tree, fnum, 0, buf,
+ sizeof(pid)+(j*sizeof(buf)),
+ sizeof(buf)) != sizeof(buf)) {
+ torture_comment(tctx, "write failed (%s)\n", smbcli_errstr(c->tree));
+ correct = False;
+ }
+ }
+
+ pid2 = 0;
+
+ if (smbcli_read(c->tree, fnum, &pid2, 0, sizeof(pid)) != sizeof(pid)) {
+ torture_comment(tctx, "read failed (%s)\n", smbcli_errstr(c->tree));
+ correct = False;
+ }
+
+ if (pid2 != pid) {
+ torture_comment(tctx, "data corruption!\n");
+ correct = False;
+ }
+
+ if (NT_STATUS_IS_ERR(smbcli_close(c->tree, fnum))) {
+ torture_comment(tctx, "close failed (%s)\n", smbcli_errstr(c->tree));
+ correct = False;
+ }
+
+ if (NT_STATUS_IS_ERR(smbcli_unlink(c->tree, fname))) {
+ torture_comment(tctx, "unlink failed (%s)\n", smbcli_errstr(c->tree));
+ correct = False;
+ }
+
+ if (NT_STATUS_IS_ERR(smbcli_unlock(c->tree, fnum2, n*sizeof(int), sizeof(int)))) {
+ torture_comment(tctx, "unlock failed (%s)\n", smbcli_errstr(c->tree));
+ correct = False;
+ }
+ free(fname);
+ }
+
+ smbcli_close(c->tree, fnum2);
+ smbcli_unlink(c->tree, lockfname);
+
+ torture_comment(tctx, "%d\n", i);
+
+ return correct;
+}
+
+BOOL run_torture(struct torture_context *tctx, struct smbcli_state *cli, int dummy)
+{
+ return rw_torture(tctx, cli);
+}
+
+
+/*
+ see how many RPC pipes we can open at once
+*/
+BOOL run_pipe_number(struct torture_context *tctx,
+ struct smbcli_state *cli1)
+{
+ const char *pipe_name = "\\WKSSVC";
+ int fnum;
+ int num_pipes = 0;
+
+ while(1) {
+ fnum = smbcli_nt_create_full(cli1->tree, pipe_name, 0, SEC_FILE_READ_DATA, FILE_ATTRIBUTE_NORMAL,
+ NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE, NTCREATEX_DISP_OPEN_IF, 0, 0);
+
+ if (fnum == -1) {
+ torture_comment(tctx, "Open of pipe %s failed with error (%s)\n", pipe_name, smbcli_errstr(cli1->tree));
+ break;
+ }
+ num_pipes++;
+ torture_comment(tctx, "%d\r", num_pipes);
+ fflush(stdout);
+ }
+
+ torture_comment(tctx, "pipe_number test - we can open %d %s pipes.\n", num_pipes, pipe_name );
+ return True;
+}
+
+
+
+
+/*
+ open N connections to the server and just hold them open
+ used for testing performance when there are N idle users
+ already connected
+ */
+BOOL torture_holdcon(struct torture_context *tctx)
+{
+ int i;
+ struct smbcli_state **cli;
+ int num_dead = 0;
+
+ torture_comment(tctx, "Opening %d connections\n", torture_numops);
+
+ cli = malloc_array_p(struct smbcli_state *, torture_numops);
+
+ for (i=0;i<torture_numops;i++) {
+ if (!torture_open_connection(&cli[i], i)) {
+ return False;
+ }
+ torture_comment(tctx, "opened %d connections\r", i);
+ fflush(stdout);
+ }
+
+ torture_comment(tctx, "\nStarting pings\n");
+
+ while (1) {
+ for (i=0;i<torture_numops;i++) {
+ NTSTATUS status;
+ if (cli[i]) {
+ status = smbcli_chkpath(cli[i]->tree, "\\");
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "Connection %d is dead\n", i);
+ cli[i] = NULL;
+ num_dead++;
+ }
+ usleep(100);
+ }
+ }
+
+ if (num_dead == torture_numops) {
+ torture_comment(tctx, "All connections dead - finishing\n");
+ break;
+ }
+
+ torture_comment(tctx, ".");
+ fflush(stdout);
+ }
+
+ return True;
+}
+
+/*
+test how many open files this server supports on the one socket
+*/
+BOOL run_maxfidtest(struct torture_context *tctx, struct smbcli_state *cli, int dummy)
+{
+#define MAXFID_TEMPLATE "\\maxfid\\fid%d\\maxfid.%d.%d"
+ char *fname;
+ int fnums[0x11000], i;
+ int retries=4, maxfid;
+ BOOL correct = True;
+
+ if (retries <= 0) {
+ torture_comment(tctx, "failed to connect\n");
+ return False;
+ }
+
+ if (smbcli_deltree(cli->tree, "\\maxfid") == -1) {
+ torture_comment(tctx, "Failed to deltree \\maxfid - %s\n",
+ smbcli_errstr(cli->tree));
+ return False;
+ }
+ if (NT_STATUS_IS_ERR(smbcli_mkdir(cli->tree, "\\maxfid"))) {
+ torture_comment(tctx, "Failed to mkdir \\maxfid, error=%s\n",
+ smbcli_errstr(cli->tree));
+ return False;
+ }
+
+ torture_comment(tctx, "Testing maximum number of open files\n");
+
+ for (i=0; i<0x11000; i++) {
+ if (i % 1000 == 0) {
+ asprintf(&fname, "\\maxfid\\fid%d", i/1000);
+ if (NT_STATUS_IS_ERR(smbcli_mkdir(cli->tree, fname))) {
+ torture_comment(tctx, "Failed to mkdir %s, error=%s\n",
+ fname, smbcli_errstr(cli->tree));
+ return False;
+ }
+ free(fname);
+ }
+ asprintf(&fname, MAXFID_TEMPLATE, i/1000, i,(int)getpid());
+ if ((fnums[i] = smbcli_open(cli->tree, fname,
+ O_RDWR|O_CREAT|O_TRUNC, DENY_NONE)) ==
+ -1) {
+ torture_comment(tctx, "open of %s failed (%s)\n",
+ fname, smbcli_errstr(cli->tree));
+ torture_comment(tctx, "maximum fnum is %d\n", i);
+ break;
+ }
+ free(fname);
+ torture_comment(tctx, "%6d\r", i);
+ }
+ torture_comment(tctx, "%6d\n", i);
+ i--;
+
+ maxfid = i;
+
+ torture_comment(tctx, "cleaning up\n");
+ for (i=0;i<maxfid/2;i++) {
+ asprintf(&fname, MAXFID_TEMPLATE, i/1000, i,(int)getpid());
+ if (NT_STATUS_IS_ERR(smbcli_close(cli->tree, fnums[i]))) {
+ torture_comment(tctx, "Close of fnum %d failed - %s\n", fnums[i], smbcli_errstr(cli->tree));
+ }
+ if (NT_STATUS_IS_ERR(smbcli_unlink(cli->tree, fname))) {
+ torture_comment(tctx, "unlink of %s failed (%s)\n",
+ fname, smbcli_errstr(cli->tree));
+ correct = False;
+ }
+ free(fname);
+
+ asprintf(&fname, MAXFID_TEMPLATE, (maxfid-i)/1000, maxfid-i,(int)getpid());
+ if (NT_STATUS_IS_ERR(smbcli_close(cli->tree, fnums[maxfid-i]))) {
+ torture_comment(tctx, "Close of fnum %d failed - %s\n", fnums[maxfid-i], smbcli_errstr(cli->tree));
+ }
+ if (NT_STATUS_IS_ERR(smbcli_unlink(cli->tree, fname))) {
+ torture_comment(tctx, "unlink of %s failed (%s)\n",
+ fname, smbcli_errstr(cli->tree));
+ correct = False;
+ }
+ free(fname);
+
+ torture_comment(tctx, "%6d %6d\r", i, maxfid-i);
+ }
+ torture_comment(tctx, "%6d\n", 0);
+
+ if (smbcli_deltree(cli->tree, "\\maxfid") == -1) {
+ torture_comment(tctx, "Failed to deltree \\maxfid - %s\n",
+ smbcli_errstr(cli->tree));
+ return False;
+ }
+
+ torture_comment(tctx, "maxfid test finished\n");
+ if (!torture_close_connection(cli)) {
+ correct = False;
+ }
+ return correct;
+#undef MAXFID_TEMPLATE
+}
+
+
+
+/*
+ sees what IOCTLs are supported
+ */
+BOOL torture_ioctl_test(struct torture_context *tctx,
+ struct smbcli_state *cli)
+{
+ uint16_t device, function;
+ int fnum;
+ const char *fname = "\\ioctl.dat";
+ NTSTATUS status;
+ union smb_ioctl parms;
+ TALLOC_CTX *mem_ctx;
+
+ mem_ctx = talloc_named_const(tctx, 0, "ioctl_test");
+
+ smbcli_unlink(cli->tree, fname);
+
+ fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+ if (fnum == -1) {
+ torture_comment(tctx, "open of %s failed (%s)\n", fname, smbcli_errstr(cli->tree));
+ return False;
+ }
+
+ parms.ioctl.level = RAW_IOCTL_IOCTL;
+ parms.ioctl.in.file.fnum = fnum;
+ parms.ioctl.in.request = IOCTL_QUERY_JOB_INFO;
+ status = smb_raw_ioctl(cli->tree, mem_ctx, &parms);
+ torture_comment(tctx, "ioctl job info: %s\n", smbcli_errstr(cli->tree));
+
+ for (device=0;device<0x100;device++) {
+ torture_comment(tctx, "testing device=0x%x\n", device);
+ for (function=0;function<0x100;function++) {
+ parms.ioctl.in.request = (device << 16) | function;
+ status = smb_raw_ioctl(cli->tree, mem_ctx, &parms);
+
+ if (NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "ioctl device=0x%x function=0x%x OK : %d bytes\n",
+ device, function, (int)parms.ioctl.out.blob.length);
+ }
+ }
+ }
+
+ return True;
+}
+
+/*
+ init params using lp_parm_xxx
+ return number of unclist entries
+*/
+static int init_benchrw_params(struct torture_context *tctx, struct params *lpar)
+{
+ char **unc_list = NULL;
+ int num_unc_names = 0, conn_index=0, empty_lines=0;
+ const char *p;
+ lpar->retry = lp_parm_int(-1, "torture", "retry",3);
+ lpar->blocksize = lp_parm_int(-1, "torture", "blocksize",65535);
+ lpar->writeblocks = lp_parm_int(-1, "torture", "writeblocks",15);
+ lpar->writeratio = lp_parm_int(-1, "torture", "writeratio",5);
+ lpar->workgroup = lp_workgroup();
+
+ p = lp_parm_string(-1, "torture", "unclist");
+ if (p) {
+ char *h, *s;
+ unc_list = file_lines_load(p, &num_unc_names, NULL);
+ if (!unc_list || num_unc_names <= 0) {
+ torture_comment(tctx, "Failed to load unc names list from '%s'\n", p);
+ exit(1);
+ }
+
+ lpar->unc = talloc_array(tctx, struct unclist *, (num_unc_names-empty_lines));
+ for(conn_index = 0; conn_index < num_unc_names; conn_index++) {
+ /* ignore empty lines */
+ if(strlen(unc_list[conn_index % num_unc_names])==0){
+ empty_lines++;
+ continue;
+ }
+ if (!smbcli_parse_unc(unc_list[conn_index % num_unc_names],
+ NULL, &h, &s)) {
+ torture_comment(tctx, "Failed to parse UNC name %s\n",
+ unc_list[conn_index % num_unc_names]);
+ exit(1);
+ }
+ lpar->unc[conn_index-empty_lines] = talloc(tctx,struct unclist);
+ lpar->unc[conn_index-empty_lines]->host = h;
+ lpar->unc[conn_index-empty_lines]->share = s;
+ }
+ return num_unc_names-empty_lines;
+ }else{
+ lpar->unc = talloc_array(tctx, struct unclist *, 1);
+ lpar->unc[0] = talloc(tctx,struct unclist);
+ lpar->unc[0]->host = lp_parm_string(-1, "torture", "host");
+ lpar->unc[0]->share = lp_parm_string(-1, "torture", "share");
+ return 1;
+ }
+}
+
+/*
+ Called when the reads & writes are finished. closes the file.
+*/
+static NTSTATUS benchrw_close(struct torture_context *tctx,struct smbcli_request *req,
+ struct benchrw_state *state)
+{
+ union smb_close close_parms;
+
+ NT_STATUS_NOT_OK_RETURN(req->status);
+
+ torture_comment(tctx, "Close file %d (%d)\n",state->nr,state->fnum);
+ close_parms.close.level = RAW_CLOSE_CLOSE;
+ close_parms.close.in.file.fnum = state->fnum ;
+ close_parms.close.in.write_time = 0;
+ state->mode=CLOSE_FILE;
+
+ req = smb_raw_close_send(state->cli, &close_parms);
+ NT_STATUS_HAVE_NO_MEMORY(req);
+ /*register the callback function!*/
+ req->async.fn = benchrw_callback;
+ req->async.private = state;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ Called when the initial write is completed is done. write or read a file.
+*/
+static NTSTATUS benchrw_readwrite(struct torture_context *tctx,struct smbcli_request *req,
+ struct benchrw_state *state)
+{
+ union smb_read rd;
+ union smb_write wr;
+
+ NT_STATUS_NOT_OK_RETURN(req->status);
+
+ state->completed++;
+ /*rotate between writes and reads*/
+ if( state->completed % state->lp_params->writeratio == 0){
+ torture_comment(tctx, "Callback WRITE file:%d (%d/%d)\n",
+ state->nr,state->completed,torture_numops);
+ wr.generic.level = RAW_WRITE_WRITEX ;
+ wr.writex.in.file.fnum = state->fnum ;
+ wr.writex.in.offset = 0;
+ wr.writex.in.wmode = 0 ;
+ wr.writex.in.remaining = 0;
+ wr.writex.in.count = state->lp_params->blocksize;
+ wr.writex.in.data = state->buffer;
+ state->readcnt=0;
+ req = smb_raw_write_send(state->cli,&wr);
+ }else{
+ torture_comment(tctx, "Callback READ file:%d (%d/%d) Offset:%d\n",
+ state->nr,state->completed,torture_numops,
+ (state->readcnt*state->lp_params->blocksize));
+ rd.generic.level = RAW_READ_READ ;
+ rd.read.in.file.fnum = state->fnum ;
+ rd.read.in.offset = state->readcnt *
+ state->lp_params->blocksize;
+ rd.read.in.count = state->lp_params->blocksize;
+ rd.read.in.remaining = 0 ;
+ rd.read.out.data = state->buffer;
+ if(state->readcnt < state->lp_params->writeblocks){
+ state->readcnt++;
+ }else{
+ /*start reading from beginn of file*/
+ state->readcnt=0;
+ }
+ req = smb_raw_read_send(state->cli,&rd);
+ }
+ NT_STATUS_HAVE_NO_MEMORY(req);
+ /*register the callback function!*/
+ req->async.fn = benchrw_callback;
+ req->async.private = state;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ Called when the open is done. writes to the file.
+*/
+static NTSTATUS benchrw_open(struct torture_context *tctx,struct smbcli_request *req,
+ struct benchrw_state *state)
+{
+ union smb_write wr;
+ if(state->mode == OPEN_FILE){
+ NTSTATUS status;
+ status = smb_raw_open_recv(req,state->mem_ctx,(
+ union smb_open*)state->req_params);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ state->fnum = ((union smb_open*)state->req_params)
+ ->openx.out.file.fnum;
+ torture_comment(tctx, "File opened (%d)\n",state->fnum);
+ state->mode=INITIAL_WRITE;
+ }
+
+ torture_comment(tctx, "Write initial test file:%d (%d/%d)\n",state->nr,
+ (state->writecnt+1)*state->lp_params->blocksize,
+ (state->lp_params->writeblocks*state->lp_params->blocksize));
+ wr.generic.level = RAW_WRITE_WRITEX ;
+ wr.writex.in.file.fnum = state->fnum ;
+ wr.writex.in.offset = state->writecnt *
+ state->lp_params->blocksize;
+ wr.writex.in.wmode = 0 ;
+ wr.writex.in.remaining = (state->lp_params->writeblocks *
+ state->lp_params->blocksize)-
+ ((state->writecnt+1)*state->
+ lp_params->blocksize);
+ wr.writex.in.count = state->lp_params->blocksize;
+ wr.writex.in.data = state->buffer;
+ state->writecnt++;
+ if(state->writecnt == state->lp_params->writeblocks){
+ state->mode=READ_WRITE_DATA;
+ }
+ req = smb_raw_write_send(state->cli,&wr);
+ NT_STATUS_HAVE_NO_MEMORY(req);
+
+ /*register the callback function!*/
+ req->async.fn = benchrw_callback;
+ req->async.private = state;
+ return NT_STATUS_OK;
+}
+
+/*
+ Called when the mkdir is done. Opens a file.
+*/
+static NTSTATUS benchrw_mkdir(struct torture_context *tctx,struct smbcli_request *req,
+ struct benchrw_state *state)
+{
+ union smb_open *open_parms;
+ uint8_t *writedata;
+
+ NT_STATUS_NOT_OK_RETURN(req->status);
+
+ /* open/create the files */
+ torture_comment(tctx, "Open File %d/%d\n",state->nr+1,torture_nprocs);
+ open_parms=talloc_zero(state->mem_ctx, union smb_open);
+ NT_STATUS_HAVE_NO_MEMORY(open_parms);
+ open_parms->openx.level = RAW_OPEN_OPENX;
+ open_parms->openx.in.flags = 0;
+ open_parms->openx.in.open_mode = OPENX_MODE_ACCESS_RDWR;
+ open_parms->openx.in.search_attrs =
+ FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN;
+ open_parms->openx.in.file_attrs = 0;
+ open_parms->openx.in.write_time = 0;
+ open_parms->openx.in.open_func = OPENX_OPEN_FUNC_CREATE;
+ open_parms->openx.in.size = 0;
+ open_parms->openx.in.timeout = 0;
+ open_parms->openx.in.fname = state->fname;
+
+ writedata = talloc_size(state->mem_ctx,state->lp_params->blocksize);
+ NT_STATUS_HAVE_NO_MEMORY(writedata);
+ generate_random_buffer(writedata,state->lp_params->blocksize);
+ state->buffer=writedata;
+ state->writecnt=1;
+ state->readcnt=0;
+ state->req_params=open_parms;
+ state->mode=OPEN_FILE;
+
+ req = smb_raw_open_send(state->cli,open_parms);
+ NT_STATUS_HAVE_NO_MEMORY(req);
+
+ /*register the callback function!*/
+ req->async.fn = benchrw_callback;
+ req->async.private = state;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ handler for completion of a sub-request of the bench-rw test
+*/
+static void benchrw_callback(struct smbcli_request *req)
+{
+ struct benchrw_state *state = req->async.private;
+ struct torture_context *tctx = state->tctx;
+
+ /*dont send new requests when torture_numops is reached*/
+ if(state->completed >= torture_numops){
+ state->completed=0;
+ state->mode=MAX_OPS_REACHED;
+ }
+
+ switch (state->mode) {
+
+ case MK_TESTDIR:
+ if (!NT_STATUS_IS_OK(benchrw_mkdir(tctx, req,state))) {
+ torture_comment(tctx, "Failed to create the test directory - %s\n",
+ nt_errstr(req->status));
+ state->mode=ERROR;
+ return;
+ }
+ break;
+ case OPEN_FILE:
+ case INITIAL_WRITE:
+ if (!NT_STATUS_IS_OK(benchrw_open(tctx, req,state))){
+ torture_comment(tctx, "Failed to open/write the file - %s\n",
+ nt_errstr(req->status));
+ state->mode=ERROR;
+ return;
+ }
+ break;
+ case READ_WRITE_DATA:
+ if (!NT_STATUS_IS_OK(benchrw_readwrite(tctx,req,state))){
+ torture_comment(tctx, "Failed to read/write the file - %s\n",
+ nt_errstr(req->status));
+ state->mode=ERROR;
+ return;
+ }
+ break;
+ case MAX_OPS_REACHED:
+ if (!NT_STATUS_IS_OK(benchrw_close(tctx,req,state))){
+ torture_comment(tctx, "Failed to read/write/close the file - %s\n",
+ nt_errstr(req->status));
+ state->mode=ERROR;
+ return;
+ }
+ break;
+ case CLOSE_FILE:
+ torture_comment(tctx, "File %d closed\n",state->nr);
+ if (!NT_STATUS_IS_OK(req->status)) {
+ torture_comment(tctx, "Failed to close the file - %s\n",
+ nt_errstr(req->status));
+ state->mode=ERROR;
+ return;
+ }
+ state->mode=CLEANUP;
+ return;
+ default:
+ break;
+ }
+
+}
+
+/* open connection async callback function*/
+static void async_open_callback(struct composite_context *con)
+{
+ struct benchrw_state *state = con->async.private_data;
+ struct torture_context *tctx = state->tctx;
+ int retry = state->lp_params->retry;
+
+ if (NT_STATUS_IS_OK(con->status)) {
+ state->cli=((struct smb_composite_connect*)
+ state->req_params)->out.tree;
+ state->mode=CLEANUP_TESTDIR;
+ }else{
+ if(state->writecnt < retry){
+ torture_comment(tctx, "Failed to open connection:%d, Retry (%d/%d)\n",
+ state->nr,state->writecnt,retry);
+ state->writecnt++;
+ state->mode=START;
+ usleep(1000);
+ }else{
+ torture_comment(tctx, "Failed to open connection (%d) - %s\n",
+ state->nr, nt_errstr(con->status));
+ state->mode=ERROR;
+ }
+ return;
+ }
+}
+
+/*
+ establishs a smbcli_tree from scratch (async)
+*/
+static struct composite_context *torture_connect_async(
+ struct torture_context *tctx,
+ struct smb_composite_connect *smb,
+ TALLOC_CTX *mem_ctx,
+ struct event_context *ev,
+ const char *host,
+ const char *share,
+ const char *workgroup)
+{
+ torture_comment(tctx, "Open Connection to %s/%s\n",host,share);
+ smb->in.dest_host=talloc_strdup(mem_ctx,host);
+ smb->in.service=talloc_strdup(mem_ctx,share);
+ smb->in.port=0;
+ smb->in.called_name = strupper_talloc(mem_ctx, host);
+ smb->in.service_type=NULL;
+ smb->in.credentials=cmdline_credentials;
+ smb->in.fallback_to_anonymous=False;
+ smb->in.workgroup=workgroup;
+
+ return smb_composite_connect_send(smb,mem_ctx,ev);
+}
+
+BOOL run_benchrw(struct torture_context *tctx)
+{
+ struct smb_composite_connect *smb_con;
+ const char *fname = "\\rwtest.dat";
+ struct smbcli_request *req;
+ struct benchrw_state **state;
+ int i , num_unc_names;
+ struct event_context *ev ;
+ struct composite_context *req1;
+ struct params lpparams;
+ union smb_mkdir parms;
+ int finished = 0;
+ BOOL success=True;
+
+ torture_comment(tctx, "Start BENCH-READWRITE num_ops=%d num_nprocs=%d\n",
+ torture_numops,torture_nprocs);
+
+ /*init talloc context*/
+ ev = event_context_init(tctx);
+ state = talloc_array(tctx, struct benchrw_state *, torture_nprocs);
+
+ /* init params using lp_parm_xxx */
+ num_unc_names = init_benchrw_params(tctx,&lpparams);
+
+ /* init private data structs*/
+ for(i = 0; i<torture_nprocs;i++){
+ state[i]=talloc(tctx,struct benchrw_state);
+ state[i]->tctx = tctx;
+ state[i]->completed=0;
+ state[i]->lp_params=&lpparams;
+ state[i]->nr=i;
+ state[i]->dname=talloc_asprintf(tctx,"benchrw%d",i);
+ state[i]->fname=talloc_asprintf(tctx,"%s%s",
+ state[i]->dname,fname);
+ state[i]->mode=START;
+ state[i]->writecnt=0;
+ }
+
+ torture_comment(tctx, "Starting async requests\n");
+ while(finished != torture_nprocs){
+ finished=0;
+ for(i = 0; i<torture_nprocs;i++){
+ switch (state[i]->mode){
+ /*open multiple connections with the same userid */
+ case START:
+ smb_con = talloc(tctx,struct smb_composite_connect) ;
+ state[i]->req_params=smb_con;
+ state[i]->mode=OPEN_CONNECTION;
+ req1 = torture_connect_async(tctx, smb_con,
+ tctx,ev,
+ lpparams.unc[i % num_unc_names]->host,
+ lpparams.unc[i % num_unc_names]->share,
+ lpparams.workgroup);
+ /* register callback fn + private data */
+ req1->async.fn = async_open_callback;
+ req1->async.private_data=state[i];
+ break;
+ /*setup test dirs (sync)*/
+ case CLEANUP_TESTDIR:
+ torture_comment(tctx, "Setup test dir %d\n",i);
+ smb_raw_exit(state[i]->cli->session);
+ if (smbcli_deltree(state[i]->cli,
+ state[i]->dname) == -1) {
+ torture_comment(tctx, "Unable to delete %s - %s\n",
+ state[i]->dname,
+ smbcli_errstr(state[i]->cli));
+ state[i]->mode=ERROR;
+ break;
+ }
+ state[i]->mode=MK_TESTDIR;
+ parms.mkdir.level = RAW_MKDIR_MKDIR;
+ parms.mkdir.in.path = state[i]->dname;
+ req = smb_raw_mkdir_send(state[i]->cli,&parms);
+ /* register callback fn + private data */
+ req->async.fn = benchrw_callback;
+ req->async.private=state[i];
+ break;
+ /* error occured , finish */
+ case ERROR:
+ finished++;
+ success=False;
+ break;
+ /* cleanup , close connection */
+ case CLEANUP:
+ torture_comment(tctx, "Deleting test dir %s %d/%d\n",state[i]->dname,
+ i+1,torture_nprocs);
+ smbcli_deltree(state[i]->cli,state[i]->dname);
+ if (NT_STATUS_IS_ERR(smb_tree_disconnect(
+ state[i]->cli))) {
+ torture_comment(tctx, "ERROR: Tree disconnect failed");
+ state[i]->mode=ERROR;
+ break;
+ }
+ state[i]->mode=FINISHED;
+ case FINISHED:
+ finished++;
+ break;
+ default:
+ event_loop_once(ev);
+ }
+ }
+ }
+
+ return success;
+}
+