s3:torture: use timeval_current/timeval_elapsed instead of start_timer/end_timer
[ira/wip.git] / source3 / torture / torture.c
index babcb1e7d0e0cfc6a0ba0418202fa4d4e32fd6c2..39f555906696d2b3ac828f3fb261c1515581366a 100644 (file)
@@ -2,6 +2,7 @@
    Unix SMB/CIFS implementation.
    SMB torture tester
    Copyright (C) Andrew Tridgell 1997-1998
+   Copyright (C) Jeremy Allison 2009
 
    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
@@ -19,6 +20,7 @@
 
 #include "includes.h"
 #include "nsswitch/libwbclient/wbc_async.h"
+#include "torture/proto.h"
 
 extern char *optarg;
 extern int optind;
@@ -40,28 +42,13 @@ static bool use_kerberos;
 static fstring multishare_conn_fname;
 static bool use_multishare_conn = False;
 static bool do_encrypt;
+static const char *local_path = NULL;
 
 bool torture_showall = False;
 
 static double create_procs(bool (*fn)(int), bool *result);
 
 
-static struct timeval tp1,tp2;
-
-
-void start_timer(void)
-{
-       GetTimeOfDay(&tp1);
-}
-
-double end_timer(void)
-{
-       GetTimeOfDay(&tp2);
-       return((tp2.tv_sec - tp1.tv_sec) + 
-              (tp2.tv_usec - tp1.tv_usec)*1.0e-6);
-}
-
-
 /* return a pointer to a anonymous shared memory segment of size "size"
    which will persist across fork() but will disappear when all processes
    exit 
@@ -76,6 +63,23 @@ void *shm_setup(int size)
        int shmid;
        void *ret;
 
+#ifdef __QNXNTO__
+       shmid = shm_open("private", O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
+       if (shmid == -1) {
+               printf("can't get shared memory\n");
+               exit(1);
+       }
+       shm_unlink("private");
+       if (ftruncate(shmid, size) == -1) {
+               printf("can't set shared memory size\n");
+               exit(1);
+       }
+       ret = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, shmid, 0);
+       if (ret == MAP_FAILED) {
+               printf("can't map shared memory\n");
+               exit(1);
+       }
+#else
        shmid = shmget(IPC_PRIVATE, size, S_IRUSR | S_IWUSR);
        if (shmid == -1) {
                printf("can't get shared memory\n");
@@ -94,6 +98,7 @@ void *shm_setup(int size)
           See Stevens "advanced programming in unix env" for details
           */
        shmctl(shmid, IPC_RMID, 0);
+#endif
 
        return ret;
 }
@@ -116,10 +121,12 @@ static bool force_cli_encryption(struct cli_state *c,
                        return false;
        }
 
-       if (!cli_unix_extensions_version(c, &major, &minor, &caplow, &caphigh)) {
+       status = cli_unix_extensions_version(c, &major, &minor, &caplow,
+                                            &caphigh);
+       if (!NT_STATUS_IS_OK(status)) {
                d_printf("Encryption required and "
                        "can't get UNIX CIFS extensions "
-                       "version from server.\n");
+                       "version from server: %s\n", nt_errstr(status));
                return false;
        }
 
@@ -2157,6 +2164,209 @@ fail:
        return correct;
 }
 
+/*
+ * This test is designed to be run in conjunction with
+ * external NFS or POSIX locks taken in the filesystem.
+ * It checks that the smbd server will block until the
+ * lock is released and then acquire it. JRA.
+ */
+
+static bool got_alarm;
+static int alarm_fd;
+
+static void alarm_handler(int dummy)
+{
+        got_alarm = True;
+}
+
+static void alarm_handler_parent(int dummy)
+{
+       close(alarm_fd);
+}
+
+static void do_local_lock(int read_fd, int write_fd)
+{
+       int fd;
+       char c = '\0';
+       struct flock lock;
+       const char *local_pathname = NULL;
+       int ret;
+
+       local_pathname = talloc_asprintf(talloc_tos(),
+                       "%s/lockt9.lck", local_path);
+       if (!local_pathname) {
+               printf("child: alloc fail\n");
+               exit(1);
+       }
+
+       unlink(local_pathname);
+       fd = open(local_pathname, O_RDWR|O_CREAT, 0666);
+       if (fd == -1) {
+               printf("child: open of %s failed %s.\n",
+                       local_pathname, strerror(errno));
+               exit(1);
+       }
+
+       /* Now take a fcntl lock. */
+       lock.l_type = F_WRLCK;
+       lock.l_whence = SEEK_SET;
+       lock.l_start = 0;
+       lock.l_len = 4;
+       lock.l_pid = getpid();
+
+       ret = fcntl(fd,F_SETLK,&lock);
+       if (ret == -1) {
+               printf("child: failed to get lock 0:4 on file %s. Error %s\n",
+                       local_pathname, strerror(errno));
+               exit(1);
+       } else {
+               printf("child: got lock 0:4 on file %s.\n",
+                       local_pathname );
+               fflush(stdout);
+       }
+
+       CatchSignal(SIGALRM, alarm_handler);
+       alarm(5);
+       /* Signal the parent. */
+       if (write(write_fd, &c, 1) != 1) {
+               printf("child: start signal fail %s.\n",
+                       strerror(errno));
+               exit(1);
+       }
+       alarm(0);
+
+       alarm(10);
+       /* Wait for the parent to be ready. */
+       if (read(read_fd, &c, 1) != 1) {
+               printf("child: reply signal fail %s.\n",
+                       strerror(errno));
+               exit(1);
+       }
+       alarm(0);
+
+       sleep(5);
+       close(fd);
+       printf("child: released lock 0:4 on file %s.\n",
+               local_pathname );
+       fflush(stdout);
+       exit(0);
+}
+
+static bool run_locktest9(int dummy)
+{
+       struct cli_state *cli1;
+       const char *fname = "\\lockt9.lck";
+       uint16_t fnum;
+       bool correct = False;
+       int pipe_in[2], pipe_out[2];
+       pid_t child_pid;
+       char c = '\0';
+       int ret;
+       struct timeval start;
+       double seconds;
+       NTSTATUS status;
+
+       printf("starting locktest9\n");
+
+       if (local_path == NULL) {
+               d_fprintf(stderr, "locktest9 must be given a local path via -l <localpath>\n");
+               return false;
+       }
+
+       if (pipe(pipe_in) == -1 || pipe(pipe_out) == -1) {
+               return false;
+       }
+
+       child_pid = fork();
+       if (child_pid == -1) {
+               return false;
+       }
+
+       if (child_pid == 0) {
+               /* Child. */
+               do_local_lock(pipe_out[0], pipe_in[1]);
+               exit(0);
+       }
+
+       close(pipe_out[0]);
+       close(pipe_in[1]);
+       pipe_out[0] = -1;
+       pipe_in[1] = -1;
+
+       /* Parent. */
+       ret = read(pipe_in[0], &c, 1);
+       if (ret != 1) {
+               d_fprintf(stderr, "failed to read start signal from child. %s\n",
+                       strerror(errno));
+               return false;
+       }
+
+       if (!torture_open_connection(&cli1, 0)) {
+               return false;
+       }
+
+       cli_sockopt(cli1, sockops);
+
+       status = cli_open(cli1, fname, O_RDWR, DENY_NONE,
+                         &fnum);
+       if (!NT_STATUS_IS_OK(status)) {
+               d_fprintf(stderr, "cli_open returned %s\n", cli_errstr(cli1));
+               return false;
+       }
+
+       /* Ensure the child has the lock. */
+       if (cli_lock(cli1, fnum, 0, 4, 0, WRITE_LOCK)) {
+               d_fprintf(stderr, "Got the lock on range 0:4 - this should not happen !\n");
+               goto fail;
+       } else {
+               d_printf("Child has the lock.\n");
+       }
+
+       /* Tell the child to wait 5 seconds then exit. */
+       ret = write(pipe_out[1], &c, 1);
+       if (ret != 1) {
+               d_fprintf(stderr, "failed to send exit signal to child. %s\n",
+                       strerror(errno));
+               goto fail;
+       }
+
+       /* Wait 20 seconds for the lock. */
+       alarm_fd = cli1->fd;
+       CatchSignal(SIGALRM, alarm_handler_parent);
+       alarm(20);
+
+       start = timeval_current();
+
+       if (!cli_lock(cli1, fnum, 0, 4, -1, WRITE_LOCK)) {
+               d_fprintf(stderr, "Unable to apply write lock on range 0:4, error was "
+                      "%s\n", cli_errstr(cli1));
+               goto fail_nofd;
+       }
+       alarm(0);
+
+       seconds = timeval_elapsed(&start);
+
+       printf("Parent got the lock after %.2f seconds.\n",
+               seconds);
+
+       status = cli_close(cli1, fnum);
+       if (!NT_STATUS_IS_OK(status)) {
+               d_fprintf(stderr, "cli_close(fnum1) %s\n", cli_errstr(cli1));
+               goto fail;
+       }
+
+       correct = true;
+
+fail:
+       cli_close(cli1, fnum);
+       torture_close_connection(cli1);
+
+fail_nofd:
+
+       printf("finished locktest9\n");
+       return correct;
+}
+
 /*
 test whether fnums and tids open on one VC are available on another (a major
 security hole)
@@ -2603,6 +2813,8 @@ static bool run_trans2test(int dummy)
        const char *fname2 = "\\trans2\\trans2.tst";
        char pname[1024];
        bool correct = True;
+       NTSTATUS status;
+       uint32_t fs_attr;
 
        printf("starting trans2 test\n");
 
@@ -2610,6 +2822,13 @@ static bool run_trans2test(int dummy)
                return False;
        }
 
+       status = cli_get_fs_attr_info(cli, &fs_attr);
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("ERROR: cli_get_fs_attr_info returned %s\n",
+                      nt_errstr(status));
+               correct = false;
+       }
+
        cli_unlink(cli, fname, aSYSTEM | aHIDDEN);
        cli_open(cli, fname, 
                        O_RDWR | O_CREAT | O_TRUNC, DENY_NONE, &fnum);
@@ -4145,6 +4364,7 @@ static bool run_simple_posix_open_test(int dummy)
        uint16_t fnum1 = (uint16_t)-1;
        SMB_STRUCT_STAT sbuf;
        bool correct = false;
+       NTSTATUS status;
 
        printf("Starting simple POSIX open test\n");
 
@@ -4159,15 +4379,19 @@ static bool run_simple_posix_open_test(int dummy)
                return false;
        }
 
-       if (!cli_unix_extensions_version(cli1, &major,
-                       &minor, &caplow, &caphigh)) {
-               printf("Server didn't return UNIX CIFS extensions.\n");
+       status = cli_unix_extensions_version(cli1, &major, &minor, &caplow,
+                                            &caphigh);
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("Server didn't return UNIX CIFS extensions: %s\n",
+                      nt_errstr(status));
                return false;
        }
 
-       if (!cli_set_unix_extensions_capabilities(cli1,
-                       major, minor, caplow, caphigh)) {
-               printf("Server doesn't support setting UNIX CIFS extensions.\n");
+       status = cli_set_unix_extensions_capabilities(cli1, major, minor,
+                                                     caplow, caphigh);
+       if (!NT_STATUS_IS_OK(status)) {
+               printf("Server doesn't support setting UNIX CIFS extensions: "
+                      "%s.\n", nt_errstr(status));
                return false;
         }
 
@@ -4538,7 +4762,7 @@ static bool run_dirtest(int dummy)
        int i;
        static struct cli_state *cli;
        uint16_t fnum;
-       double t1;
+       struct timeval core_start;
        bool correct = True;
 
        printf("starting directory test\n");
@@ -4560,13 +4784,13 @@ static bool run_dirtest(int dummy)
                cli_close(cli, fnum);
        }
 
-       t1 = end_timer();
+       core_start = timeval_current();
 
        printf("Matched %d\n", cli_list(cli, "a*.*", 0, list_fn, NULL));
        printf("Matched %d\n", cli_list(cli, "b*.*", 0, list_fn, NULL));
        printf("Matched %d\n", cli_list(cli, "xyzabc", 0, list_fn, NULL));
 
-       printf("dirtest core %g seconds\n", end_timer() - t1);
+       printf("dirtest core %g seconds\n", timeval_elapsed(&core_start));
 
        srandom(0);
        for (i=0;i<torture_numops;i++) {
@@ -5253,6 +5477,408 @@ static bool run_chain2(int dummy)
        return True;
 }
 
+
+struct torture_createdel_state {
+       struct tevent_context *ev;
+       struct cli_state *cli;
+};
+
+static void torture_createdel_created(struct tevent_req *subreq);
+static void torture_createdel_closed(struct tevent_req *subreq);
+
+static struct tevent_req *torture_createdel_send(TALLOC_CTX *mem_ctx,
+                                                struct tevent_context *ev,
+                                                struct cli_state *cli,
+                                                const char *name)
+{
+       struct tevent_req *req, *subreq;
+       struct torture_createdel_state *state;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct torture_createdel_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->ev = ev;
+       state->cli = cli;
+
+       subreq = cli_ntcreate_send(
+               state, ev, cli, name, 0,
+               FILE_READ_DATA|FILE_WRITE_DATA|DELETE_ACCESS,
+               FILE_ATTRIBUTE_NORMAL,
+               FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
+               FILE_OPEN_IF, FILE_DELETE_ON_CLOSE, 0);
+
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, torture_createdel_created, req);
+       return req;
+}
+
+static void torture_createdel_created(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct torture_createdel_state *state = tevent_req_data(
+               req, struct torture_createdel_state);
+       NTSTATUS status;
+       uint16_t fnum;
+
+       status = cli_ntcreate_recv(subreq, &fnum);
+       TALLOC_FREE(subreq);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(10, ("cli_ntcreate_recv returned %s\n",
+                          nt_errstr(status)));
+               tevent_req_nterror(req, status);
+               return;
+       }
+
+       subreq = cli_close_send(state, state->ev, state->cli, fnum);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, torture_createdel_closed, req);
+}
+
+static void torture_createdel_closed(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       NTSTATUS status;
+
+       status = cli_close_recv(subreq);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(10, ("cli_close_recv returned %s\n", nt_errstr(status)));
+               tevent_req_nterror(req, status);
+               return;
+       }
+       tevent_req_done(req);
+}
+
+static NTSTATUS torture_createdel_recv(struct tevent_req *req)
+{
+       return tevent_req_simple_recv_ntstatus(req);
+}
+
+struct torture_createdels_state {
+       struct tevent_context *ev;
+       struct cli_state *cli;
+       const char *base_name;
+       int sent;
+       int received;
+       int num_files;
+       struct tevent_req **reqs;
+};
+
+static void torture_createdels_done(struct tevent_req *subreq);
+
+static struct tevent_req *torture_createdels_send(TALLOC_CTX *mem_ctx,
+                                                 struct tevent_context *ev,
+                                                 struct cli_state *cli,
+                                                 const char *base_name,
+                                                 int num_parallel,
+                                                 int num_files)
+{
+       struct tevent_req *req;
+       struct torture_createdels_state *state;
+       int i;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct torture_createdels_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->ev = ev;
+       state->cli = cli;
+       state->base_name = talloc_strdup(state, base_name);
+       if (tevent_req_nomem(state->base_name, req)) {
+               return tevent_req_post(req, ev);
+       }
+       state->num_files = MAX(num_parallel, num_files);
+       state->sent = 0;
+       state->received = 0;
+
+       state->reqs = talloc_array(state, struct tevent_req *, num_parallel);
+       if (tevent_req_nomem(state->reqs, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       for (i=0; i<num_parallel; i++) {
+               char *name;
+
+               name = talloc_asprintf(state, "%s%8.8d", state->base_name,
+                                      state->sent);
+               if (tevent_req_nomem(name, req)) {
+                       return tevent_req_post(req, ev);
+               }
+               state->reqs[i] = torture_createdel_send(
+                       state->reqs, state->ev, state->cli, name);
+               if (tevent_req_nomem(state->reqs[i], req)) {
+                       return tevent_req_post(req, ev);
+               }
+               name = talloc_move(state->reqs[i], &name);
+               tevent_req_set_callback(state->reqs[i],
+                                       torture_createdels_done, req);
+               state->sent += 1;
+       }
+       return req;
+}
+
+static void torture_createdels_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct torture_createdels_state *state = tevent_req_data(
+               req, struct torture_createdels_state);
+       size_t num_parallel = talloc_array_length(state->reqs);
+       NTSTATUS status;
+       char *name;
+       int i;
+
+       status = torture_createdel_recv(subreq);
+       if (!NT_STATUS_IS_OK(status)){
+               DEBUG(10, ("torture_createdel_recv returned %s\n",
+                          nt_errstr(status)));
+               TALLOC_FREE(subreq);
+               tevent_req_nterror(req, status);
+               return;
+       }
+
+       for (i=0; i<num_parallel; i++) {
+               if (subreq == state->reqs[i]) {
+                       break;
+               }
+       }
+       if (i == num_parallel) {
+               DEBUG(10, ("received something we did not send\n"));
+               tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+               return;
+       }
+       TALLOC_FREE(state->reqs[i]);
+
+       if (state->sent >= state->num_files) {
+               tevent_req_done(req);
+               return;
+       }
+
+       name = talloc_asprintf(state, "%s%8.8d", state->base_name,
+                              state->sent);
+       if (tevent_req_nomem(name, req)) {
+               return;
+       }
+       state->reqs[i] = torture_createdel_send(state->reqs, state->ev,
+                                               state->cli, name);
+       if (tevent_req_nomem(state->reqs[i], req)) {
+               return;
+       }
+       name = talloc_move(state->reqs[i], &name);
+       tevent_req_set_callback(state->reqs[i], torture_createdels_done, req);
+       state->sent += 1;
+}
+
+static NTSTATUS torture_createdels_recv(struct tevent_req *req)
+{
+       return tevent_req_simple_recv_ntstatus(req);
+}
+
+struct swallow_notify_state {
+       struct tevent_context *ev;
+       struct cli_state *cli;
+       uint16_t fnum;
+       uint32_t completion_filter;
+       bool recursive;
+       bool (*fn)(uint32_t action, const char *name, void *priv);
+       void *priv;
+};
+
+static void swallow_notify_done(struct tevent_req *subreq);
+
+static struct tevent_req *swallow_notify_send(TALLOC_CTX *mem_ctx,
+                                             struct tevent_context *ev,
+                                             struct cli_state *cli,
+                                             uint16_t fnum,
+                                             uint32_t completion_filter,
+                                             bool recursive,
+                                             bool (*fn)(uint32_t action,
+                                                        const char *name,
+                                                        void *priv),
+                                             void *priv)
+{
+       struct tevent_req *req, *subreq;
+       struct swallow_notify_state *state;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct swallow_notify_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->ev = ev;
+       state->cli = cli;
+       state->fnum = fnum;
+       state->completion_filter = completion_filter;
+       state->recursive = recursive;
+       state->fn = fn;
+       state->priv = priv;
+
+       subreq = cli_notify_send(state, state->ev, state->cli, state->fnum,
+                                0xffff, state->completion_filter,
+                                state->recursive);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, swallow_notify_done, req);
+       return req;
+}
+
+static void swallow_notify_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct swallow_notify_state *state = tevent_req_data(
+               req, struct swallow_notify_state);
+       NTSTATUS status;
+       uint32_t i, num_changes;
+       struct notify_change *changes;
+
+       status = cli_notify_recv(subreq, state, &num_changes, &changes);
+       TALLOC_FREE(subreq);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(10, ("cli_notify_recv returned %s\n",
+                          nt_errstr(status)));
+               tevent_req_nterror(req, status);
+               return;
+       }
+
+       for (i=0; i<num_changes; i++) {
+               state->fn(changes[i].action, changes[i].name, state->priv);
+       }
+       TALLOC_FREE(changes);
+
+       subreq = cli_notify_send(state, state->ev, state->cli, state->fnum,
+                                0xffff, state->completion_filter,
+                                state->recursive);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, swallow_notify_done, req);
+}
+
+static bool print_notifies(uint32_t action, const char *name, void *priv)
+{
+       if (DEBUGLEVEL > 5) {
+               d_printf("%d %s\n", (int)action, name);
+       }
+       return true;
+}
+
+static void notify_bench_done(struct tevent_req *req)
+{
+       int *num_finished = (int *)tevent_req_callback_data_void(req);
+       *num_finished += 1;
+}
+
+static bool run_notify_bench(int dummy)
+{
+       const char *dname = "\\notify-bench";
+       struct tevent_context *ev;
+       NTSTATUS status;
+       uint16_t dnum;
+       struct tevent_req *req1, *req2;
+       int i, num_unc_names;
+       int num_finished = 0;
+
+       printf("starting notify-bench test\n");
+
+       if (use_multishare_conn) {
+               char **unc_list;
+               unc_list = file_lines_load(multishare_conn_fname,
+                                          &num_unc_names, 0, NULL);
+               if (!unc_list || num_unc_names <= 0) {
+                       d_printf("Failed to load unc names list from '%s'\n",
+                                multishare_conn_fname);
+                       return false;
+               }
+               TALLOC_FREE(unc_list);
+       } else {
+               num_unc_names = 1;
+       }
+
+       ev = tevent_context_init(talloc_tos());
+       if (ev == NULL) {
+               d_printf("tevent_context_init failed\n");
+               return false;
+       }
+
+       for (i=0; i<num_unc_names; i++) {
+               struct cli_state *cli;
+               char *base_fname;
+
+               base_fname = talloc_asprintf(talloc_tos(), "%s\\file%3.3d.",
+                                            dname, i);
+               if (base_fname == NULL) {
+                       return false;
+               }
+
+               if (!torture_open_connection(&cli, i)) {
+                       return false;
+               }
+
+               status = cli_ntcreate(cli, dname, 0,
+                                     MAXIMUM_ALLOWED_ACCESS,
+                                     0, FILE_SHARE_READ|FILE_SHARE_WRITE|
+                                     FILE_SHARE_DELETE,
+                                     FILE_OPEN_IF, FILE_DIRECTORY_FILE, 0,
+                                     &dnum);
+
+               if (!NT_STATUS_IS_OK(status)) {
+                       d_printf("Could not create %s: %s\n", dname,
+                                nt_errstr(status));
+                       return false;
+               }
+
+               req1 = swallow_notify_send(talloc_tos(), ev, cli, dnum,
+                                          FILE_NOTIFY_CHANGE_FILE_NAME |
+                                          FILE_NOTIFY_CHANGE_DIR_NAME |
+                                          FILE_NOTIFY_CHANGE_ATTRIBUTES |
+                                          FILE_NOTIFY_CHANGE_LAST_WRITE,
+                                          false, print_notifies, NULL);
+               if (req1 == NULL) {
+                       d_printf("Could not create notify request\n");
+                       return false;
+               }
+
+               req2 = torture_createdels_send(talloc_tos(), ev, cli,
+                                              base_fname, 10, torture_numops);
+               if (req2 == NULL) {
+                       d_printf("Could not create createdels request\n");
+                       return false;
+               }
+               TALLOC_FREE(base_fname);
+
+               tevent_req_set_callback(req2, notify_bench_done,
+                                       &num_finished);
+       }
+
+       while (num_finished < num_unc_names) {
+               int ret;
+               ret = tevent_loop_once(ev);
+               if (ret != 0) {
+                       d_printf("tevent_loop_once failed\n");
+                       return false;
+               }
+       }
+
+       if (!tevent_req_poll(req2, ev)) {
+               d_printf("tevent_req_poll failed\n");
+       }
+
+       status = torture_createdels_recv(req2);
+       d_printf("torture_createdels_recv returned %s\n", nt_errstr(status));
+
+       return true;
+}
+
 static bool run_mangle1(int dummy)
 {
        struct cli_state *cli;
@@ -5326,6 +5952,7 @@ static bool run_windows_write(int dummy)
        int i;
        bool ret = false;
        const char *fname = "\\writetest.txt";
+       struct timeval start;
        double seconds;
        double kbytes;
 
@@ -5341,7 +5968,7 @@ static bool run_windows_write(int dummy)
 
        cli_sockopt(cli1, sockops);
 
-       start_timer();
+       start = timeval_current();
 
        for (i=0; i<torture_numops; i++) {
                char c = 0;
@@ -5363,7 +5990,7 @@ static bool run_windows_write(int dummy)
                }
        }
 
-       seconds = end_timer();
+       seconds = timeval_elapsed(&start);
        kbytes = (double)torture_blocksize * torture_numops;
        kbytes /= 1024;
 
@@ -5807,6 +6434,11 @@ static bool run_local_gencache(int dummy)
                return False;
        }
 
+       if (!gencache_get("foo", NULL, NULL)) {
+               d_printf("%s: gencache_get() failed\n", __location__);
+               return False;
+       }
+
        if (!gencache_get("foo", &val, &tm)) {
                d_printf("%s: gencache_get() failed\n", __location__);
                return False;
@@ -5845,7 +6477,7 @@ static bool run_local_gencache(int dummy)
                return False;
        }
 
-       if (!gencache_get_data_blob("foo", &blob, NULL)) {
+       if (!gencache_get_data_blob("foo", &blob, NULL, NULL)) {
                d_printf("%s: gencache_get_data_blob() failed\n", __location__);
                return False;
        }
@@ -5869,7 +6501,7 @@ static bool run_local_gencache(int dummy)
                return False;
        }
 
-       if (gencache_get_data_blob("foo", &blob, NULL)) {
+       if (gencache_get_data_blob("foo", &blob, NULL, NULL)) {
                d_printf("%s: gencache_get_data_blob() on deleted entry "
                         "succeeded\n", __location__);
                return False;
@@ -5968,6 +6600,53 @@ static bool run_local_rbtree(int dummy)
        return ret;
 }
 
+struct talloc_dict_test {
+       int content;
+};
+
+static int talloc_dict_traverse_fn(DATA_BLOB key, void *data, void *priv)
+{
+       int *count = (int *)priv;
+       *count += 1;
+       return 0;
+}
+
+static bool run_local_talloc_dict(int dummy)
+{
+       struct talloc_dict *dict;
+       struct talloc_dict_test *t;
+       int key, count;
+
+       dict = talloc_dict_init(talloc_tos());
+       if (dict == NULL) {
+               return false;
+       }
+
+       t = talloc(talloc_tos(), struct talloc_dict_test);
+       if (t == NULL) {
+               return false;
+       }
+
+       key = 1;
+       t->content = 1;
+       if (!talloc_dict_set(dict, data_blob_const(&key, sizeof(key)), t)) {
+               return false;
+       }
+
+       count = 0;
+       if (talloc_dict_traverse(dict, talloc_dict_traverse_fn, &count) != 0) {
+               return false;
+       }
+
+       if (count != 1) {
+               return false;
+       }
+
+       TALLOC_FREE(dict);
+
+       return true;
+}
+
 /* Split a path name into filename and stream name components. Canonicalise
  * such that an implicit $DATA token is always explicit.
  *
@@ -6358,6 +7037,7 @@ static double create_procs(bool (*fn)(int), bool *result)
        volatile bool *child_status_out;
        int synccount;
        int tries = 8;
+       struct timeval start;
 
        synccount = 0;
 
@@ -6378,7 +7058,7 @@ static double create_procs(bool (*fn)(int), bool *result)
                child_status_out[i] = True;
        }
 
-       start_timer();
+       start = timeval_current();
 
        for (i=0;i<nprocs;i++) {
                procnum = i;
@@ -6399,7 +7079,7 @@ static double create_procs(bool (*fn)(int), bool *result)
 
                        child_status[i] = getpid();
 
-                       while (child_status[i] && end_timer() < 5) smb_msleep(2);
+                       while (child_status[i] && timeval_elapsed(&start) < 5) smb_msleep(2);
 
                        child_status_out[i] = fn(i);
                        _exit(0);
@@ -6413,16 +7093,16 @@ static double create_procs(bool (*fn)(int), bool *result)
                }
                if (synccount == nprocs) break;
                smb_msleep(10);
-       } while (end_timer() < 30);
+       } while (timeval_elapsed(&start) < 30);
 
        if (synccount != nprocs) {
                printf("FAILED TO START %d CLIENTS (started %d)\n", nprocs, synccount);
                *result = False;
-               return end_timer();
+               return timeval_elapsed(&start);
        }
 
        /* start the client load */
-       start_timer();
+       start = timeval_current();
 
        for (i=0;i<nprocs;i++) {
                child_status[i] = 0;
@@ -6441,7 +7121,7 @@ static double create_procs(bool (*fn)(int), bool *result)
                        *result = False;
                }
        }
-       return end_timer();
+       return timeval_elapsed(&start);
 }
 
 #define FLAG_MULTIPROC 1
@@ -6460,6 +7140,7 @@ static struct {
        {"LOCK6",  run_locktest6,  0},
        {"LOCK7",  run_locktest7,  0},
        {"LOCK8",  run_locktest8,  0},
+       {"LOCK9",  run_locktest9,  0},
        {"UNLINK", run_unlinktest, 0},
        {"BROWSE", run_browsetest, 0},
        {"ATTR",   run_attrtest,   0},
@@ -6514,8 +7195,10 @@ static struct {
        { "GETADDRINFO", run_getaddrinfo_send, 0},
        { "TLDAP", run_tldap },
        { "STREAMERROR", run_streamerror },
+       { "NOTIFY-BENCH", run_notify_bench },
        { "LOCAL-SUBSTITUTE", run_local_substitute, 0},
        { "LOCAL-GENCACHE", run_local_gencache, 0},
+       { "LOCAL-TALLOC-DICT", run_local_talloc_dict, 0},
        { "LOCAL-BASE64", run_local_base64, 0},
        { "LOCAL-RBTREE", run_local_rbtree, 0},
        { "LOCAL-MEMCACHE", run_local_memcache, 0},
@@ -6556,12 +7239,13 @@ static bool run_test(const char *name)
                                        printf("TEST %s FAILED!\n", name);
                                }
                        } else {
-                               start_timer();
+                               struct timeval start;
+                               start = timeval_current();
                                if (!torture_ops[i].fn(0)) {
                                        ret = False;
                                        printf("TEST %s FAILED!\n", name);
                                }
-                               t = end_timer();
+                               t = timeval_elapsed(&start);
                        }
                        printf("%s took %g secs\n\n", name, t);
                }
@@ -6677,7 +7361,7 @@ static void usage(void)
 
        fstrcpy(workgroup, lp_workgroup());
 
-       while ((opt = getopt(argc, argv, "p:hW:U:n:N:O:o:m:Ld:Aec:ks:b:B:")) != EOF) {
+       while ((opt = getopt(argc, argv, "p:hW:U:n:N:O:o:m:Ll:d:Aec:ks:b:B:")) != EOF) {
                switch (opt) {
                case 'p':
                        port_to_use = atoi(optarg);
@@ -6706,6 +7390,9 @@ static void usage(void)
                case 'L':
                        use_oplocks = True;
                        break;
+               case 'l':
+                       local_path = optarg;
+                       break;
                case 'A':
                        torture_showall = True;
                        break;