tridge the destroyer returns!
[samba.git] / source3 / utils / torture.c
index f3d76e0ee2536006f3a30ff21eab4562ca9293cc..2bee5f3769059fe3a2eb2ab21d6f79ef4f553a3e 100644 (file)
@@ -2,7 +2,7 @@
    Unix SMB/Netbios implementation.
    Version 1.9.
    SMB torture tester
-   Copyright (C) Andrew Tridgell 1997
+   Copyright (C) Andrew Tridgell 1997-1998
    
    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
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
-#ifdef SYSLOG
-#undef SYSLOG
-#endif
+#define NO_SYSLOG
 
 #include "includes.h"
 
-static struct cli_state cli;
 static fstring host, workgroup, share, password, username, myname;
+static int max_protocol = PROTOCOL_NT1;
 static char *sockops="";
 
 
 static struct timeval tp1,tp2;
 
-static void start_timer()
+static void start_timer(void)
 {
        gettimeofday(&tp1,NULL);
 }
 
-static double end_timer()
+static double end_timer(void)
 {
        gettimeofday(&tp2,NULL);
        return((tp2.tv_sec - tp1.tv_sec) + 
@@ -45,62 +43,65 @@ static double end_timer()
 }
 
 
-static int open_connection(void)
+static BOOL open_connection(struct cli_state *c)
 {
-       if (!cli_initialise(&cli) || !cli_connect(&cli, host, NULL)) {
+       if (!cli_initialise(c) || !cli_connect(c, host, NULL)) {
                printf("Failed to connect with %s\n", host);
+               return False;
        }
 
-       if (!cli_session_request(&cli, host, 0x20, myname)) {
+       if (!cli_session_request(c, host, 0x20, myname)) {
                printf("%s rejected the session\n",host);
-               cli_shutdown(&cli);
-               return -1;
+               cli_shutdown(c);
+               return False;
        }
 
-       if (!cli_negprot(&cli)) {
-               printf("%s rejected the negprot (%s)\n",host, cli_errstr(&cli));
-               cli_shutdown(&cli);
-               return -1;
+       c->protocol = max_protocol;
+
+       if (!cli_negprot(c)) {
+               printf("%s rejected the negprot (%s)\n",host, cli_errstr(c));
+               cli_shutdown(c);
+               return False;
        }
 
-       if (!cli_session_setup(&cli, username, password, strlen(password),
+       if (!cli_session_setup(c, username, password, strlen(password),
                               "", 0, workgroup)) {
-               printf("%s rejected the sessionsetup (%s)\n", host, cli_errstr(&cli));
-               cli_shutdown(&cli);
-               return -1;
+               printf("%s rejected the sessionsetup (%s)\n", host, cli_errstr(c));
+               cli_shutdown(c);
+               return False;
        }
 
-       if (!cli_send_tconX(&cli, share, "A:", password, strlen(password)+1)) {
-               printf("%s refused tree connect (%s)\n", host, cli_errstr(&cli));
-               cli_shutdown(&cli);
-               return -1;
+       if (!cli_send_tconX(c, share, 
+                           strstr(share,"IPC$")?"IPC":"A:", 
+                           password, strlen(password)+1)) {
+               printf("%s refused tree connect (%s)\n", host, cli_errstr(c));
+               cli_shutdown(c);
+               return False;
        }
 
-       return 0;
+       return True;
 }
 
 
 
-static void close_connection(void)
+static void close_connection(struct cli_state *c)
 {
-       if (!cli_tdis(&cli)) {
-               printf("tdis failed (%s)\n", cli_errstr(&cli));
+       if (!cli_tdis(c)) {
+               printf("tdis failed (%s)\n", cli_errstr(c));
        }
 
-       cli_shutdown(&cli);
+       cli_shutdown(c);
 }
 
 
-
-
-static BOOL wait_lock(int fnum, uint32 offset, uint32 len)
+static BOOL wait_lock(struct cli_state *c, int fnum, uint32 offset, uint32 len)
 {
-       while (!cli_lock(&cli, fnum, offset, len, -1)) {
+       while (!cli_lock(c, fnum, offset, len, -1)) {
                int eclass, num;
-               cli_error(&cli, &eclass, &num);
+               cli_error(c, &eclass, &num);
                if (eclass != ERRDOS || num != ERRlock) {
                        printf("lock failed (%s)\n", 
-                              cli_errstr(&cli));
+                              cli_errstr(c));
                        return False;
                }
        }
@@ -108,22 +109,23 @@ static BOOL wait_lock(int fnum, uint32 offset, uint32 len)
 }
 
 
-static int rw_torture(int numops)
+static BOOL rw_torture(struct cli_state *c, int numops)
 {
        char *lockfname = "\\torture.lck";
        fstring fname;
        int fnum;
        int fnum2;
        int pid2, pid = getpid();
-       int i;
+       int i, j;
+       char buf[1024];
 
-       fnum2 = cli_open(&cli, lockfname, O_RDWR | O_CREAT | O_EXCL, 
+       fnum2 = cli_open(c, lockfname, O_RDWR | O_CREAT | O_EXCL, 
                         DENY_NONE);
        if (fnum2 == -1)
-               fnum2 = cli_open(&cli, lockfname, O_RDWR, DENY_NONE);
+               fnum2 = cli_open(c, lockfname, O_RDWR, DENY_NONE);
        if (fnum2 == -1) {
-               printf("open of %s failed (%s)\n", lockfname, cli_errstr(&cli));
-               return -1;
+               printf("open of %s failed (%s)\n", lockfname, cli_errstr(c));
+               return False;
        }
 
 
@@ -132,53 +134,64 @@ static int rw_torture(int numops)
                if (i % 10 == 0) {
                        printf("%d\r", i); fflush(stdout);
                }
-               sprintf(fname,"\\torture.%u", n);
+               slprintf(fname, sizeof(fstring) - 1, "\\torture.%u", n);
 
-               if (!wait_lock(fnum2, n*sizeof(int), sizeof(int))) {
-                       return -1;
+               if (!wait_lock(c, fnum2, n*sizeof(int), sizeof(int))) {
+                       return False;
                }
 
-               fnum = cli_open(&cli, fname, O_RDWR | O_CREAT | O_TRUNC, DENY_ALL);
+               fnum = cli_open(c, fname, O_RDWR | O_CREAT | O_TRUNC, DENY_ALL);
                if (fnum == -1) {
-                       printf("open failed (%s)\n", cli_errstr(&cli));
+                       printf("open failed (%s)\n", cli_errstr(c));
                        break;
                }
 
-               if (cli_write(&cli, fnum, (char *)&pid, 0, sizeof(pid)) != sizeof(pid)) {
-                       printf("write failed (%s)\n", cli_errstr(&cli));
+               if (cli_write(c, fnum, (char *)&pid, 0, sizeof(pid)) != sizeof(pid)) {
+                       printf("write failed (%s)\n", cli_errstr(c));
+               }
+
+               for (j=0;j<50;j++) {
+                       if (cli_write(c, fnum, (char *)buf, 
+                                     sizeof(pid)+(j*sizeof(buf)), 
+                                     sizeof(buf)) != sizeof(buf)) {
+                               printf("write failed (%s)\n", cli_errstr(c));
+                       }
                }
 
                pid2 = 0;
 
-               if (cli_read(&cli, fnum, (char *)&pid2, 0, sizeof(pid)) != sizeof(pid)) {
-                       printf("read failed (%s)\n", cli_errstr(&cli));
+               if (cli_read(c, fnum, (char *)&pid2, 0, sizeof(pid)) != sizeof(pid)) {
+                       printf("read failed (%s)\n", cli_errstr(c));
                }
 
                if (pid2 != pid) {
                        printf("data corruption!\n");
                }
 
-               if (!cli_close(&cli, fnum)) {
-                       printf("close failed (%s)\n", cli_errstr(&cli));
+               if (!cli_close(c, fnum)) {
+                       printf("close failed (%s)\n", cli_errstr(c));
                }
 
-               if (!cli_unlink(&cli, fname)) {
-                       printf("unlink failed (%s)\n", cli_errstr(&cli));
+               if (!cli_unlink(c, fname)) {
+                       printf("unlink failed (%s)\n", cli_errstr(c));
                }
 
-               if (!cli_unlock(&cli, fnum2, n*sizeof(int), sizeof(int), -1)) {
-                       printf("unlock failed (%s)\n", cli_errstr(&cli));
+               if (!cli_unlock(c, fnum2, n*sizeof(int), sizeof(int), -1)) {
+                       printf("unlock failed (%s)\n", cli_errstr(c));
                }
        }
 
+       cli_close(c, fnum2);
+       cli_unlink(c, lockfname);
+
        printf("%d\n", i);
 
-       return 0;
+       return True;
 }
 
 static void usage(void)
 {
-       printf("Usage: smbtorture \\\\server\\share <options>\n");
+       printf("Usage: smbtorture //server/share <options>\n");
 
        printf("\t-U user%%pass\n");
        printf("\t-N numprocs\n");
@@ -186,6 +199,7 @@ static void usage(void)
        printf("\t-W workgroup\n");
        printf("\t-o num_operations\n");
        printf("\t-O socket_options\n");
+       printf("\t-m maximum protocol\n");
        printf("\n");
 
        exit(1);
@@ -195,15 +209,647 @@ static void usage(void)
 
 static void run_torture(int numops)
 {
-       if (open_connection() == 0) {
+       static struct cli_state cli;
+
+       if (open_connection(&cli)) {
                cli_sockopt(&cli, sockops);
 
                printf("pid %d OK\n", getpid());
 
-               rw_torture(numops);
+               rw_torture(&cli, numops);
+
+               close_connection(&cli);
+       }
+}
+
+/*
+  This test checks for two things:
+
+  1) correct support for retaining locks over a close (ie. the server
+     must not use posix semantics)
+  2) support for lock timeouts
+ */
+static void run_locktest1(void)
+{
+       static struct cli_state cli1, cli2;
+       char *fname = "\\lockt1.lck";
+       int fnum1, fnum2, fnum3;
+       time_t t1, t2;
+
+       if (!open_connection(&cli1) || !open_connection(&cli2)) {
+               return;
+       }
+       cli_sockopt(&cli1, sockops);
+       cli_sockopt(&cli2, sockops);
+
+       printf("starting locktest1\n");
+
+       cli_unlink(&cli1, fname);
+
+       fnum1 = cli_open(&cli1, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+       if (fnum1 == -1) {
+               printf("open of %s failed (%s)\n", fname, cli_errstr(&cli1));
+               return;
+       }
+       fnum2 = cli_open(&cli1, fname, O_RDWR, DENY_NONE);
+       if (fnum2 == -1) {
+               printf("open2 of %s failed (%s)\n", fname, cli_errstr(&cli1));
+               return;
+       }
+       fnum3 = cli_open(&cli2, fname, O_RDWR, DENY_NONE);
+       if (fnum3 == -1) {
+               printf("open3 of %s failed (%s)\n", fname, cli_errstr(&cli2));
+               return;
+       }
+
+       if (!cli_lock(&cli1, fnum1, 0, 4, 0)) {
+               printf("lock1 failed (%s)\n", cli_errstr(&cli1));
+               return;
+       }
+
+
+       if (cli_lock(&cli2, fnum3, 0, 4, 0)) {
+               printf("lock2 succeeded! This is a locking bug\n");
+               return;
+       } else {
+               int eclass, num;
+               cli_error(&cli2, &eclass, &num);
+               if (eclass != ERRDOS || num != ERRlock) {
+                       printf("error should have been ERRDOS/ERRlock (%s)\n", 
+                              cli_errstr(&cli2));
+                       return;
+               }
+       }
+
+
+       printf("Testing lock timeouts\n");
+       t1 = time(NULL);
+       if (cli_lock(&cli2, fnum3, 0, 4, 10*1000)) {
+               printf("lock3 succeeded! This is a locking bug\n");
+               return;
+       } else {
+               int eclass, num;
+               cli_error(&cli2, &eclass, &num);
+               if (eclass != ERRDOS || num != ERRlock) {
+                       printf("error should have been ERRDOS/ERRlock (%s)\n", 
+                              cli_errstr(&cli2));
+                       return;
+               }
+       }
+       t2 = time(NULL);
+
+       if (t2 - t1 < 5) {
+               printf("error: This server appears not to support timed lock requests\n");
+       }
+
+       if (!cli_close(&cli1, fnum2)) {
+               printf("close1 failed (%s)\n", cli_errstr(&cli1));
+               return;
+       }
+
+       if (cli_lock(&cli2, fnum3, 0, 4, 0)) {
+               printf("lock4 succeeded! This is a locking bug\n");
+               return;
+       } else {
+               int eclass, num;
+               cli_error(&cli2, &eclass, &num);
+               if (eclass != ERRDOS || num != ERRlock) {
+                       printf("error should have been ERRDOS/ERRlock (%s)\n", 
+                              cli_errstr(&cli2));
+                       return;
+               }
+       }
+
+       if (!cli_close(&cli1, fnum1)) {
+               printf("close2 failed (%s)\n", cli_errstr(&cli1));
+               return;
+       }
+
+       if (!cli_close(&cli2, fnum3)) {
+               printf("close3 failed (%s)\n", cli_errstr(&cli2));
+               return;
+       }
+
+       if (!cli_unlink(&cli1, fname)) {
+               printf("unlink failed (%s)\n", cli_errstr(&cli1));
+               return;
+       }
+
+
+       close_connection(&cli1);
+       close_connection(&cli2);
+
+       printf("Passed locktest1\n");
+}
+
+
+/*
+  This test checks that 
+
+  1) the server supports multiple locking contexts on the one SMB
+  connection, distinguished by PID.  
+
+  2) the server correctly fails overlapping locks made by the same PID (this
+     goes against POSIX behaviour, which is why it is tricky to implement)
+
+  3) the server denies unlock requests by an incorrect client PID
+*/
+static void run_locktest2(void)
+{
+       static struct cli_state cli;
+       char *fname = "\\lockt2.lck";
+       int fnum1, fnum2, fnum3;
+
+       if (!open_connection(&cli)) {
+               return;
+       }
+
+       cli_sockopt(&cli, sockops);
+
+       printf("starting locktest2\n");
+
+       cli_unlink(&cli, fname);
+
+       cli_setpid(&cli, 1);
+
+       fnum1 = cli_open(&cli, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+       if (fnum1 == -1) {
+               printf("open of %s failed (%s)\n", fname, cli_errstr(&cli));
+               return;
+       }
+
+       fnum2 = cli_open(&cli, fname, O_RDWR, DENY_NONE);
+       if (fnum2 == -1) {
+               printf("open2 of %s failed (%s)\n", fname, cli_errstr(&cli));
+               return;
+       }
+
+       cli_setpid(&cli, 2);
+
+       fnum3 = cli_open(&cli, fname, O_RDWR, DENY_NONE);
+       if (fnum3 == -1) {
+               printf("open3 of %s failed (%s)\n", fname, cli_errstr(&cli));
+               return;
+       }
+
+       cli_setpid(&cli, 1);
+
+       if (!cli_lock(&cli, fnum1, 0, 4, 0)) {
+               printf("lock1 failed (%s)\n", cli_errstr(&cli));
+               return;
+       }
+
+       if (cli_lock(&cli, fnum2, 0, 4, 0)) {
+               printf("lock2 succeeded! This is a locking bug\n");
+       } else {
+               int eclass, num;
+               cli_error(&cli, &eclass, &num);
+               if (eclass != ERRDOS || num != ERRlock) {
+                       printf("error should have been ERRDOS/ERRlock (%s)\n", 
+                              cli_errstr(&cli));
+                       return;
+               }
+       }
+
+       cli_setpid(&cli, 2);
+
+       if (cli_unlock(&cli, fnum1, 0, 4, 0)) {
+               printf("unlock1 succeeded! This is a locking bug\n");
+       }
+
+       if (cli_lock(&cli, fnum3, 0, 4, 0)) {
+               printf("lock3 succeeded! This is a locking bug\n");
+       } else {
+               int eclass, num;
+               cli_error(&cli, &eclass, &num);
+               if (eclass != ERRDOS || num != ERRlock) {
+                       printf("error should have been ERRDOS/ERRlock (%s)\n", 
+                              cli_errstr(&cli));
+                       return;
+               }
+       }
+
+       cli_setpid(&cli, 1);
+
+       if (!cli_close(&cli, fnum1)) {
+               printf("close1 failed (%s)\n", cli_errstr(&cli));
+               return;
+       }
+
+       if (!cli_close(&cli, fnum2)) {
+               printf("close2 failed (%s)\n", cli_errstr(&cli));
+               return;
+       }
+
+       if (!cli_close(&cli, fnum3)) {
+               printf("close3 failed (%s)\n", cli_errstr(&cli));
+               return;
+       }
+
+       close_connection(&cli);
+
+       printf("locktest2 finished\n");
+}
+
+
+/*
+  This test checks that 
+
+  1) the server supports the full offset range in lock requests
+*/
+static void run_locktest3(int numops)
+{
+       static struct cli_state cli1, cli2;
+       char *fname = "\\lockt3.lck";
+       int fnum1, fnum2, i;
+       uint32 offset;
+
+#define NEXT_OFFSET offset += (~(uint32)0) / numops
+
+       if (!open_connection(&cli1) || !open_connection(&cli2)) {
+               return;
+       }
+       cli_sockopt(&cli1, sockops);
+       cli_sockopt(&cli2, sockops);
+
+       printf("starting locktest3\n");
+
+       cli_unlink(&cli1, fname);
+
+       fnum1 = cli_open(&cli1, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+       if (fnum1 == -1) {
+               printf("open of %s failed (%s)\n", fname, cli_errstr(&cli1));
+               return;
+       }
+       fnum2 = cli_open(&cli2, fname, O_RDWR, DENY_NONE);
+       if (fnum2 == -1) {
+               printf("open2 of %s failed (%s)\n", fname, cli_errstr(&cli2));
+               return;
+       }
+
+       for (offset=i=0;i<numops;i++) {
+               NEXT_OFFSET;
+               if (!cli_lock(&cli1, fnum1, offset-1, 1, 0)) {
+                       printf("lock1 %d failed (%s)\n", 
+                              i,
+                              cli_errstr(&cli1));
+                       return;
+               }
+
+               if (!cli_lock(&cli2, fnum2, offset-2, 1, 0)) {
+                       printf("lock2 %d failed (%s)\n", 
+                              i,
+                              cli_errstr(&cli1));
+                       return;
+               }
+       }
+
+       for (offset=i=0;i<numops;i++) {
+               NEXT_OFFSET;
+
+               if (cli_lock(&cli1, fnum1, offset-2, 1, 0)) {
+                       printf("error: lock1 %d succeeded!\n", i);
+                       return;
+               }
+
+               if (cli_lock(&cli2, fnum2, offset-1, 1, 0)) {
+                       printf("error: lock2 %d succeeded!\n", i);
+                       return;
+               }
+
+               if (cli_lock(&cli1, fnum1, offset-1, 1, 0)) {
+                       printf("error: lock3 %d succeeded!\n", i);
+                       return;
+               }
+
+               if (cli_lock(&cli2, fnum2, offset-2, 1, 0)) {
+                       printf("error: lock4 %d succeeded!\n", i);
+                       return;
+               }
+       }
+
+       for (offset=i=0;i<numops;i++) {
+               NEXT_OFFSET;
+
+               if (!cli_unlock(&cli1, fnum1, offset-1, 1, 0)) {
+                       printf("unlock1 %d failed (%s)\n", 
+                              i,
+                              cli_errstr(&cli1));
+                       return;
+               }
+
+               if (!cli_unlock(&cli2, fnum2, offset-2, 1, 0)) {
+                       printf("unlock2 %d failed (%s)\n", 
+                              i,
+                              cli_errstr(&cli1));
+                       return;
+               }
+       }
+
+       if (!cli_close(&cli1, fnum1)) {
+               printf("close1 failed (%s)\n", cli_errstr(&cli1));
+       }
+
+       if (!cli_close(&cli2, fnum2)) {
+               printf("close2 failed (%s)\n", cli_errstr(&cli2));
+       }
+
+       if (!cli_unlink(&cli1, fname)) {
+               printf("unlink failed (%s)\n", cli_errstr(&cli1));
+               return;
+       }
+
+       close_connection(&cli1);
+       close_connection(&cli2);
+
+       printf("finished locktest3\n");
+}
+
+
+/*
+  This test checks that 
+
+  1) the server does not allow an unlink on a file that is open
+*/
+static void run_unlinktest(void)
+{
+       static struct cli_state cli;
+       char *fname = "\\unlink.tst";
+       int fnum;
+
+       if (!open_connection(&cli)) {
+               return;
+       }
+
+       cli_sockopt(&cli, sockops);
+
+       printf("starting unlink test\n");
+
+       cli_unlink(&cli, fname);
 
-               close_connection();
+       cli_setpid(&cli, 1);
+
+       fnum = cli_open(&cli, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
+       if (fnum == -1) {
+               printf("open of %s failed (%s)\n", fname, cli_errstr(&cli));
+               return;
        }
+
+       if (cli_unlink(&cli, fname)) {
+               printf("error: server allowed unlink on an open file\n");
+       }
+
+       cli_close(&cli, fnum);
+       cli_unlink(&cli, fname);
+
+       close_connection(&cli);
+
+       printf("unlink test finished\n");
+}
+
+/* generate a random buffer */
+static void rand_buf(char *buf, int len)
+{
+       while (len--) {
+               *buf = random();
+               buf++;
+       }
+}
+
+/* send random IPC commands */
+static void run_randomipc(void)
+{
+       char *rparam = NULL;
+       char *rdata = NULL;
+       int rdrcnt,rprcnt;
+       pstring param;
+       int api, param_len, i;
+       static struct cli_state cli;
+       struct {
+               int api, level;
+               char *format;
+               char *subformat;
+               int len;
+       } foo;
+
+       printf("starting random ipc test\n");
+
+       if (!open_connection(&cli)) {
+               return;
+       }
+
+       for (i=0;i<1000;i++) {
+               api = random() % 500;
+               param_len = random() % 64;
+
+               rand_buf(param, param_len);
+  
+               SSVAL(param,0,api); 
+
+               cli_api(&cli, 
+                       param, param_len, 8,  
+                       NULL, 0, BUFFER_SIZE, 
+                       &rparam, &rprcnt,     
+                       &rdata, &rdrcnt);
+       }
+
+       close_connection(&cli);
+
+       printf("finished random ipc test\n");
+}
+
+
+
+static void browse_callback(char *sname, uint32 stype, char *comment)
+{
+       printf("\t%20.20s %08x %s\n", sname, stype, comment);
+}
+
+
+
+/*
+  This test checks the browse list code
+
+*/
+static void run_browsetest(void)
+{
+       static struct cli_state cli;
+
+       printf("starting browse test\n");
+
+       if (!open_connection(&cli)) {
+               return;
+       }
+
+       printf("domain list:\n");
+       cli_NetServerEnum(&cli, workgroup, 
+                         SV_TYPE_DOMAIN_ENUM,
+                         browse_callback);
+
+       printf("machine list:\n");
+       cli_NetServerEnum(&cli, workgroup, 
+                         SV_TYPE_ALL,
+                         browse_callback);
+
+       close_connection(&cli);
+
+       printf("browse test finished\n");
+}
+
+
+/*
+  This checks how the getatr calls works
+*/
+static void run_attrtest(void)
+{
+       static struct cli_state cli;
+       int fnum;
+       time_t t, t2;
+       char *fname = "\\attrib.tst";
+
+       printf("starting attrib test\n");
+
+       if (!open_connection(&cli)) {
+               return;
+       }
+
+       cli_unlink(&cli, fname);
+       fnum = cli_open(&cli, fname, 
+                       O_RDWR | O_CREAT | O_TRUNC, DENY_NONE);
+       cli_close(&cli, fnum);
+       if (!cli_getatr(&cli, fname, NULL, NULL, &t)) {
+               printf("getatr failed (%s)\n", cli_errstr(&cli));
+       }
+
+       if (abs(t - time(NULL)) > 2) {
+               printf("ERROR: SMBgetatr bug. time is %s",
+                      ctime(&t));
+               t = time(NULL);
+       }
+
+       t2 = t-60*60*24; /* 1 day ago */
+
+       if (!cli_setatr(&cli, fname, 0, t2)) {
+               printf("setatr failed (%s)\n", cli_errstr(&cli));
+       }
+
+       if (!cli_getatr(&cli, fname, NULL, NULL, &t)) {
+               printf("getatr failed (%s)\n", cli_errstr(&cli));
+       }
+
+       if (t != t2) {
+               printf("ERROR: getatr/setatr bug. times are\n%s",
+                      ctime(&t));
+               printf("%s", ctime(&t2));
+       }
+
+       cli_unlink(&cli, fname);
+
+       close_connection(&cli);
+
+       printf("attrib test finished\n");
+}
+
+
+/*
+  This checks a couple of trans2 calls
+*/
+static void run_trans2test(void)
+{
+       static struct cli_state cli;
+       int fnum;
+       uint32 size;
+       time_t c_time, a_time, m_time, w_time, m_time2;
+       char *fname = "\\trans2.tst";
+       char *dname = "\\trans2";
+       char *fname2 = "\\trans2\\trans2.tst";
+
+       printf("starting trans2 test\n");
+
+       if (!open_connection(&cli)) {
+               return;
+       }
+
+       cli_unlink(&cli, fname);
+       fnum = cli_open(&cli, fname, 
+                       O_RDWR | O_CREAT | O_TRUNC, DENY_NONE);
+       if (!cli_qfileinfo(&cli, fnum, &c_time, &a_time, &m_time, &size)) {
+               printf("ERROR: qfileinfo failed (%s)\n", cli_errstr(&cli));
+       }
+       cli_close(&cli, fnum);
+
+       sleep(2);
+
+       cli_unlink(&cli, fname);
+       fnum = cli_open(&cli, fname, 
+                       O_RDWR | O_CREAT | O_TRUNC, DENY_NONE);
+       cli_close(&cli, fnum);
+
+       if (!cli_qpathinfo(&cli, fname, &c_time, &a_time, &m_time, &size)) {
+               printf("ERROR: qpathinfo failed (%s)\n", cli_errstr(&cli));
+       } else {
+               if (c_time != m_time) {
+                       printf("create time=%s", ctime(&c_time));
+                       printf("modify time=%s", ctime(&m_time));
+                       printf("This system appears to have sticky create times\n");
+               }
+               if (a_time % (60*60) == 0) {
+                       printf("access time=%s", ctime(&a_time));
+                       printf("This system appears to set a midnight access time\n");
+               }
+
+               if (abs(m_time - time(NULL)) > 60*60*24*7) {
+                       printf("ERROR: totally incorrect times - maybe word reversed?\n");
+               }
+       }
+
+
+       cli_unlink(&cli, fname);
+       fnum = cli_open(&cli, fname, 
+                       O_RDWR | O_CREAT | O_TRUNC, DENY_NONE);
+       cli_close(&cli, fnum);
+       if (!cli_qpathinfo2(&cli, fname, &c_time, &a_time, &m_time, 
+                           &w_time, &size)) {
+               printf("ERROR: qpathinfo2 failed (%s)\n", cli_errstr(&cli));
+       } else {
+               if (w_time < 60*60*24*2) {
+                       printf("write time=%s", ctime(&w_time));
+                       printf("This system appears to set a initial 0 write time\n");
+               }
+       }
+
+       cli_unlink(&cli, fname);
+
+
+       /* check if the server updates the directory modification time
+           when creating a new file */
+       if (!cli_mkdir(&cli, dname)) {
+               printf("ERROR: mkdir failed (%s)\n", cli_errstr(&cli));
+       }
+       sleep(3);
+       if (!cli_qpathinfo2(&cli, "\\trans2\\", &c_time, &a_time, &m_time, 
+                           &w_time, &size)) {
+               printf("ERROR: qpathinfo2 failed (%s)\n", cli_errstr(&cli));
+       }
+
+       fnum = cli_open(&cli, fname2, 
+                       O_RDWR | O_CREAT | O_TRUNC, DENY_NONE);
+       cli_write(&cli, fnum,  (char *)&fnum, 0, sizeof(fnum));
+       cli_close(&cli, fnum);
+       if (!cli_qpathinfo2(&cli, "\\trans2\\", &c_time, &a_time, &m_time2, 
+                           &w_time, &size)) {
+               printf("ERROR: qpathinfo2 failed (%s)\n", cli_errstr(&cli));
+       } else {
+               if (m_time2 == m_time)
+                       printf("This system does not update directory modification times\n");
+       }
+       cli_unlink(&cli, fname2);
+       cli_rmdir(&cli, dname);
+
+
+       close_connection(&cli);
+
+       printf("trans2 test finished\n");
 }
 
 
@@ -247,12 +893,16 @@ static void create_procs(int nprocs, int numops)
                usage();
        }
 
-       if (strncmp(argv[1], "\\\\", 2)) {
+        for(p = argv[1]; *p; p++)
+          if(*p == '\\')
+            *p = '/';
+       if (strncmp(argv[1], "//", 2)) {
                usage();
        }
 
        fstrcpy(host, &argv[1][2]);
-       p = strchr(&host[2],'\\');
+       p = strchr(&host[2],'/');
        if (!p) {
                usage();
        }
@@ -262,18 +912,21 @@ static void create_procs(int nprocs, int numops)
        get_myname(myname,NULL);
 
        if (*username == 0 && getenv("LOGNAME")) {
-         strcpy(username,getenv("LOGNAME"));
+         pstrcpy(username,getenv("LOGNAME"));
        }
 
        argc--;
        argv++;
 
 
-       while ((opt = getopt(argc, argv, "hW:U:n:N:O:o:")) != EOF) {
+       while ((opt = getopt(argc, argv, "hW:U:n:N:O:o:m:")) != EOF) {
                switch (opt) {
                case 'W':
                        fstrcpy(workgroup,optarg);
                        break;
+               case 'm':
+                       max_protocol = interpret_protocol(optarg, max_protocol);
+                       break;
                case 'N':
                        nprocs = atoi(optarg);
                        break;
@@ -287,11 +940,11 @@ static void create_procs(int nprocs, int numops)
                        fstrcpy(myname, optarg);
                        break;
                case 'U':
-                       strcpy(username,optarg);
+                       pstrcpy(username,optarg);
                        p = strchr(username,'%');
                        if (p) {
                                *p = 0;
-                               strcpy(password, p+1);
+                               pstrcpy(password, p+1);
                                gotpass = 1;
                        }
                        break;
@@ -305,7 +958,7 @@ static void create_procs(int nprocs, int numops)
        while (!gotpass) {
                p = getpass("Password:");
                if (p) {
-                       strcpy(password, p);
+                       pstrcpy(password, p);
                        gotpass = 1;
                }
        }
@@ -313,10 +966,20 @@ static void create_procs(int nprocs, int numops)
        printf("host=%s share=%s user=%s myname=%s\n", 
               host, share, username, myname);
 
+       run_randomipc();
+
        start_timer();
        create_procs(nprocs, numops);
        printf("rw_torture: %g secs\n", end_timer());
 
+       run_locktest1();
+       run_locktest2();
+       run_locktest3(numops);
+       run_unlinktest();
+       run_browsetest();
+       run_attrtest();
+       run_trans2test();
+
        return(0);
 }