Make smbtorture4 match Windows behavior.
[samba.git] / source4 / torture / raw / lock.c
index cd8d60679520258a21ab96e28e5f63d53ca73e49..6871ed354698db19d2ad0046e03430df55d54308 100644 (file)
 
 #define CHECK_STATUS(status, correct) do { \
        if (!NT_STATUS_EQUAL(status, correct)) { \
-               printf("(%s) Incorrect status %s - should be %s\n", \
-                      __location__, nt_errstr(status), nt_errstr(correct)); \
+               torture_result(tctx, TORTURE_FAIL, \
+                       "(%s) Incorrect status %s - should be %s\n", \
+                       __location__, nt_errstr(status), nt_errstr(correct)); \
                ret = false; \
                goto done; \
        }} while (0)
 
-#define BASEDIR "\\testlock"
+#define CHECK_STATUS_CONT(status, correct) do { \
+       if (!NT_STATUS_EQUAL(status, correct)) { \
+               torture_result(tctx, TORTURE_FAIL, \
+                       "(%s) Incorrect status %s - should be %s\n", \
+                       __location__, nt_errstr(status), nt_errstr(correct)); \
+               ret = false; \
+       }} while (0)
 
+#define CHECK_STATUS_OR(status, correct1, correct2) do { \
+       if ((!NT_STATUS_EQUAL(status, correct1)) && \
+           (!NT_STATUS_EQUAL(status, correct2))) { \
+               torture_result(tctx, TORTURE_FAIL, \
+                       "(%s) Incorrect status %s - should be %s or %s\n", \
+                       __location__, nt_errstr(status), nt_errstr(correct1), \
+                       nt_errstr(correct2)); \
+               ret = false; \
+               goto done; \
+       }} while (0)
 
+#define CHECK_STATUS_OR_CONT(status, correct1, correct2) do { \
+       if ((!NT_STATUS_EQUAL(status, correct1)) && \
+           (!NT_STATUS_EQUAL(status, correct2))) { \
+               torture_result(tctx, TORTURE_FAIL, \
+                       "(%s) Incorrect status %s - should be %s or %s\n", \
+                       __location__, nt_errstr(status), nt_errstr(correct1), \
+                       nt_errstr(correct2)); \
+               ret = false; \
+       }} while (0)
+#define BASEDIR "\\testlock"
+
+#define TARGET_IS_W2K8(_tctx) (torture_setting_bool(_tctx, "w2k8", false))
+#define TARGET_IS_WIN7(_tctx) (torture_setting_bool(_tctx, "win7", false))
+#define TARGET_IS_WINDOWS(_tctx) ((torture_setting_bool(_tctx, "w2k8", false)) || \
+                                 (torture_setting_bool(_tctx, "win7", false)) || \
+                                 (torture_setting_bool(_tctx, "w2k3", false)))
+#define TARGET_IS_SAMBA3(_tctx) (torture_setting_bool(_tctx, "samba3", false))
+#define TARGET_IS_SAMBA4(_tctx) (torture_setting_bool(_tctx, "samba4", false))
+
+#define TARGET_SUPPORTS_INVALID_LOCK_RANGE(_tctx) \
+       (torture_setting_bool(_tctx, "invalid_lock_range_support", true))
+#define TARGET_SUPPORTS_SMBLOCK(_tctx) \
+    (torture_setting_bool(_tctx, "smblock_pdu_support", true))
+#define TARGET_SUPPORTS_OPENX_DENY_DOS(_tctx) \
+    (torture_setting_bool(_tctx, "openx_deny_dos_support", true))
 /*
   test SMBlock and SMBunlock ops
 */
@@ -52,21 +94,22 @@ static bool test_lock(struct torture_context *tctx, struct smbcli_state *cli)
        int fnum;
        const char *fname = BASEDIR "\\test.txt";
 
+       if (!TARGET_SUPPORTS_SMBLOCK(tctx))
+               torture_skip(tctx, "Target does not support the SMBlock PDU");
+
        if (!torture_setup_dir(cli, BASEDIR)) {
                return false;
        }
 
-       printf("Testing RAW_LOCK_LOCK\n");
+       torture_comment(tctx, "Testing RAW_LOCK_LOCK\n");
        io.generic.level = RAW_LOCK_LOCK;
        
        fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
-       if (fnum == -1) {
-               printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree));
-               ret = false;
-               goto done;
-       }
+       torture_assert(tctx,(fnum != -1), talloc_asprintf(tctx,
+                      "Failed to create %s - %s\n",
+                      fname, smbcli_errstr(cli->tree)));
 
-       printf("Trying 0/0 lock\n");
+       torture_comment(tctx, "Trying 0/0 lock\n");
        io.lock.level = RAW_LOCK_LOCK;
        io.lock.in.file.fnum = fnum;
        io.lock.in.count = 0;
@@ -81,7 +124,7 @@ static bool test_lock(struct torture_context *tctx, struct smbcli_state *cli)
        status = smb_raw_lock(cli->tree, &io);
        CHECK_STATUS(status, NT_STATUS_OK);
 
-       printf("Trying 0/1 lock\n");
+       torture_comment(tctx, "Trying 0/1 lock\n");
        io.lock.level = RAW_LOCK_LOCK;
        io.lock.in.file.fnum = fnum;
        io.lock.in.count = 1;
@@ -99,7 +142,7 @@ static bool test_lock(struct torture_context *tctx, struct smbcli_state *cli)
        status = smb_raw_lock(cli->tree, &io);
        CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
 
-       printf("Trying 0xEEFFFFFF lock\n");
+       torture_comment(tctx, "Trying 0xEEFFFFFF lock\n");
        io.lock.level = RAW_LOCK_LOCK;
        io.lock.in.file.fnum = fnum;
        io.lock.in.count = 4000;
@@ -117,7 +160,7 @@ static bool test_lock(struct torture_context *tctx, struct smbcli_state *cli)
        status = smb_raw_lock(cli->tree, &io);
        CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
 
-       printf("Trying 0xEF000000 lock\n");
+       torture_comment(tctx, "Trying 0xEF000000 lock\n");
        io.lock.level = RAW_LOCK_LOCK;
        io.lock.in.file.fnum = fnum;
        io.lock.in.count = 4000;
@@ -135,7 +178,7 @@ static bool test_lock(struct torture_context *tctx, struct smbcli_state *cli)
        status = smb_raw_lock(cli->tree, &io);
        CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
 
-       printf("Trying max lock\n");
+       torture_comment(tctx, "Trying max lock\n");
        io.lock.level = RAW_LOCK_LOCK;
        io.lock.in.file.fnum = fnum;
        io.lock.in.count = 4000;
@@ -153,7 +196,7 @@ static bool test_lock(struct torture_context *tctx, struct smbcli_state *cli)
        status = smb_raw_lock(cli->tree, &io);
        CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
 
-       printf("Trying wrong pid unlock\n");
+       torture_comment(tctx, "Trying wrong pid unlock\n");
        io.lock.level = RAW_LOCK_LOCK;
        io.lock.in.file.fnum = fnum;
        io.lock.in.count = 4002;
@@ -192,15 +235,13 @@ static bool test_lockx(struct torture_context *tctx, struct smbcli_state *cli)
                return false;
        }
 
-       printf("Testing RAW_LOCK_LOCKX\n");
+       torture_comment(tctx, "Testing RAW_LOCK_LOCKX\n");
        io.generic.level = RAW_LOCK_LOCKX;
        
        fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
-       if (fnum == -1) {
-               printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree));
-               ret = false;
-               goto done;
-       }
+       torture_assert(tctx,(fnum != -1), talloc_asprintf(tctx,
+                      "Failed to create %s - %s\n",
+                      fname, smbcli_errstr(cli->tree)));
 
        io.lockx.level = RAW_LOCK_LOCKX;
        io.lockx.in.file.fnum = fnum;
@@ -216,7 +257,7 @@ static bool test_lockx(struct torture_context *tctx, struct smbcli_state *cli)
        CHECK_STATUS(status, NT_STATUS_OK);
 
 
-       printf("Trying 0xEEFFFFFF lock\n");
+       torture_comment(tctx, "Trying 0xEEFFFFFF lock\n");
        io.lockx.in.ulock_cnt = 0;
        io.lockx.in.lock_cnt = 1;
        lock[0].count = 4000;
@@ -234,7 +275,7 @@ static bool test_lockx(struct torture_context *tctx, struct smbcli_state *cli)
        status = smb_raw_lock(cli->tree, &io);
        CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
 
-       printf("Trying 0xEF000000 lock\n");
+       torture_comment(tctx, "Trying 0xEF000000 lock\n");
        io.lockx.in.ulock_cnt = 0;
        io.lockx.in.lock_cnt = 1;
        lock[0].count = 4000;
@@ -252,7 +293,7 @@ static bool test_lockx(struct torture_context *tctx, struct smbcli_state *cli)
        status = smb_raw_lock(cli->tree, &io);
        CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
 
-       printf("Trying zero lock\n");
+       torture_comment(tctx, "Trying zero lock\n");
        io.lockx.in.ulock_cnt = 0;
        io.lockx.in.lock_cnt = 1;
        lock[0].count = 0;
@@ -270,7 +311,7 @@ static bool test_lockx(struct torture_context *tctx, struct smbcli_state *cli)
        status = smb_raw_lock(cli->tree, &io);
        CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
 
-       printf("Trying max lock\n");
+       torture_comment(tctx, "Trying max lock\n");
        io.lockx.in.ulock_cnt = 0;
        io.lockx.in.lock_cnt = 1;
        lock[0].count = 0;
@@ -288,7 +329,7 @@ static bool test_lockx(struct torture_context *tctx, struct smbcli_state *cli)
        status = smb_raw_lock(cli->tree, &io);
        CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
 
-       printf("Trying 2^63\n");
+       torture_comment(tctx, "Trying 2^63\n");
        io.lockx.in.ulock_cnt = 0;
        io.lockx.in.lock_cnt = 1;
        lock[0].count = 1;
@@ -307,7 +348,7 @@ static bool test_lockx(struct torture_context *tctx, struct smbcli_state *cli)
        status = smb_raw_lock(cli->tree, &io);
        CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
 
-       printf("Trying 2^63 - 1\n");
+       torture_comment(tctx, "Trying 2^63 - 1\n");
        io.lockx.in.ulock_cnt = 0;
        io.lockx.in.lock_cnt = 1;
        lock[0].count = 1;
@@ -327,7 +368,7 @@ static bool test_lockx(struct torture_context *tctx, struct smbcli_state *cli)
        status = smb_raw_lock(cli->tree, &io);
        CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
 
-       printf("Trying max lock 2\n");
+       torture_comment(tctx, "Trying max lock 2\n");
        io.lockx.in.ulock_cnt = 0;
        io.lockx.in.lock_cnt = 1;
        lock[0].count = 1;
@@ -337,12 +378,16 @@ static bool test_lockx(struct torture_context *tctx, struct smbcli_state *cli)
        lock[0].pid++;
        lock[0].count = 2;
        status = smb_raw_lock(cli->tree, &io);
-       CHECK_STATUS(status, NT_STATUS_OK);
+       if (TARGET_SUPPORTS_INVALID_LOCK_RANGE(tctx))
+               CHECK_STATUS(status, NT_STATUS_INVALID_LOCK_RANGE);
+       else
+               CHECK_STATUS(status, NT_STATUS_OK);
        lock[0].pid--;
        io.lockx.in.ulock_cnt = 1;
        io.lockx.in.lock_cnt = 0;
        lock[0].count = 1;
        status = smb_raw_lock(cli->tree, &io);
+
        CHECK_STATUS(status, NT_STATUS_OK);
        status = smb_raw_lock(cli->tree, &io);
        CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
@@ -354,7 +399,6 @@ done:
        return ret;
 }
 
-
 /*
   test high pid
 */
@@ -373,20 +417,20 @@ static bool test_pidhigh(struct torture_context *tctx,
                return false;
        }
 
-       printf("Testing high pid\n");
+       torture_comment(tctx, "Testing high pid\n");
        io.generic.level = RAW_LOCK_LOCKX;
 
        cli->session->pid = 1;
        
        fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
-       if (fnum == -1) {
-               printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree));
-               ret = false;
-               goto done;
-       }
+       torture_assert(tctx,(fnum != -1), talloc_asprintf(tctx,
+                      "Failed to create %s - %s\n",
+                      fname, smbcli_errstr(cli->tree)));
 
        if (smbcli_write(cli->tree, fnum, 0, &c, 0, 1) != 1) {
-               printf("Failed to write 1 byte - %s\n", smbcli_errstr(cli->tree));
+               torture_result(tctx, TORTURE_FAIL,
+                       "Failed to write 1 byte - %s\n",
+                       smbcli_errstr(cli->tree));
                ret = false;
                goto done;
        }
@@ -405,17 +449,18 @@ static bool test_pidhigh(struct torture_context *tctx,
        CHECK_STATUS(status, NT_STATUS_OK);
 
        if (smbcli_read(cli->tree, fnum, &c, 0, 1) != 1) {
-               printf("Failed to read 1 byte - %s\n", smbcli_errstr(cli->tree));
+               torture_result(tctx, TORTURE_FAIL,
+                       "Failed to read 1 byte - %s\n",
+                       smbcli_errstr(cli->tree));
                ret = false;
                goto done;
        }
 
-       cli->session->pid |= 0x10000;
-
        cli->session->pid = 2;
 
        if (smbcli_read(cli->tree, fnum, &c, 0, 1) == 1) {
-               printf("pid is incorrect handled for read with lock!\n");
+               torture_result(tctx, TORTURE_FAIL,
+                       "pid is incorrect handled for read with lock!\n");
                ret = false;
                goto done;
        }
@@ -423,10 +468,11 @@ static bool test_pidhigh(struct torture_context *tctx,
        cli->session->pid = 0x10001;
 
        if (smbcli_read(cli->tree, fnum, &c, 0, 1) != 1) {
-               printf("High pid is used on this server!\n");
+               torture_result(tctx, TORTURE_FAIL,
+                       "High pid is used on this server!\n");
                ret = false;
        } else {
-               printf("High pid is not used on this server (correct)\n");
+               torture_warning(tctx, "High pid is not used on this server (correct)\n");
        }
 
 done:
@@ -455,21 +501,22 @@ static bool test_async(struct torture_context *tctx,
        int fnum;
        const char *fname = BASEDIR "\\test.txt";
        time_t t;
-       struct smbcli_request *req;
+       struct smbcli_request *req, *req2;
+       struct smbcli_session_options options;
 
        if (!torture_setup_dir(cli, BASEDIR)) {
                return false;
        }
 
-       printf("Testing LOCKING_ANDX_CANCEL_LOCK\n");
+       lp_smbcli_session_options(tctx->lp_ctx, &options);
+
+       torture_comment(tctx, "Testing LOCKING_ANDX_CANCEL_LOCK\n");
        io.generic.level = RAW_LOCK_LOCKX;
 
        fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
-       if (fnum == -1) {
-               printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree));
-               ret = false;
-               goto done;
-       }
+       torture_assert(tctx,(fnum != -1), talloc_asprintf(tctx,
+                      "Failed to create %s - %s\n",
+                      fname, smbcli_errstr(cli->tree)));
 
        io.lockx.level = RAW_LOCK_LOCKX;
        io.lockx.in.file.fnum = fnum;
@@ -480,22 +527,22 @@ static bool test_async(struct torture_context *tctx,
        lock[0].pid = cli->session->pid;
        lock[0].offset = 100;
        lock[0].count = 10;
+       lock[1].pid = cli->session->pid;
+       lock[1].offset = 110;
+       lock[1].count = 10;
        io.lockx.in.locks = &lock[0];
        status = smb_raw_lock(cli->tree, &io);
        CHECK_STATUS(status, NT_STATUS_OK);
 
        t = time(NULL);
 
-       printf("testing cancel by CANCEL_LOCK\n");
+       torture_comment(tctx, "testing cancel by CANCEL_LOCK\n");
 
        /* setup a timed lock */
        io.lockx.in.timeout = 10000;
        req = smb_raw_lock_send(cli->tree, &io);
-       if (req == NULL) {
-               printf("Failed to setup timed lock (%s)\n", __location__);
-               ret = false;
-               goto done;
-       }
+       torture_assert(tctx,(req != NULL), talloc_asprintf(tctx,
+                      "Failed to setup timed lock (%s)\n", __location__));
 
        /* cancel the wrong range */
        lock[0].offset = 0;
@@ -522,27 +569,96 @@ static bool test_async(struct torture_context *tctx,
        status = smbcli_request_simple_recv(req);
        CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
 
-       if (time(NULL) > t+2) {
-               printf("lock cancel was not immediate (%s)\n", __location__);
-               ret = false;
-               goto done;
-       }
+       torture_assert(tctx,!(time(NULL) > t+2), talloc_asprintf(tctx,
+                      "lock cancel was not immediate (%s)\n", __location__));
 
-       printf("testing cancel by unlock\n");
+       /* MS-CIFS (2.2.4.32.1) states that a cancel is honored if and only
+        * if the lock vector contains one entry. When given mutliple cancel
+        * requests in a single PDU we expect the server to return an
+        * error. Samba4 handles this correctly. Windows servers seem to
+        * accept the request but only cancel the first lock.  Samba3
+        * now does what Windows does (JRA).
+        */
+       torture_comment(tctx, "testing multiple cancel\n");
+
+       /* acquire second lock */
+       io.lockx.in.timeout = 0;
        io.lockx.in.ulock_cnt = 0;
        io.lockx.in.lock_cnt = 1;
        io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES;
+       io.lockx.in.locks = &lock[1];
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       /* setup 2 timed locks */
+       t = time(NULL);
+       io.lockx.in.timeout = 10000;
+       io.lockx.in.lock_cnt = 1;
+       io.lockx.in.locks = &lock[0];
+       req = smb_raw_lock_send(cli->tree, &io);
+       torture_assert(tctx,(req != NULL), talloc_asprintf(tctx,
+                      "Failed to setup timed lock (%s)\n", __location__));
+       io.lockx.in.locks = &lock[1];
+       req2 = smb_raw_lock_send(cli->tree, &io);
+       torture_assert(tctx,(req2 != NULL), talloc_asprintf(tctx,
+                      "Failed to setup timed lock (%s)\n", __location__));
+
+       /* try to cancel both locks in the same packet */
        io.lockx.in.timeout = 0;
+       io.lockx.in.lock_cnt = 2;
+       io.lockx.in.mode = LOCKING_ANDX_CANCEL_LOCK | LOCKING_ANDX_LARGE_FILES;
+       io.lockx.in.locks = lock;
        status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       torture_warning(tctx, "Target server accepted a lock cancel "
+                             "request with multiple locks. This violates "
+                             "MS-CIFS 2.2.4.32.1.\n");
+
+       /* receive the failed lock requests */
+       status = smbcli_request_simple_recv(req);
        CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
 
+       torture_assert(tctx,!(time(NULL) > t+2), talloc_asprintf(tctx,
+                      "first lock was not cancelled immediately (%s)\n",
+                      __location__));
+
+       /* send cancel to second lock */
+       io.lockx.in.timeout = 0;
+       io.lockx.in.lock_cnt = 1;
+       io.lockx.in.mode = LOCKING_ANDX_CANCEL_LOCK |
+                          LOCKING_ANDX_LARGE_FILES;
+       io.lockx.in.locks = &lock[1];
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       status = smbcli_request_simple_recv(req2);
+       CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+       torture_assert(tctx,!(time(NULL) > t+2), talloc_asprintf(tctx,
+                      "second lock was not cancelled immediately (%s)\n",
+                      __location__));
+
+       /* cleanup the second lock */
+       io.lockx.in.ulock_cnt = 1;
+       io.lockx.in.lock_cnt = 0;
+       io.lockx.in.locks = &lock[1];
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       torture_comment(tctx, "testing cancel by unlock\n");
+       io.lockx.in.ulock_cnt = 0;
+       io.lockx.in.lock_cnt = 1;
+       io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES;
+       io.lockx.in.timeout = 0;
+       io.lockx.in.locks = &lock[0];
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED);
+
        io.lockx.in.timeout = 5000;
        req = smb_raw_lock_send(cli->tree, &io);
-       if (req == NULL) {
-               printf("Failed to setup timed lock (%s)\n", __location__);
-               ret = false;
-               goto done;
-       }
+       torture_assert(tctx,(req != NULL), talloc_asprintf(tctx,
+                      "Failed to setup timed lock (%s)\n", __location__));
 
        io.lockx.in.ulock_cnt = 1;
        io.lockx.in.lock_cnt = 0;
@@ -553,14 +669,11 @@ static bool test_async(struct torture_context *tctx,
        status = smbcli_request_simple_recv(req);
        CHECK_STATUS(status, NT_STATUS_OK);
 
-       if (time(NULL) > t+2) {
-               printf("lock cancel by unlock was not immediate (%s) - took %d secs\n", 
-                      __location__, (int)(time(NULL)-t));
-               ret = false;
-               goto done;
-       }
+       torture_assert(tctx,!(time(NULL) > t+2), talloc_asprintf(tctx,
+                      "lock cancel by unlock was not immediate (%s) - took %d secs\n",
+                      __location__, (int)(time(NULL)-t)));
 
-       printf("testing cancel by close\n");
+       torture_comment(tctx, "testing cancel by close\n");
        io.lockx.in.ulock_cnt = 0;
        io.lockx.in.lock_cnt = 1;
        io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES;
@@ -571,11 +684,8 @@ static bool test_async(struct torture_context *tctx,
        t = time(NULL);
        io.lockx.in.timeout = 10000;
        req = smb_raw_lock_send(cli->tree, &io);
-       if (req == NULL) {
-               printf("Failed to setup timed lock (%s)\n", __location__);
-               ret = false;
-               goto done;
-       }
+       torture_assert(tctx,(req != NULL), talloc_asprintf(tctx,
+                      "Failed to setup timed lock (%s)\n", __location__));
 
        status = smbcli_close(cli->tree, fnum);
        CHECK_STATUS(status, NT_STATUS_OK);
@@ -583,23 +693,21 @@ static bool test_async(struct torture_context *tctx,
        status = smbcli_request_simple_recv(req);
        CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
 
-       if (time(NULL) > t+2) {
-               printf("lock cancel by close was not immediate (%s)\n", __location__);
-               ret = false;
-               goto done;
-       }
+       torture_assert(tctx,!(time(NULL) > t+2), talloc_asprintf(tctx,
+                      "lock cancel by close was not immediate (%s)\n", __location__));
 
-       printf("create a new sessions\n");
-       session = smbcli_session_init(cli->transport, tctx, false);
+       torture_comment(tctx, "create a new sessions\n");
+       session = smbcli_session_init(cli->transport, tctx, false, options);
        setup.in.sesskey = cli->transport->negotiate.sesskey;
        setup.in.capabilities = cli->transport->negotiate.capabilities;
        setup.in.workgroup = lp_workgroup(tctx->lp_ctx);
        setup.in.credentials = cmdline_credentials;
+       setup.in.gensec_settings = lp_gensec_settings(tctx, tctx->lp_ctx);
        status = smb_composite_sesssetup(session, &setup);
        CHECK_STATUS(status, NT_STATUS_OK);
        session->vuid = setup.out.vuid;
 
-       printf("create new tree context\n");
+       torture_comment(tctx, "create new tree context\n");
        share = torture_setting_string(tctx, "share", NULL);
        host  = torture_setting_string(tctx, "host", NULL);
        tree = smbcli_tree_init(session, tctx, false);
@@ -612,14 +720,13 @@ static bool test_async(struct torture_context *tctx,
        CHECK_STATUS(status, NT_STATUS_OK);
        tree->tid = tcon.tconx.out.tid;
 
-       printf("testing cancel by exit\n");
+       torture_comment(tctx, "testing cancel by exit\n");
        fname = BASEDIR "\\test_exit.txt";
        fnum = smbcli_open(tree, fname, O_RDWR|O_CREAT, DENY_NONE);
-       if (fnum == -1) {
-               printf("Failed to reopen %s - %s\n", fname, smbcli_errstr(tree));
-               ret = false;
-               goto done;
-       }
+       torture_assert(tctx,(fnum != -1), talloc_asprintf(tctx,
+                      "Failed to reopen %s - %s\n",
+                      fname, smbcli_errstr(tree)));
+
        io.lockx.level = RAW_LOCK_LOCKX;
        io.lockx.in.file.fnum = fnum;
        io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES;
@@ -643,11 +750,8 @@ static bool test_async(struct torture_context *tctx,
        io.lockx.in.timeout = 10000;
        t = time(NULL);
        req = smb_raw_lock_send(tree, &io);
-       if (req == NULL) {
-               printf("Failed to setup timed lock (%s)\n", __location__);
-               ret = false;
-               goto done;
-       }
+       torture_assert(tctx,(req != NULL), talloc_asprintf(tctx,
+                      "Failed to setup timed lock (%s)\n", __location__));
 
        status = smb_raw_exit(session);
        CHECK_STATUS(status, NT_STATUS_OK);
@@ -655,20 +759,16 @@ static bool test_async(struct torture_context *tctx,
        status = smbcli_request_simple_recv(req);
        CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
 
-       if (time(NULL) > t+2) {
-               printf("lock cancel by exit was not immediate (%s)\n", __location__);
-               ret = false;
-               goto done;
-       }
+       torture_assert(tctx,!(time(NULL) > t+2), talloc_asprintf(tctx,
+                      "lock cancel by exit was not immediate (%s)\n", __location__));
 
-       printf("testing cancel by ulogoff\n");
+       torture_comment(tctx, "testing cancel by ulogoff\n");
        fname = BASEDIR "\\test_ulogoff.txt";
        fnum = smbcli_open(tree, fname, O_RDWR|O_CREAT, DENY_NONE);
-       if (fnum == -1) {
-               printf("Failed to reopen %s - %s\n", fname, smbcli_errstr(tree));
-               ret = false;
-               goto done;
-       }
+       torture_assert(tctx,(fnum != -1), talloc_asprintf(tctx,
+                      "Failed to reopen %s - %s\n",
+                      fname, smbcli_errstr(tree)));
+
        io.lockx.level = RAW_LOCK_LOCKX;
        io.lockx.in.file.fnum = fnum;
        io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES;
@@ -692,18 +792,16 @@ static bool test_async(struct torture_context *tctx,
        io.lockx.in.timeout = 10000;
        t = time(NULL);
        req = smb_raw_lock_send(tree, &io);
-       if (req == NULL) {
-               printf("Failed to setup timed lock (%s)\n", __location__);
-               ret = false;
-               goto done;
-       }
+       torture_assert(tctx,(req != NULL), talloc_asprintf(tctx,
+                      "Failed to setup timed lock (%s)\n", __location__));
 
        status = smb_raw_ulogoff(session);
        CHECK_STATUS(status, NT_STATUS_OK);
 
        status = smbcli_request_simple_recv(req);
        if (NT_STATUS_EQUAL(NT_STATUS_FILE_LOCK_CONFLICT, status)) {
-               printf("lock not canceled by ulogoff - %s (ignored because of vfs_vifs fails it)\n",
+               torture_result(tctx, TORTURE_FAIL,
+                       "lock not canceled by ulogoff - %s (ignored because of vfs_vifs fails it)\n",
                        nt_errstr(status));
                smb_tree_disconnect(tree);
                smb_raw_exit(session);
@@ -711,22 +809,18 @@ static bool test_async(struct torture_context *tctx,
        }
        CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
 
-       if (time(NULL) > t+2) {
-               printf("lock cancel by ulogoff was not immediate (%s)\n", __location__);
-               ret = false;
-               goto done;
-       }
+       torture_assert(tctx,!(time(NULL) > t+2), talloc_asprintf(tctx,
+                      "lock cancel by ulogoff was not immediate (%s)\n", __location__));
 
-       printf("testing cancel by tdis\n");
+       torture_comment(tctx, "testing cancel by tdis\n");
        tree->session = cli->session;
 
        fname = BASEDIR "\\test_tdis.txt";
        fnum = smbcli_open(tree, fname, O_RDWR|O_CREAT, DENY_NONE);
-       if (fnum == -1) {
-               printf("Failed to reopen %s - %s\n", fname, smbcli_errstr(tree));
-               ret = false;
-               goto done;
-       }
+       torture_assert(tctx,(fnum != -1), talloc_asprintf(tctx,
+                      "Failed to reopen %s - %s\n",
+                      fname, smbcli_errstr(tree)));
+
        io.lockx.level = RAW_LOCK_LOCKX;
        io.lockx.in.file.fnum = fnum;
        io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES;
@@ -746,11 +840,8 @@ static bool test_async(struct torture_context *tctx,
        io.lockx.in.timeout = 10000;
        t = time(NULL);
        req = smb_raw_lock_send(tree, &io);
-       if (req == NULL) {
-               printf("Failed to setup timed lock (%s)\n", __location__);
-               ret = false;
-               goto done;
-       }
+       torture_assert(tctx,(req != NULL), talloc_asprintf(tctx,
+                      "Failed to setup timed lock (%s)\n", __location__));
 
        status = smb_tree_disconnect(tree);
        CHECK_STATUS(status, NT_STATUS_OK);
@@ -758,11 +849,8 @@ static bool test_async(struct torture_context *tctx,
        status = smbcli_request_simple_recv(req);
        CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
 
-       if (time(NULL) > t+2) {
-               printf("lock cancel by tdis was not immediate (%s)\n", __location__);
-               ret = false;
-               goto done;
-       }
+       torture_assert(tctx,!(time(NULL) > t+2), talloc_asprintf(tctx,
+                      "lock cancel by tdis was not immediate (%s)\n", __location__));
 
 done:
        smb_raw_exit(cli->session);
@@ -786,14 +874,16 @@ static bool test_errorcode(struct torture_context *tctx,
        struct smbcli_request *req;
        time_t start;
        int t;
+       int delay;
+       uint16_t deny_mode = 0;
 
        if (!torture_setup_dir(cli, BASEDIR)) {
                return false;
        }
 
-       printf("Testing LOCK_NOT_GRANTED vs. FILE_LOCK_CONFLICT\n");
+       torture_comment(tctx, "Testing LOCK_NOT_GRANTED vs. FILE_LOCK_CONFLICT\n");
 
-       printf("testing with timeout = 0\n");
+       torture_comment(tctx, "testing with timeout = 0\n");
        fname = BASEDIR "\\test0.txt";
        t = 0;
 
@@ -802,14 +892,20 @@ static bool test_errorcode(struct torture_context *tctx,
         * the second with t > 0 (=1)
         */
 next_run:
-       /* 
-        * use the DENY_DOS mode, that creates two fnum's of one low-level file handle,
-        * this demonstrates that the cache is per fnum
+       /*
+        * use the DENY_DOS mode, that creates two fnum's of one low-level
+        * file handle, this demonstrates that the cache is per fnum, not
+        * per file handle
         */
+       if (TARGET_SUPPORTS_OPENX_DENY_DOS(tctx))
+           deny_mode = OPENX_MODE_DENY_DOS;
+       else
+           deny_mode = OPENX_MODE_DENY_NONE;
+
        op.openx.level = RAW_OPEN_OPENX;
        op.openx.in.fname = fname;
        op.openx.in.flags = OPENX_FLAGS_ADDITIONAL_INFO;
-       op.openx.in.open_mode = OPENX_MODE_ACCESS_RDWR | OPENX_MODE_DENY_DOS;
+       op.openx.in.open_mode = OPENX_MODE_ACCESS_RDWR | deny_mode;
        op.openx.in.open_func = OPENX_OPEN_FUNC_OPEN | OPENX_OPEN_FUNC_CREATE;
        op.openx.in.search_attrs = 0;
        op.openx.in.file_attrs = 0;
@@ -1060,7 +1156,7 @@ next_run:
        /* 
         * demonstrate the a successful lock in a different range, 
         * doesn't reset the cache, the failing lock on the 2nd handle
-        * resets the resets the cache
+        * resets the cache
         */
        lock[0].offset = 120;
        lock[0].count = 15;
@@ -1089,26 +1185,30 @@ next_run:
        /* end of the loop */
        if (t == 0) {
                smb_raw_exit(cli->session);
-               printf("testing with timeout > 0 (=1)\n");
-               fname = BASEDIR "\\test1.txt";
                t = 1;
+               torture_comment(tctx, "testing with timeout > 0 (=%d)\n",
+                               t);
+               fname = BASEDIR "\\test1.txt";
                goto next_run;
        }
 
+       t = 4000;
+       torture_comment(tctx, "testing special cases with timeout > 0 (=%d)\n",
+                       t);
+
        /*
         * the following 3 test sections demonstrate that
         * the cache is only set when the error is reported
         * to the client (after the timeout went by)
         */
        smb_raw_exit(cli->session);
-       printf("testing a conflict while a lock is pending\n");
+       torture_comment(tctx, "testing a conflict while a lock is pending\n");
        fname = BASEDIR "\\test2.txt";
        fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
-       if (fnum == -1) {
-               printf("Failed to reopen %s - %s\n", fname, smbcli_errstr(cli->tree));
-               ret = false;
-               goto done;
-       }
+       torture_assert(tctx,(fnum != -1), talloc_asprintf(tctx,
+                      "Failed to reopen %s - %s\n",
+                      fname, smbcli_errstr(cli->tree)));
+
        io.lockx.level = RAW_LOCK_LOCKX;
        io.lockx.in.file.fnum = fnum;
        io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES;
@@ -1123,13 +1223,10 @@ next_run:
        CHECK_STATUS(status, NT_STATUS_OK);
 
        start = time(NULL);
-       io.lockx.in.timeout = 1000;
+       io.lockx.in.timeout = t;
        req = smb_raw_lock_send(cli->tree, &io);
-       if (req == NULL) {
-               printf("Failed to setup timed lock (%s)\n", __location__);
-               ret = false;
-               goto done;
-       }
+       torture_assert(tctx,(req != NULL), talloc_asprintf(tctx,
+                      "Failed to setup timed lock (%s)\n", __location__));
 
        io.lockx.in.timeout = 0;
        lock[0].offset = 105;
@@ -1140,23 +1237,25 @@ next_run:
        status = smbcli_request_simple_recv(req);
        CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
 
+       delay = t / 1000;
+       if (TARGET_IS_W2K8(tctx) || TARGET_IS_WIN7(tctx)) {
+               delay /= 2;
+       }
+
+       torture_assert(tctx,!(time(NULL) < start+delay), talloc_asprintf(tctx,
+                      "lock comes back to early timeout[%d] delay[%d]"
+                      "(%s)\n", t, delay, __location__));
+
        status = smb_raw_lock(cli->tree, &io);
        CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED);
 
-       if (time(NULL) < start+1) {
-               printf("lock comes back to early (%s)\n", __location__);
-               ret = false;
-               goto done;
-       }
-
        smbcli_close(cli->tree, fnum);
        fname = BASEDIR "\\test3.txt";
        fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
-       if (fnum == -1) {
-               printf("Failed to reopen %s - %s\n", fname, smbcli_errstr(cli->tree));
-               ret = false;
-               goto done;
-       }
+       torture_assert(tctx,(fnum != -1), talloc_asprintf(tctx,
+                      "Failed to reopen %s - %s\n",
+                      fname, smbcli_errstr(cli->tree)));
+
        io.lockx.level = RAW_LOCK_LOCKX;
        io.lockx.in.file.fnum = fnum;
        io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES;
@@ -1171,13 +1270,10 @@ next_run:
        CHECK_STATUS(status, NT_STATUS_OK);
 
        start = time(NULL);
-       io.lockx.in.timeout = 1000;
+       io.lockx.in.timeout = t;
        req = smb_raw_lock_send(cli->tree, &io);
-       if (req == NULL) {
-               printf("Failed to setup timed lock (%s)\n", __location__);
-               ret = false;
-               goto done;
-       }
+       torture_assert(tctx,(req != NULL), talloc_asprintf(tctx,
+                      "Failed to setup timed lock (%s)\n", __location__));
 
        io.lockx.in.timeout = 0;
        lock[0].offset = 105;
@@ -1188,25 +1284,27 @@ next_run:
        status = smbcli_request_simple_recv(req);
        CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
 
+       delay = t / 1000;
+       if (TARGET_IS_W2K8(tctx) || TARGET_IS_WIN7(tctx)) {
+               delay /= 2;
+       }
+
+       torture_assert(tctx,!(time(NULL) < start+delay), talloc_asprintf(tctx,
+                      "lock comes back to early timeout[%d] delay[%d]"
+                      "(%s)\n", t, delay, __location__));
+
        lock[0].offset = 100;
        lock[0].count = 10;
        status = smb_raw_lock(cli->tree, &io);
        CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
 
-       if (time(NULL) < start+1) {
-               printf("lock comes back to early (%s)\n", __location__);
-               ret = false;
-               goto done;
-       }
-
        smbcli_close(cli->tree, fnum);
        fname = BASEDIR "\\test4.txt";
        fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
-       if (fnum == -1) {
-               printf("Failed to reopen %s - %s\n", fname, smbcli_errstr(cli->tree));
-               ret = false;
-               goto done;
-       }
+       torture_assert(tctx,(fnum != -1), talloc_asprintf(tctx,
+                      "Failed to reopen %s - %s\n",
+                      fname, smbcli_errstr(cli->tree)));
+
        io.lockx.level = RAW_LOCK_LOCKX;
        io.lockx.in.file.fnum = fnum;
        io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES;
@@ -1221,13 +1319,10 @@ next_run:
        CHECK_STATUS(status, NT_STATUS_OK);
 
        start = time(NULL);
-       io.lockx.in.timeout = 1000;
+       io.lockx.in.timeout = t;
        req = smb_raw_lock_send(cli->tree, &io);
-       if (req == NULL) {
-               printf("Failed to setup timed lock (%s)\n", __location__);
-               ret = false;
-               goto done;
-       }
+       torture_assert(tctx,(req != NULL), talloc_asprintf(tctx,
+                      "Failed to setup timed lock (%s)\n", __location__));
 
        io.lockx.in.timeout = 0;
        status = smb_raw_lock(cli->tree, &io);
@@ -1236,15 +1331,18 @@ next_run:
        status = smbcli_request_simple_recv(req);
        CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
 
+       delay = t / 1000;
+       if (TARGET_IS_W2K8(tctx) || TARGET_IS_WIN7(tctx)) {
+               delay /= 2;
+       }
+
+       torture_assert(tctx,!(time(NULL) < start+delay), talloc_asprintf(tctx,
+                      "lock comes back to early timeout[%d] delay[%d]"
+                      "(%s)\n", t, delay, __location__));
+
        status = smb_raw_lock(cli->tree, &io);
        CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
 
-       if (time(NULL) < start+1) {
-               printf("lock comes back to early (%s)\n", __location__);
-               ret = false;
-               goto done;
-       }
-
 done:
        smb_raw_exit(cli->session);
        smbcli_deltree(cli->tree, BASEDIR);
@@ -1270,15 +1368,13 @@ static bool test_changetype(struct torture_context *tctx,
                return false;
        }
 
-       printf("Testing LOCKING_ANDX_CHANGE_LOCKTYPE\n");
+       torture_comment(tctx, "Testing LOCKING_ANDX_CHANGE_LOCKTYPE\n");
        io.generic.level = RAW_LOCK_LOCKX;
        
        fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
-       if (fnum == -1) {
-               printf("Failed to create %s - %s\n", fname, smbcli_errstr(cli->tree));
-               ret = false;
-               goto done;
-       }
+       torture_assert(tctx,(fnum != -1), talloc_asprintf(tctx,
+                      "Failed to create %s - %s\n",
+                      fname, smbcli_errstr(cli->tree)));
 
        io.lockx.level = RAW_LOCK_LOCKX;
        io.lockx.in.file.fnum = fnum;
@@ -1294,7 +1390,8 @@ static bool test_changetype(struct torture_context *tctx,
        CHECK_STATUS(status, NT_STATUS_OK);
 
        if (smbcli_write(cli->tree, fnum, 0, &c, 100, 1) == 1) {
-               printf("allowed write on read locked region (%s)\n", __location__);
+               torture_result(tctx, TORTURE_FAIL,
+                       "allowed write on read locked region (%s)\n", __location__);
                ret = false;
                goto done;
        }
@@ -1305,7 +1402,8 @@ static bool test_changetype(struct torture_context *tctx,
        CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRnoatomiclocks));
 
        if (smbcli_write(cli->tree, fnum, 0, &c, 100, 1) == 1) {
-               printf("allowed write after lock change (%s)\n", __location__);
+               torture_result(tctx, TORTURE_FAIL,
+                       "allowed write after lock change (%s)\n", __location__);
                ret = false;
                goto done;
        }
@@ -1317,6 +1415,586 @@ done:
        return ret;
 }
 
+struct double_lock_test {
+       struct smb_lock_entry lock1;
+       struct smb_lock_entry lock2;
+       NTSTATUS exp_status;
+};
+
+/**
+ * Tests zero byte locks.
+ */
+static struct double_lock_test zero_byte_tests[] = {
+       /* {pid, offset, count}, {pid, offset, count}, status */
+
+       /** First, takes a zero byte lock at offset 10. Then:
+       *   - Taking 0 byte lock at 10 should succeed.
+       *   - Taking 1 byte locks at 9,10,11 should succeed.
+       *   - Taking 2 byte lock at 9 should fail.
+       *   - Taking 2 byte lock at 10 should succeed.
+       *   - Taking 3 byte lock at 9 should fail.
+       */
+       {{1000, 10, 0}, {1001, 10, 0}, NT_STATUS_OK},
+       {{1000, 10, 0}, {1001, 9, 1},  NT_STATUS_OK},
+       {{1000, 10, 0}, {1001, 10, 1}, NT_STATUS_OK},
+       {{1000, 10, 0}, {1001, 11, 1}, NT_STATUS_OK},
+       {{1000, 10, 0}, {1001, 9, 2},  NT_STATUS_LOCK_NOT_GRANTED},
+       {{1000, 10, 0}, {1001, 10, 2}, NT_STATUS_OK},
+       {{1000, 10, 0}, {1001, 9, 3},  NT_STATUS_LOCK_NOT_GRANTED},
+
+       /** Same, but opposite order. */
+       {{1001, 10, 0}, {1000, 10, 0}, NT_STATUS_OK},
+       {{1001, 9, 1},  {1000, 10, 0}, NT_STATUS_OK},
+       {{1001, 10, 1}, {1000, 10, 0}, NT_STATUS_OK},
+       {{1001, 11, 1}, {1000, 10, 0}, NT_STATUS_OK},
+       {{1001, 9, 2},  {1000, 10, 0}, NT_STATUS_LOCK_NOT_GRANTED},
+       {{1001, 10, 2}, {1000, 10, 0}, NT_STATUS_OK},
+       {{1001, 9, 3},  {1000, 10, 0}, NT_STATUS_LOCK_NOT_GRANTED},
+
+       /** Zero zero case. */
+       {{1000, 0, 0},  {1001, 0, 0},  NT_STATUS_OK},
+};
+
+static bool test_zerobytelocks(struct torture_context *tctx, struct smbcli_state *cli)
+{
+       union smb_lock io;
+       NTSTATUS status;
+       bool ret = true;
+       int fnum, i;
+       const char *fname = BASEDIR "\\zero.txt";
+
+       torture_comment(tctx, "Testing zero length byte range locks:\n");
+
+       if (!torture_setup_dir(cli, BASEDIR)) {
+               return false;
+       }
+
+       io.generic.level = RAW_LOCK_LOCKX;
+
+       fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
+       torture_assert(tctx,(fnum != -1), talloc_asprintf(tctx,
+                      "Failed to create %s - %s\n",
+                      fname, smbcli_errstr(cli->tree)));
+
+       /* Setup initial parameters */
+       io.lockx.level = RAW_LOCK_LOCKX;
+       io.lockx.in.file.fnum = fnum;
+       io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES; /* Exclusive */
+       io.lockx.in.timeout = 0;
+
+       /* Try every combination of locks in zero_byte_tests. The first lock is
+        * assumed to succeed. The second lock may contend, depending on the
+        * expected status. */
+       for (i = 0;
+            i < ARRAY_SIZE(zero_byte_tests);
+            i++) {
+               torture_comment(tctx, "  ... {%d, %llu, %llu} + {%d, %llu, %llu} = %s\n",
+                   zero_byte_tests[i].lock1.pid,
+                   zero_byte_tests[i].lock1.offset,
+                   zero_byte_tests[i].lock1.count,
+                   zero_byte_tests[i].lock2.pid,
+                   zero_byte_tests[i].lock2.offset,
+                   zero_byte_tests[i].lock2.count,
+                   nt_errstr(zero_byte_tests[i].exp_status));
+
+               /* Lock both locks. */
+               io.lockx.in.ulock_cnt = 0;
+               io.lockx.in.lock_cnt = 1;
+
+               io.lockx.in.locks = discard_const_p(struct smb_lock_entry,
+                                                   &zero_byte_tests[i].lock1);
+               status = smb_raw_lock(cli->tree, &io);
+               CHECK_STATUS(status, NT_STATUS_OK);
+
+               io.lockx.in.locks = discard_const_p(struct smb_lock_entry,
+                                                   &zero_byte_tests[i].lock2);
+               status = smb_raw_lock(cli->tree, &io);
+
+               if (NT_STATUS_EQUAL(zero_byte_tests[i].exp_status,
+                       NT_STATUS_LOCK_NOT_GRANTED)) {
+                       /* Allow either of the failure messages and keep going
+                        * if we see the wrong status. */
+                       CHECK_STATUS_OR_CONT(status,
+                           NT_STATUS_LOCK_NOT_GRANTED,
+                           NT_STATUS_FILE_LOCK_CONFLICT);
+
+               } else {
+                       CHECK_STATUS_CONT(status,
+                           zero_byte_tests[i].exp_status);
+               }
+
+               /* Unlock both locks. */
+               io.lockx.in.ulock_cnt = 1;
+               io.lockx.in.lock_cnt = 0;
+
+               if (NT_STATUS_EQUAL(status, NT_STATUS_OK)) {
+                       status = smb_raw_lock(cli->tree, &io);
+                       CHECK_STATUS(status, NT_STATUS_OK);
+               }
+
+               io.lockx.in.locks = discard_const_p(struct smb_lock_entry,
+                                                   &zero_byte_tests[i].lock1);
+               status = smb_raw_lock(cli->tree, &io);
+               CHECK_STATUS(status, NT_STATUS_OK);
+       }
+
+done:
+       smbcli_close(cli->tree, fnum);
+       smb_raw_exit(cli->session);
+       smbcli_deltree(cli->tree, BASEDIR);
+       return ret;
+}
+
+static bool test_unlock(struct torture_context *tctx, struct smbcli_state *cli)
+{
+       union smb_lock io;
+       NTSTATUS status;
+       bool ret = true;
+       int fnum1, fnum2;
+       const char *fname = BASEDIR "\\unlock.txt";
+       struct smb_lock_entry lock1;
+       struct smb_lock_entry lock2;
+
+       torture_comment(tctx, "Testing LOCKX unlock:\n");
+
+       if (!torture_setup_dir(cli, BASEDIR)) {
+               return false;
+       }
+
+       fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
+       torture_assert(tctx,(fnum1 != -1), talloc_asprintf(tctx,
+                      "Failed to create %s - %s\n",
+                      fname, smbcli_errstr(cli->tree)));
+
+       fnum2 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
+       torture_assert(tctx,(fnum2 != -1), talloc_asprintf(tctx,
+                      "Failed to create %s - %s\n",
+                      fname, smbcli_errstr(cli->tree)));
+
+       /* Setup initial parameters */
+       io.lockx.level = RAW_LOCK_LOCKX;
+       io.lockx.in.timeout = 0;
+
+       lock1.pid = cli->session->pid;
+       lock1.offset = 0;
+       lock1.count = 10;
+       lock2.pid = cli->session->pid - 1;
+       lock2.offset = 0;
+       lock2.count = 10;
+
+       /**
+        * Take exclusive lock, then unlock it with a shared-unlock call.
+        */
+       torture_comment(tctx, "  taking exclusive lock.\n");
+       io.lockx.in.ulock_cnt = 0;
+       io.lockx.in.lock_cnt = 1;
+       io.lockx.in.mode = 0;
+       io.lockx.in.file.fnum = fnum1;
+       io.lockx.in.locks = &lock1;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       torture_comment(tctx, "  unlock the exclusive with a shared unlock call.\n");
+       io.lockx.in.ulock_cnt = 1;
+       io.lockx.in.lock_cnt = 0;
+       io.lockx.in.mode = LOCKING_ANDX_SHARED_LOCK;
+       io.lockx.in.file.fnum = fnum1;
+       io.lockx.in.locks = &lock1;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       torture_comment(tctx, "  try shared lock on pid2/fnum2, testing the unlock.\n");
+       io.lockx.in.ulock_cnt = 0;
+       io.lockx.in.lock_cnt = 1;
+       io.lockx.in.mode = LOCKING_ANDX_SHARED_LOCK;
+       io.lockx.in.file.fnum = fnum2;
+       io.lockx.in.locks = &lock2;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       /**
+        * Unlock a shared lock with an exclusive-unlock call.
+        */
+       torture_comment(tctx, "  unlock new shared lock with exclusive unlock call.\n");
+       io.lockx.in.ulock_cnt = 1;
+       io.lockx.in.lock_cnt = 0;
+       io.lockx.in.mode = 0;
+       io.lockx.in.file.fnum = fnum2;
+       io.lockx.in.locks = &lock2;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       torture_comment(tctx, "  try exclusive lock on pid1, testing the unlock.\n");
+       io.lockx.in.ulock_cnt = 0;
+       io.lockx.in.lock_cnt = 1;
+       io.lockx.in.mode = 0;
+       io.lockx.in.file.fnum = fnum1;
+       io.lockx.in.locks = &lock1;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       /* cleanup */
+       io.lockx.in.ulock_cnt = 1;
+       io.lockx.in.lock_cnt = 0;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       /**
+        * Test unlocking of 0-byte locks.
+        */
+
+       torture_comment(tctx, "  lock shared and exclusive 0-byte locks, testing that Windows "
+           "always unlocks the exclusive first.\n");
+       lock1.pid = cli->session->pid;
+       lock1.offset = 10;
+       lock1.count = 0;
+       lock2.pid = cli->session->pid;
+       lock2.offset = 5;
+       lock2.count = 10;
+       io.lockx.in.ulock_cnt = 0;
+       io.lockx.in.lock_cnt = 1;
+       io.lockx.in.file.fnum = fnum1;
+       io.lockx.in.locks = &lock1;
+
+       /* lock 0-byte shared
+        * Note: Order of the shared/exclusive locks doesn't matter. */
+       io.lockx.in.mode = LOCKING_ANDX_SHARED_LOCK;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       /* lock 0-byte exclusive */
+       io.lockx.in.mode = 0;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       /* test contention */
+       io.lockx.in.mode = LOCKING_ANDX_SHARED_LOCK;
+       io.lockx.in.locks = &lock2;
+       io.lockx.in.file.fnum = fnum2;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS_OR(status, NT_STATUS_LOCK_NOT_GRANTED,
+           NT_STATUS_FILE_LOCK_CONFLICT);
+
+       /* unlock */
+       io.lockx.in.ulock_cnt = 1;
+       io.lockx.in.lock_cnt = 0;
+       io.lockx.in.file.fnum = fnum1;
+       io.lockx.in.locks = &lock1;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       /* test - can we take a shared lock? */
+       io.lockx.in.ulock_cnt = 0;
+       io.lockx.in.lock_cnt = 1;
+       io.lockx.in.mode = LOCKING_ANDX_SHARED_LOCK;
+       io.lockx.in.file.fnum = fnum2;
+       io.lockx.in.locks = &lock2;
+       status = smb_raw_lock(cli->tree, &io);
+
+       /* XXX Samba 3 will fail this test. This is temporary(because this isn't
+        * new to Win7, it succeeds in WinXP too), until I can come to a
+        * resolution as to whether Samba should support this or not. There is
+        * code to preference unlocking exclusive locks before shared locks,
+        * but its wrapped with "#ifdef ZERO_ZERO". -zkirsch */
+       if (TARGET_IS_SAMBA3(tctx)) {
+               CHECK_STATUS_OR(status, NT_STATUS_LOCK_NOT_GRANTED,
+                   NT_STATUS_FILE_LOCK_CONFLICT);
+       } else {
+               CHECK_STATUS(status, NT_STATUS_OK);
+       }
+
+       /* cleanup */
+       io.lockx.in.ulock_cnt = 1;
+       io.lockx.in.lock_cnt = 0;
+       status = smb_raw_lock(cli->tree, &io);
+
+       /* XXX Same as above. */
+       if (TARGET_IS_SAMBA3(tctx)) {
+               CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
+       } else {
+               CHECK_STATUS(status, NT_STATUS_OK);
+       }
+
+       io.lockx.in.file.fnum = fnum1;
+       io.lockx.in.locks = &lock1;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+done:
+       smbcli_close(cli->tree, fnum1);
+       smbcli_close(cli->tree, fnum2);
+       smb_raw_exit(cli->session);
+       smbcli_deltree(cli->tree, BASEDIR);
+       return ret;
+}
+
+static bool test_multiple_unlock(struct torture_context *tctx, struct smbcli_state *cli)
+{
+       union smb_lock io;
+       NTSTATUS status;
+       bool ret = true;
+       int fnum1;
+       const char *fname = BASEDIR "\\unlock_multiple.txt";
+       struct smb_lock_entry lock1;
+       struct smb_lock_entry lock2;
+       struct smb_lock_entry locks[2];
+
+       torture_comment(tctx, "Testing LOCKX multiple unlock:\n");
+
+       if (!torture_setup_dir(cli, BASEDIR)) {
+               return false;
+       }
+
+       fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
+       torture_assert(tctx,(fnum1 != -1), talloc_asprintf(tctx,
+                      "Failed to create %s - %s\n",
+                      fname, smbcli_errstr(cli->tree)));
+
+       /* Setup initial parameters */
+       io.lockx.level = RAW_LOCK_LOCKX;
+       io.lockx.in.timeout = 0;
+
+       lock1.pid = cli->session->pid;
+       lock1.offset = 0;
+       lock1.count = 10;
+       lock2.pid = cli->session->pid;
+       lock2.offset = 10;
+       lock2.count = 10;
+
+       locks[0] = lock1;
+       locks[1] = lock2;
+
+       io.lockx.in.file.fnum = fnum1;
+       io.lockx.in.mode = 0; /* exclusive */
+
+       /** Test1: Take second lock, but not first. */
+       torture_comment(tctx, "  unlock 2 locks, first one not locked. Expect no locks "
+           "unlocked. \n");
+
+       io.lockx.in.ulock_cnt = 0;
+       io.lockx.in.lock_cnt = 1;
+       io.lockx.in.locks = &lock2;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       /* Try to unlock both locks. */
+       io.lockx.in.ulock_cnt = 2;
+       io.lockx.in.lock_cnt = 0;
+       io.lockx.in.locks = locks;
+
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
+
+       /* Second lock should not be unlocked. */
+       io.lockx.in.ulock_cnt = 0;
+       io.lockx.in.lock_cnt = 1;
+       io.lockx.in.locks = &lock2;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED);
+
+       /* cleanup */
+       io.lockx.in.ulock_cnt = 1;
+       io.lockx.in.lock_cnt = 0;
+       io.lockx.in.locks = &lock2;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       /** Test2: Take first lock, but not second. */
+       torture_comment(tctx, "  unlock 2 locks, second one not locked. Expect first lock "
+           "unlocked.\n");
+
+       io.lockx.in.ulock_cnt = 0;
+       io.lockx.in.lock_cnt = 1;
+       io.lockx.in.locks = &lock1;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       /* Try to unlock both locks. */
+       io.lockx.in.ulock_cnt = 2;
+       io.lockx.in.lock_cnt = 0;
+       io.lockx.in.locks = locks;
+
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
+
+       /* First lock should be unlocked. */
+       io.lockx.in.ulock_cnt = 0;
+       io.lockx.in.lock_cnt = 1;
+       io.lockx.in.locks = &lock1;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       /* cleanup */
+       io.lockx.in.ulock_cnt = 1;
+       io.lockx.in.lock_cnt = 0;
+       io.lockx.in.locks = &lock1;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       /* Test3: Request 2 locks, second will contend.  What happens to the
+        * first? */
+       torture_comment(tctx, "  request 2 locks, second one will contend. "
+          "Expect both to fail.\n");
+
+       /* Lock the second range */
+       io.lockx.in.ulock_cnt = 0;
+       io.lockx.in.lock_cnt = 1;
+       io.lockx.in.locks = &lock2;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       /* Request both locks */
+       io.lockx.in.ulock_cnt = 0;
+       io.lockx.in.lock_cnt = 2;
+       io.lockx.in.locks = locks;
+
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+       /* First lock should be unlocked. */
+       io.lockx.in.ulock_cnt = 0;
+       io.lockx.in.lock_cnt = 1;
+       io.lockx.in.locks = &lock1;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       /* cleanup */
+       io.lockx.in.ulock_cnt = 2;
+       io.lockx.in.lock_cnt = 0;
+       io.lockx.in.locks = locks;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       /* Test4: Request unlock and lock. The lock contends, is the unlock
+        * then re-locked? */
+       torture_comment(tctx, "  request unlock and lock, second one will "
+          "contend. Expect the unlock to succeed.\n");
+
+       /* Lock both ranges */
+       io.lockx.in.ulock_cnt = 0;
+       io.lockx.in.lock_cnt = 2;
+       io.lockx.in.locks = locks;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       /* Attempt to unlock the first range and lock the second */
+       io.lockx.in.ulock_cnt = 1;
+       io.lockx.in.lock_cnt = 1;
+       io.lockx.in.locks = locks;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+       /* The first lock should've been unlocked */
+       io.lockx.in.ulock_cnt = 0;
+       io.lockx.in.lock_cnt = 1;
+       io.lockx.in.locks = &lock1;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       /* cleanup */
+       io.lockx.in.ulock_cnt = 2;
+       io.lockx.in.lock_cnt = 0;
+       io.lockx.in.locks = locks;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+done:
+       smbcli_close(cli->tree, fnum1);
+       smb_raw_exit(cli->session);
+       smbcli_deltree(cli->tree, BASEDIR);
+       return ret;
+}
+
+/**
+ * torture_locktest5 covers stacking pretty well, but its missing two tests:
+ * - stacking an exclusive on top of shared fails
+ * - stacking two exclusives fail
+ */
+static bool test_stacking(struct torture_context *tctx, struct smbcli_state *cli)
+{
+       union smb_lock io;
+       NTSTATUS status;
+       bool ret = true;
+       int fnum1;
+       const char *fname = BASEDIR "\\stacking.txt";
+       struct smb_lock_entry lock1;
+       struct smb_lock_entry lock2;
+
+       torture_comment(tctx, "Testing stacking:\n");
+
+       if (!torture_setup_dir(cli, BASEDIR)) {
+               return false;
+       }
+
+       io.generic.level = RAW_LOCK_LOCKX;
+
+       fnum1 = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
+       torture_assert(tctx,(fnum1 != -1), talloc_asprintf(tctx,
+                      "Failed to create %s - %s\n",
+                      fname, smbcli_errstr(cli->tree)));
+
+       /* Setup initial parameters */
+       io.lockx.level = RAW_LOCK_LOCKX;
+       io.lockx.in.timeout = 0;
+
+       lock1.pid = cli->session->pid;
+       lock1.offset = 0;
+       lock1.count = 10;
+       lock2.pid = cli->session->pid - 1;
+       lock2.offset = 0;
+       lock2.count = 10;
+
+       /**
+        * Try to take a shared lock, then stack an exclusive.
+        */
+       torture_comment(tctx, "  stacking an exclusive on top of a shared lock fails.\n");
+       io.lockx.in.file.fnum = fnum1;
+       io.lockx.in.locks = &lock1;
+
+       io.lockx.in.ulock_cnt = 0;
+       io.lockx.in.lock_cnt = 1;
+       io.lockx.in.mode = LOCKING_ANDX_SHARED_LOCK;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       io.lockx.in.ulock_cnt = 0;
+       io.lockx.in.lock_cnt = 1;
+       io.lockx.in.mode = 0;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS_OR(status, NT_STATUS_LOCK_NOT_GRANTED,
+           NT_STATUS_FILE_LOCK_CONFLICT);
+
+       /* cleanup */
+       io.lockx.in.ulock_cnt = 1;
+       io.lockx.in.lock_cnt = 0;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       /**
+        * Prove that two exclusive locks do not stack.
+        */
+       torture_comment(tctx, "  two exclusive locks do not stack.\n");
+       io.lockx.in.ulock_cnt = 0;
+       io.lockx.in.lock_cnt = 1;
+       io.lockx.in.mode = 0;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS_OR(status, NT_STATUS_LOCK_NOT_GRANTED,
+           NT_STATUS_FILE_LOCK_CONFLICT);
+
+       /* cleanup */
+       io.lockx.in.ulock_cnt = 1;
+       io.lockx.in.lock_cnt = 0;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+done:
+       smbcli_close(cli->tree, fnum1);
+       smb_raw_exit(cli->session);
+       smbcli_deltree(cli->tree, BASEDIR);
+       return ret;
+}
 
 /* 
    basic testing of lock calls
@@ -1332,5 +2010,12 @@ struct torture_suite *torture_raw_lock(TALLOC_CTX *mem_ctx)
        torture_suite_add_1smb_test(suite, "errorcode", test_errorcode);
        torture_suite_add_1smb_test(suite, "changetype", test_changetype);
 
+       torture_suite_add_1smb_test(suite, "stacking", test_stacking);
+       torture_suite_add_1smb_test(suite, "unlock", test_unlock);
+       torture_suite_add_1smb_test(suite, "multiple_unlock",
+           test_multiple_unlock);
+       torture_suite_add_1smb_test(suite, "zerobytelocks",
+           test_zerobytelocks);
+
        return suite;
 }