s4-torture: mark s4 as doing valid lock range tests on SMB
[ira/wip.git] / source4 / torture / raw / lock.c
index 97242e8f176fa747d043b6149024f2b29759a41f..8b49df5de5c70a4642d21bbe8bf78bf769106770 100644 (file)
@@ -5,7 +5,7 @@
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
+#include "torture/torture.h"
 #include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
 #include "system/time.h"
 #include "system/filesys.h"
+#include "libcli/libcli.h"
+#include "torture/util.h"
+#include "libcli/composite/composite.h"
+#include "libcli/smb_composite/smb_composite.h"
+#include "lib/cmdline/popt_common.h"
+#include "param/param.h"
 
 #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)); \
-               ret = False; \
+               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 CHECK_VALUE(v, correct) do { \
-       if ((v) != (correct)) { \
-               printf("(%s) Incorrect value %s=%d - should be %d\n", \
-                      __location__, #v, v, correct); \
-               ret = False; \
+#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_SAMBA3(_tctx) (torture_setting_bool(_tctx, "samba3", false))
+#define TARGET_IS_SAMBA4(_tctx) (torture_setting_bool(_tctx, "samba4", false))
 
 /*
   test SMBlock and SMBunlock ops
 */
-static BOOL test_lock(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
+static bool test_lock(struct torture_context *tctx, struct smbcli_state *cli)
 {
        union smb_lock io;
        NTSTATUS status;
-       BOOL ret = True;
+       bool ret = true;
        int fnum;
        const char *fname = BASEDIR "\\test.txt";
 
        if (!torture_setup_dir(cli, BASEDIR)) {
-               return False;
+               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.fnum = fnum;
+       io.lock.in.file.fnum = fnum;
        io.lock.in.count = 0;
        io.lock.in.offset = 0;
        status = smb_raw_lock(cli->tree, &io);
@@ -82,9 +112,9 @@ static BOOL test_lock(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
        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.fnum = fnum;
+       io.lock.in.file.fnum = fnum;
        io.lock.in.count = 1;
        io.lock.in.offset = 0;
        status = smb_raw_lock(cli->tree, &io);
@@ -100,9 +130,9 @@ static BOOL test_lock(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
        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.fnum = fnum;
+       io.lock.in.file.fnum = fnum;
        io.lock.in.count = 4000;
        io.lock.in.offset = 0xEEFFFFFF;
        status = smb_raw_lock(cli->tree, &io);
@@ -118,9 +148,9 @@ static BOOL test_lock(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
        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.fnum = fnum;
+       io.lock.in.file.fnum = fnum;
        io.lock.in.count = 4000;
        io.lock.in.offset = 0xEEFFFFFF;
        status = smb_raw_lock(cli->tree, &io);
@@ -136,9 +166,9 @@ static BOOL test_lock(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
        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.fnum = fnum;
+       io.lock.in.file.fnum = fnum;
        io.lock.in.count = 4000;
        io.lock.in.offset = 0xEF000000;
        status = smb_raw_lock(cli->tree, &io);
@@ -154,9 +184,9 @@ static BOOL test_lock(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
        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.fnum = fnum;
+       io.lock.in.file.fnum = fnum;
        io.lock.in.count = 4002;
        io.lock.in.offset = 10001;
        status = smb_raw_lock(cli->tree, &io);
@@ -180,31 +210,29 @@ done:
 /*
   test locking&X ops
 */
-static BOOL test_lockx(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
+static bool test_lockx(struct torture_context *tctx, struct smbcli_state *cli)
 {
        union smb_lock io;
        struct smb_lock_entry lock[1];
        NTSTATUS status;
-       BOOL ret = True;
+       bool ret = true;
        int fnum;
        const char *fname = BASEDIR "\\test.txt";
 
        if (!torture_setup_dir(cli, BASEDIR)) {
-               return False;
+               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.fnum = fnum;
+       io.lockx.in.file.fnum = fnum;
        io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES;
        io.lockx.in.timeout = 0;
        io.lockx.in.ulock_cnt = 0;
@@ -217,7 +245,7 @@ static BOOL test_lockx(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
        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;
@@ -235,7 +263,7 @@ static BOOL test_lockx(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
        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;
@@ -253,7 +281,7 @@ static BOOL test_lockx(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
        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;
@@ -271,7 +299,7 @@ static BOOL test_lockx(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
        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;
@@ -289,7 +317,7 @@ static BOOL test_lockx(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
        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;
@@ -308,7 +336,7 @@ static BOOL test_lockx(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
        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;
@@ -328,7 +356,7 @@ static BOOL test_lockx(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
        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;
@@ -338,12 +366,16 @@ static BOOL test_lockx(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
        lock[0].pid++;
        lock[0].count = 2;
        status = smb_raw_lock(cli->tree, &io);
-       CHECK_STATUS(status, NT_STATUS_OK);
+       if (TARGET_IS_WIN7(tctx) || TARGET_IS_SAMBA4(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);
@@ -355,44 +387,44 @@ done:
        return ret;
 }
 
-
 /*
   test high pid
 */
-static BOOL test_pidhigh(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
+static bool test_pidhigh(struct torture_context *tctx, 
+                                                struct smbcli_state *cli)
 {
        union smb_lock io;
        struct smb_lock_entry lock[1];
        NTSTATUS status;
-       BOOL ret = True;
+       bool ret = true;
        int fnum;
        const char *fname = BASEDIR "\\test.txt";
        uint8_t c = 1;
 
        if (!torture_setup_dir(cli, BASEDIR)) {
-               return False;
+               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));
-               ret = False;
+               torture_result(tctx, TORTURE_FAIL,
+                       "Failed to write 1 byte - %s\n",
+                       smbcli_errstr(cli->tree));
+               ret = false;
                goto done;
        }
 
        io.lockx.level = RAW_LOCK_LOCKX;
-       io.lockx.in.fnum = fnum;
+       io.lockx.in.file.fnum = fnum;
        io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES;
        io.lockx.in.timeout = 0;
        io.lockx.in.ulock_cnt = 0;
@@ -405,28 +437,30 @@ static BOOL test_pidhigh(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
        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));
-               ret = False;
+               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");
-               ret = False;
+               torture_result(tctx, TORTURE_FAIL,
+                       "pid is incorrect handled for read with lock!\n");
+               ret = false;
                goto done;
        }
 
        cli->session->pid = 0x10001;
 
        if (smbcli_read(cli->tree, fnum, &c, 0, 1) != 1) {
-               printf("High pid is used on this server!\n");
-               ret = False;
+               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:
@@ -440,33 +474,40 @@ done:
 /*
   test locking&X async operation
 */
-static BOOL test_async(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
+static bool test_async(struct torture_context *tctx, 
+                                          struct smbcli_state *cli)
 {
+       struct smbcli_session *session;
+       struct smb_composite_sesssetup setup;
+       struct smbcli_tree *tree;
+       union smb_tcon tcon;
+       const char *host, *share;
        union smb_lock io;
        struct smb_lock_entry lock[2];
        NTSTATUS status;
-       BOOL ret = True;
+       bool ret = true;
        int fnum;
        const char *fname = BASEDIR "\\test.txt";
        time_t t;
        struct smbcli_request *req;
+       struct smbcli_session_options options;
 
        if (!torture_setup_dir(cli, BASEDIR)) {
-               return False;
+               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.fnum = fnum;
+       io.lockx.in.file.fnum = fnum;
        io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES;
        io.lockx.in.timeout = 0;
        io.lockx.in.ulock_cnt = 0;
@@ -480,30 +521,27 @@ static BOOL test_async(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
 
        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;
        io.lockx.in.timeout = 0;
        io.lockx.in.mode = LOCKING_ANDX_CANCEL_LOCK;
        status = smb_raw_lock(cli->tree, &io);
-       CHECK_STATUS(status, NT_STATUS_UNSUCCESSFUL);
+       CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRcancelviolation));
 
        /* cancel with the wrong bits set */
        lock[0].offset = 100;
        io.lockx.in.timeout = 0;
        io.lockx.in.mode = LOCKING_ANDX_CANCEL_LOCK;
        status = smb_raw_lock(cli->tree, &io);
-       CHECK_STATUS(status, NT_STATUS_UNSUCCESSFUL);
+       CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRcancelviolation));
 
        /* cancel the right range */
        lock[0].offset = 100;
@@ -516,13 +554,10 @@ static BOOL test_async(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
        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");
+       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;
@@ -532,28 +567,23 @@ static BOOL test_async(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
 
        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;
        status = smb_raw_lock(cli->tree, &io);
        CHECK_STATUS(status, NT_STATUS_OK);
 
+       t = time(NULL);
        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)\n", __location__);
-               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;
@@ -561,65 +591,243 @@ static BOOL test_async(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
        status = smb_raw_lock(cli->tree, &io);
        CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
 
+       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__));
 
-       smbcli_close(cli->tree, fnum);
+       status = smbcli_close(cli->tree, fnum);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       status = smbcli_request_simple_recv(req);
+       CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
+
+       torture_assert(tctx,!(time(NULL) > t+2), talloc_asprintf(tctx,
+                      "lock cancel by close was not immediate (%s)\n", __location__));
+
+       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;
+
+       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);
+       tcon.generic.level = RAW_TCON_TCONX;
+       tcon.tconx.in.flags = 0;
+       tcon.tconx.in.password = data_blob(NULL, 0);
+       tcon.tconx.in.path = talloc_asprintf(tctx, "\\\\%s\\%s", host, share);
+       tcon.tconx.in.device = "A:";
+       status = smb_raw_tcon(tree, tctx, &tcon);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       tree->tid = tcon.tconx.out.tid;
+
+       torture_comment(tctx, "testing cancel by exit\n");
+       fname = BASEDIR "\\test_exit.txt";
+       fnum = smbcli_open(tree, fname, O_RDWR|O_CREAT, DENY_NONE);
+       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;
+       io.lockx.in.timeout = 0;
+       io.lockx.in.ulock_cnt = 0;
+       io.lockx.in.lock_cnt = 1;
+       lock[0].pid = session->pid;
+       lock[0].offset = 100;
+       lock[0].count = 10;
+       io.lockx.in.locks = &lock[0];
+       status = smb_raw_lock(tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       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;
+       status = smb_raw_lock(tree, &io);
+       CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED);
+
+       io.lockx.in.timeout = 10000;
+       t = time(NULL);
+       req = smb_raw_lock_send(tree, &io);
+       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);
 
        status = smbcli_request_simple_recv(req);
        CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
 
-       if (time(NULL) > t+2) {
-               printf("lock cancel by unlock was not immediate (%s)\n", __location__);
-               ret = False;
+       torture_assert(tctx,!(time(NULL) > t+2), talloc_asprintf(tctx,
+                      "lock cancel by exit was not immediate (%s)\n", __location__));
+
+       torture_comment(tctx, "testing cancel by ulogoff\n");
+       fname = BASEDIR "\\test_ulogoff.txt";
+       fnum = smbcli_open(tree, fname, O_RDWR|O_CREAT, DENY_NONE);
+       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;
+       io.lockx.in.timeout = 0;
+       io.lockx.in.ulock_cnt = 0;
+       io.lockx.in.lock_cnt = 1;
+       lock[0].pid = session->pid;
+       lock[0].offset = 100;
+       lock[0].count = 10;
+       io.lockx.in.locks = &lock[0];
+       status = smb_raw_lock(tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       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;
+       status = smb_raw_lock(tree, &io);
+       CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED);
+
+       io.lockx.in.timeout = 10000;
+       t = time(NULL);
+       req = smb_raw_lock_send(tree, &io);
+       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)) {
+               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);
                goto done;
        }
-       
+       CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
+
+       torture_assert(tctx,!(time(NULL) > t+2), talloc_asprintf(tctx,
+                      "lock cancel by ulogoff was not immediate (%s)\n", __location__));
+
+       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);
+       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;
+       io.lockx.in.timeout = 0;
+       io.lockx.in.ulock_cnt = 0;
+       io.lockx.in.lock_cnt = 1;
+       lock[0].pid = cli->session->pid;
+       lock[0].offset = 100;
+       lock[0].count = 10;
+       io.lockx.in.locks = &lock[0];
+       status = smb_raw_lock(tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       status = smb_raw_lock(tree, &io);
+       CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED);
+
+       io.lockx.in.timeout = 10000;
+       t = time(NULL);
+       req = smb_raw_lock_send(tree, &io);
+       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);
+
+       status = smbcli_request_simple_recv(req);
+       CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED);
+
+       torture_assert(tctx,!(time(NULL) > t+2), talloc_asprintf(tctx,
+                      "lock cancel by tdis was not immediate (%s)\n", __location__));
 
 done:
-       smbcli_close(cli->tree, fnum);
        smb_raw_exit(cli->session);
        smbcli_deltree(cli->tree, BASEDIR);
        return ret;
 }
 
-
 /*
-  test LOCKING_ANDX_CHANGE_LOCKTYPE
+  test NT_STATUS_LOCK_NOT_GRANTED vs. NT_STATUS_FILE_LOCK_CONFLICT
 */
-static BOOL test_changetype(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
+static bool test_errorcode(struct torture_context *tctx, 
+                                                  struct smbcli_state *cli)
 {
        union smb_lock io;
+       union smb_open op;
        struct smb_lock_entry lock[2];
        NTSTATUS status;
-       BOOL ret = True;
-       int fnum;
-       uint8_t c = 0;
-       const char *fname = BASEDIR "\\test.txt";
+       bool ret = true;
+       int fnum, fnum2;
+       const char *fname;
+       struct smbcli_request *req;
+       time_t start;
+       int t;
+       int delay;
 
        if (!torture_setup_dir(cli, BASEDIR)) {
-               return False;
+               return false;
        }
 
-       printf("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_comment(tctx, "Testing LOCK_NOT_GRANTED vs. FILE_LOCK_CONFLICT\n");
+
+       torture_comment(tctx, "testing with timeout = 0\n");
+       fname = BASEDIR "\\test0.txt";
+       t = 0;
+
+       /*
+        * the first run is with t = 0,
+        * 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
+        */
+       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_func = OPENX_OPEN_FUNC_OPEN | OPENX_OPEN_FUNC_CREATE;
+       op.openx.in.search_attrs = 0;
+       op.openx.in.file_attrs = 0;
+       op.openx.in.write_time = 0;
+       op.openx.in.size = 0;
+       op.openx.in.timeout = 0;
+
+       status = smb_raw_open(cli->tree, tctx, &op);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       fnum = op.openx.out.file.fnum;
+
+       status = smb_raw_open(cli->tree, tctx, &op);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       fnum2 = op.openx.out.file.fnum;
 
        io.lockx.level = RAW_LOCK_LOCKX;
-       io.lockx.in.fnum = fnum;
-       io.lockx.in.mode = LOCKING_ANDX_SHARED_LOCK;
-       io.lockx.in.timeout = 0;
+       io.lockx.in.file.fnum = fnum;
+       io.lockx.in.mode = LOCKING_ANDX_LARGE_FILES;
+       io.lockx.in.timeout = t;
        io.lockx.in.ulock_cnt = 0;
        io.lockx.in.lock_cnt = 1;
        lock[0].pid = cli->session->pid;
@@ -629,53 +837,1018 @@ static BOOL test_changetype(struct smbcli_state *cli, TALLOC_CTX *mem_ctx)
        status = smb_raw_lock(cli->tree, &io);
        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__);
-               ret = False;
-               goto done;
-       }
+       /*
+        * demonstrate that the first conflicting lock on each handle give LOCK_NOT_GRANTED
+        * this also demonstrates that the error code cache is per file handle
+        * (LOCK_NOT_GRANTED is only be used when timeout is 0!)
+        */
+       io.lockx.in.file.fnum = fnum2;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, (t?NT_STATUS_FILE_LOCK_CONFLICT:NT_STATUS_LOCK_NOT_GRANTED));
 
-       /* windows server don't seem to support this */
-       io.lockx.in.mode = LOCKING_ANDX_CHANGE_LOCKTYPE;
+       io.lockx.in.file.fnum = fnum;
        status = smb_raw_lock(cli->tree, &io);
-       CHECK_STATUS(status, NT_STATUS_UNSUCCESSFUL);
+       CHECK_STATUS(status, (t?NT_STATUS_FILE_LOCK_CONFLICT:NT_STATUS_LOCK_NOT_GRANTED));
 
-       if (smbcli_write(cli->tree, fnum, 0, &c, 100, 1) == 1) {
-               printf("allowed write after lock change (%s)\n", __location__);
-               ret = False;
-               goto done;
-       }
+       /* demonstrate that each following conflict gives FILE_LOCK_CONFLICT */
+       io.lockx.in.file.fnum = fnum;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
 
-done:
-       smbcli_close(cli->tree, fnum);
-       smb_raw_exit(cli->session);
-       smbcli_deltree(cli->tree, BASEDIR);
-       return ret;
-}
+       io.lockx.in.file.fnum = fnum2;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
 
+       io.lockx.in.file.fnum = fnum;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
 
-/* 
-   basic testing of lock calls
-*/
-BOOL torture_raw_lock(void)
-{
-       struct smbcli_state *cli;
-       BOOL ret = True;
-       TALLOC_CTX *mem_ctx;
+       io.lockx.in.file.fnum = fnum2;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
 
-       if (!torture_open_connection(&cli)) {
-               return False;
-       }
+       /* demonstrate that the smbpid doesn't matter */
+       lock[0].pid++;
+       io.lockx.in.file.fnum = fnum;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+       io.lockx.in.file.fnum = fnum2;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+       lock[0].pid--;
 
-       mem_ctx = talloc_init("torture_raw_lock");
+       /* 
+        * demonstrate the a successful lock with count = 0 and the same offset,
+        * doesn't reset the error cache
+        */
+       lock[0].offset = 100;
+       lock[0].count = 0;
+       io.lockx.in.file.fnum = fnum;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
 
-       ret &= test_lockx(cli, mem_ctx);
-       ret &= test_lock(cli, mem_ctx);
-       ret &= test_pidhigh(cli, mem_ctx);
-       ret &= test_async(cli, mem_ctx);
-       ret &= test_changetype(cli, mem_ctx);
+       io.lockx.in.file.fnum = fnum2;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
 
-       torture_close_connection(cli);
-       talloc_free(mem_ctx);
-       return ret;
+       lock[0].offset = 100;
+       lock[0].count = 10;
+       io.lockx.in.file.fnum = fnum;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+       io.lockx.in.file.fnum = fnum2;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+       /* 
+        * demonstrate the a successful lock with count = 0 and outside the locked range,
+        * doesn't reset the error cache
+        */
+       lock[0].offset = 110;
+       lock[0].count = 0;
+       io.lockx.in.file.fnum = fnum;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       io.lockx.in.file.fnum = fnum2;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       lock[0].offset = 100;
+       lock[0].count = 10;
+       io.lockx.in.file.fnum = fnum;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+       io.lockx.in.file.fnum = fnum2;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+       lock[0].offset = 99;
+       lock[0].count = 0;
+       io.lockx.in.file.fnum = fnum;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       io.lockx.in.file.fnum = fnum2;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       lock[0].offset = 100;
+       lock[0].count = 10;
+       io.lockx.in.file.fnum = fnum;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+       io.lockx.in.file.fnum = fnum2;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+       /* demonstrate that a changing count doesn't reset the error cache */
+       lock[0].offset = 100;
+       lock[0].count = 5;
+       io.lockx.in.file.fnum = fnum;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+       io.lockx.in.file.fnum = fnum2;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+       lock[0].offset = 100;
+       lock[0].count = 15;
+       io.lockx.in.file.fnum = fnum;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+       io.lockx.in.file.fnum = fnum2;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+       /* 
+        * demonstrate the a lock with count = 0 and inside the locked range,
+        * fails and resets the error cache
+        */
+       lock[0].offset = 101;
+       lock[0].count = 0;
+       io.lockx.in.file.fnum = fnum;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, (t?NT_STATUS_FILE_LOCK_CONFLICT:NT_STATUS_LOCK_NOT_GRANTED));
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+       io.lockx.in.file.fnum = fnum2;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, (t?NT_STATUS_FILE_LOCK_CONFLICT:NT_STATUS_LOCK_NOT_GRANTED));
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+       lock[0].offset = 100;
+       lock[0].count = 10;
+       io.lockx.in.file.fnum = fnum;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, (t?NT_STATUS_FILE_LOCK_CONFLICT:NT_STATUS_LOCK_NOT_GRANTED));
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+       io.lockx.in.file.fnum = fnum2;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, (t?NT_STATUS_FILE_LOCK_CONFLICT:NT_STATUS_LOCK_NOT_GRANTED));
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+       /* demonstrate the a changing offset, resets the error cache */
+       lock[0].offset = 105;
+       lock[0].count = 10;
+       io.lockx.in.file.fnum = fnum;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, (t?NT_STATUS_FILE_LOCK_CONFLICT:NT_STATUS_LOCK_NOT_GRANTED));
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+       io.lockx.in.file.fnum = fnum2;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, (t?NT_STATUS_FILE_LOCK_CONFLICT:NT_STATUS_LOCK_NOT_GRANTED));
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+       lock[0].offset = 100;
+       lock[0].count = 10;
+       io.lockx.in.file.fnum = fnum;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, (t?NT_STATUS_FILE_LOCK_CONFLICT:NT_STATUS_LOCK_NOT_GRANTED));
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+       io.lockx.in.file.fnum = fnum2;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, (t?NT_STATUS_FILE_LOCK_CONFLICT:NT_STATUS_LOCK_NOT_GRANTED));
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+       lock[0].offset = 95;
+       lock[0].count = 9;
+       io.lockx.in.file.fnum = fnum;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, (t?NT_STATUS_FILE_LOCK_CONFLICT:NT_STATUS_LOCK_NOT_GRANTED));
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+       io.lockx.in.file.fnum = fnum2;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, (t?NT_STATUS_FILE_LOCK_CONFLICT:NT_STATUS_LOCK_NOT_GRANTED));
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+       lock[0].offset = 100;
+       lock[0].count = 10;
+       io.lockx.in.file.fnum = fnum;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, (t?NT_STATUS_FILE_LOCK_CONFLICT:NT_STATUS_LOCK_NOT_GRANTED));
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+       io.lockx.in.file.fnum = fnum2;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, (t?NT_STATUS_FILE_LOCK_CONFLICT:NT_STATUS_LOCK_NOT_GRANTED));
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+       /* 
+        * 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
+        */
+       lock[0].offset = 120;
+       lock[0].count = 15;
+       io.lockx.in.file.fnum = fnum;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       io.lockx.in.file.fnum = fnum2;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, (t?NT_STATUS_FILE_LOCK_CONFLICT:NT_STATUS_LOCK_NOT_GRANTED));
+
+       lock[0].offset = 100;
+       lock[0].count = 10;
+       io.lockx.in.file.fnum = fnum;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+       io.lockx.in.file.fnum = fnum2;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, (t?NT_STATUS_FILE_LOCK_CONFLICT:NT_STATUS_LOCK_NOT_GRANTED));
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_FILE_LOCK_CONFLICT);
+
+       /* end of the loop */
+       if (t == 0) {
+               smb_raw_exit(cli->session);
+               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);
+       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);
+       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;
+       io.lockx.in.timeout = 0;
+       io.lockx.in.ulock_cnt = 0;
+       io.lockx.in.lock_cnt = 1;
+       lock[0].pid = cli->session->pid;
+       lock[0].offset = 100;
+       lock[0].count = 10;
+       io.lockx.in.locks = &lock[0];
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       start = time(NULL);
+       io.lockx.in.timeout = t;
+       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.timeout = 0;
+       lock[0].offset = 105;
+       lock[0].count = 10;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED);
+
+       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);
+
+       smbcli_close(cli->tree, fnum);
+       fname = BASEDIR "\\test3.txt";
+       fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
+       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;
+       io.lockx.in.timeout = 0;
+       io.lockx.in.ulock_cnt = 0;
+       io.lockx.in.lock_cnt = 1;
+       lock[0].pid = cli->session->pid;
+       lock[0].offset = 100;
+       lock[0].count = 10;
+       io.lockx.in.locks = &lock[0];
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       start = time(NULL);
+       io.lockx.in.timeout = t;
+       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.timeout = 0;
+       lock[0].offset = 105;
+       lock[0].count = 10;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED);
+
+       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);
+
+       smbcli_close(cli->tree, fnum);
+       fname = BASEDIR "\\test4.txt";
+       fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
+       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;
+       io.lockx.in.timeout = 0;
+       io.lockx.in.ulock_cnt = 0;
+       io.lockx.in.lock_cnt = 1;
+       lock[0].pid = cli->session->pid;
+       lock[0].offset = 100;
+       lock[0].count = 10;
+       io.lockx.in.locks = &lock[0];
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       start = time(NULL);
+       io.lockx.in.timeout = t;
+       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.timeout = 0;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED);
+
+       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);
+
+done:
+       smb_raw_exit(cli->session);
+       smbcli_deltree(cli->tree, BASEDIR);
+       return ret;
+}
+
+
+/*
+  test LOCKING_ANDX_CHANGE_LOCKTYPE
+*/
+static bool test_changetype(struct torture_context *tctx, 
+                                                       struct smbcli_state *cli)
+{
+       union smb_lock io;
+       struct smb_lock_entry lock[2];
+       NTSTATUS status;
+       bool ret = true;
+       int fnum;
+       uint8_t c = 0;
+       const char *fname = BASEDIR "\\test.txt";
+
+       if (!torture_setup_dir(cli, BASEDIR)) {
+               return false;
+       }
+
+       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);
+       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;
+       io.lockx.in.mode = LOCKING_ANDX_SHARED_LOCK;
+       io.lockx.in.timeout = 0;
+       io.lockx.in.ulock_cnt = 0;
+       io.lockx.in.lock_cnt = 1;
+       lock[0].pid = cli->session->pid;
+       lock[0].offset = 100;
+       lock[0].count = 10;
+       io.lockx.in.locks = &lock[0];
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       if (smbcli_write(cli->tree, fnum, 0, &c, 100, 1) == 1) {
+               torture_result(tctx, TORTURE_FAIL,
+                       "allowed write on read locked region (%s)\n", __location__);
+               ret = false;
+               goto done;
+       }
+
+       /* windows server don't seem to support this */
+       io.lockx.in.mode = LOCKING_ANDX_CHANGE_LOCKTYPE;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRnoatomiclocks));
+
+       if (smbcli_write(cli->tree, fnum, 0, &c, 100, 1) == 1) {
+               torture_result(tctx, TORTURE_FAIL,
+                       "allowed write after lock change (%s)\n", __location__);
+               ret = false;
+               goto done;
+       }
+
+done:
+       smbcli_close(cli->tree, fnum);
+       smb_raw_exit(cli->session);
+       smbcli_deltree(cli->tree, BASEDIR);
+       return ret;
+}
+
+struct double_lock_test {
+       struct smb_lock_entry lock1;
+       struct smb_lock_entry lock2;
+       NTSTATUS exp_status;
+};
+
+/**
+ * Tests zero byte locks.
+ */
+static const 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 = &zero_byte_tests[i].lock1;
+               status = smb_raw_lock(cli->tree, &io);
+               CHECK_STATUS(status, NT_STATUS_OK);
+
+               io.lockx.in.locks = &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 = &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);
+
+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
+*/
+struct torture_suite *torture_raw_lock(TALLOC_CTX *mem_ctx)
+{
+       struct torture_suite *suite = torture_suite_create(mem_ctx, "LOCK");
+
+       torture_suite_add_1smb_test(suite, "lockx", test_lockx);
+       torture_suite_add_1smb_test(suite, "lock", test_lock);
+       torture_suite_add_1smb_test(suite, "pidhigh", test_pidhigh);
+       torture_suite_add_1smb_test(suite, "async", test_async);
+       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;
 }