s4/torture: add test for zero byte read contention with byte range locks
authorSteven Danneman <steven.danneman@isilon.com>
Fri, 4 Dec 2009 02:50:33 +0000 (18:50 -0800)
committerSteven Danneman <steven.danneman@isilon.com>
Tue, 8 Dec 2009 00:10:25 +0000 (16:10 -0800)
source4/selftest/knownfail
source4/torture/raw/lock.c
source4/torture/smb2/lock.c

index b9d40f24092877db3ef7804655c30643324ddcf7..ef3c10c32d16ddfe15cb84317d16b1ce045da69f 100644 (file)
@@ -67,3 +67,5 @@ samba4.raw.lock.*.async # bug 6960
 samba4.smb2.lock.*.MULTIPLE-UNLOCK # bug 6959
 samba4.raw.sfileinfo.*.END-OF-FILE # bug 6962
 samba4.raw.oplock.*.BATCH22 # bug 6963
+samba4.raw.lock.*.zerobyteread # bug 6974
+samba4.smb2.lock.*.ZEROBYTEREAD # bug 6974
index 05d9beda30a11b662661306fdbf36363a2c9a5c0..34b05b70e4992d19b10ad5ad7106ea06b353fa75 100644 (file)
@@ -1997,7 +1997,158 @@ done:
        return ret;
 }
 
-/* 
+/**
+ * Test how 0-byte read requests contend with byte range locks
+ */
+static bool test_zerobyteread(struct torture_context *tctx,
+                             struct smbcli_state *cli)
+{
+       union smb_lock io;
+       union smb_read rd;
+       NTSTATUS status;
+       bool ret = true;
+       int fnum1, fnum2;
+       const char *fname = BASEDIR "\\zerobyteread.txt";
+       struct smb_lock_entry lock1;
+       uint8_t c = 1;
+
+       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)));
+
+       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;
+
+       ZERO_STRUCT(rd);
+       rd.readx.level = RAW_READ_READX;
+
+       torture_comment(tctx, "Testing zero byte read on lock range:\n");
+
+       /* Take an exclusive lock */
+       torture_comment(tctx, "  taking exclusive lock.\n");
+       io.lockx.in.ulock_cnt = 0;
+       io.lockx.in.lock_cnt = 1;
+       io.lockx.in.mode = LOCKING_ANDX_EXCLUSIVE_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);
+
+       /* Try a zero byte read */
+       torture_comment(tctx, "  reading 0 bytes.\n");
+       rd.readx.in.file.fnum = fnum2;
+       rd.readx.in.offset    = 5;
+       rd.readx.in.mincnt    = 0;
+       rd.readx.in.maxcnt    = 0;
+       rd.readx.in.remaining = 0;
+       rd.readx.in.read_for_execute = false;
+       rd.readx.out.data     = &c;
+       status = smb_raw_read(cli->tree, &rd);
+       torture_assert_int_equal_goto(tctx, rd.readx.out.nread, 0, ret, done,
+                                     "zero byte read did not return 0 bytes");
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       /* Unlock lock */
+       io.lockx.in.ulock_cnt = 1;
+       io.lockx.in.lock_cnt = 0;
+       io.lockx.in.mode = LOCKING_ANDX_EXCLUSIVE_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, "Testing zero byte read on zero byte lock "
+                             "range:\n");
+
+       /* Take an exclusive lock */
+       torture_comment(tctx, "  taking exclusive 0-byte lock.\n");
+       io.lockx.in.ulock_cnt = 0;
+       io.lockx.in.lock_cnt = 1;
+       io.lockx.in.mode = LOCKING_ANDX_EXCLUSIVE_LOCK;
+       io.lockx.in.file.fnum = fnum1;
+       io.lockx.in.locks = &lock1;
+       lock1.offset = 5;
+       lock1.count = 0;
+       status = smb_raw_lock(cli->tree, &io);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       /* Try a zero byte read before the lock */
+       torture_comment(tctx, "  reading 0 bytes before the lock.\n");
+       rd.readx.in.file.fnum = fnum2;
+       rd.readx.in.offset    = 4;
+       rd.readx.in.mincnt    = 0;
+       rd.readx.in.maxcnt    = 0;
+       rd.readx.in.remaining = 0;
+       rd.readx.in.read_for_execute = false;
+       rd.readx.out.data     = &c;
+       status = smb_raw_read(cli->tree, &rd);
+       torture_assert_int_equal_goto(tctx, rd.readx.out.nread, 0, ret, done,
+                                     "zero byte read did not return 0 bytes");
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       /* Try a zero byte read on the lock */
+       torture_comment(tctx, "  reading 0 bytes on the lock.\n");
+       rd.readx.in.file.fnum = fnum2;
+       rd.readx.in.offset    = 5;
+       rd.readx.in.mincnt    = 0;
+       rd.readx.in.maxcnt    = 0;
+       rd.readx.in.remaining = 0;
+       rd.readx.in.read_for_execute = false;
+       rd.readx.out.data     = &c;
+       status = smb_raw_read(cli->tree, &rd);
+       torture_assert_int_equal_goto(tctx, rd.readx.out.nread, 0, ret, done,
+                                     "zero byte read did not return 0 bytes");
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       /* Try a zero byte read after the lock */
+       torture_comment(tctx, "  reading 0 bytes after the lock.\n");
+       rd.readx.in.file.fnum = fnum2;
+       rd.readx.in.offset    = 6;
+       rd.readx.in.mincnt    = 0;
+       rd.readx.in.maxcnt    = 0;
+       rd.readx.in.remaining = 0;
+       rd.readx.in.read_for_execute = false;
+       rd.readx.out.data     = &c;
+       status = smb_raw_read(cli->tree, &rd);
+       torture_assert_int_equal_goto(tctx, rd.readx.out.nread, 0, ret, done,
+                                     "zero byte read did not return 0 bytes");
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       /* Unlock lock */
+       io.lockx.in.ulock_cnt = 1;
+       io.lockx.in.lock_cnt = 0;
+       io.lockx.in.mode = LOCKING_ANDX_EXCLUSIVE_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);
+
+done:
+       smbcli_close(cli->tree, fnum1);
+       smbcli_close(cli->tree, fnum2);
+       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)
@@ -2017,6 +2168,8 @@ struct torture_suite *torture_raw_lock(TALLOC_CTX *mem_ctx)
            test_multiple_unlock);
        torture_suite_add_1smb_test(suite, "zerobytelocks",
            test_zerobytelocks);
+       torture_suite_add_1smb_test(suite, "zerobyteread",
+           test_zerobyteread);
 
        return suite;
 }
index 35a08d3d8c12ecaa7598aff8ddfe4c35b887d970..508358ea613471158ad10eeb8fa2672f3670b3e5 100644 (file)
@@ -1453,6 +1453,124 @@ done:
        return ret;
 }
 
+static bool test_zerobyteread(struct torture_context *torture,
+                             struct smb2_tree *tree)
+{
+       NTSTATUS status;
+       bool ret = true;
+       struct smb2_handle h, h2;
+       uint8_t buf[200];
+       struct smb2_lock lck;
+       struct smb2_lock_element el[1];
+       struct smb2_read rd;
+
+       const char *fname = BASEDIR "\\zerobyteread.txt";
+
+       status = torture_smb2_testdir(tree, BASEDIR, &h);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       smb2_util_close(tree, h);
+
+       status = torture_smb2_testfile(tree, fname, &h);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       ZERO_STRUCT(buf);
+       status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf));
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       status = torture_smb2_testfile(tree, fname, &h2);
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       /* Setup initial parameters */
+       lck.in.locks            = el;
+       lck.in.lock_count       = 0x0001;
+       lck.in.lock_sequence    = 0x00000000;
+       lck.in.file.handle      = h;
+
+       ZERO_STRUCT(rd);
+       rd.in.file.handle = h2;
+
+       torture_comment(torture, "Testing zero byte read on lock range:\n");
+
+       /* Take an exclusive lock */
+       torture_comment(torture, "  taking exclusive lock.\n");
+       el[0].offset            = 0;
+       el[0].length            = 10;
+       el[0].reserved          = 0x00000000;
+       el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE |
+                                 SMB2_LOCK_FLAG_FAIL_IMMEDIATELY;
+       status = smb2_lock(tree, &lck);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(lck.out.reserved, 0);
+
+       /* Try a zero byte read */
+       torture_comment(torture, "  reading 0 bytes.\n");
+       rd.in.offset      = 5;
+       rd.in.length      = 0;
+       status = smb2_read(tree, tree, &rd);
+       torture_assert_int_equal_goto(torture, rd.out.data.length, 0, ret, done,
+                                     "zero byte read did not return 0 bytes");
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       /* Unlock lock */
+       el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
+       status = smb2_lock(tree, &lck);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(lck.out.reserved, 0);
+
+       torture_comment(torture, "Testing zero byte read on zero byte lock "
+                                "range:\n");
+
+       /* Take an exclusive lock */
+       torture_comment(torture, "  taking exclusive 0-byte lock.\n");
+       el[0].offset            = 5;
+       el[0].length            = 0;
+       el[0].reserved          = 0x00000000;
+       el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE |
+                                 SMB2_LOCK_FLAG_FAIL_IMMEDIATELY;
+       status = smb2_lock(tree, &lck);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(lck.out.reserved, 0);
+
+       /* Try a zero byte read before the lock */
+       torture_comment(torture, "  reading 0 bytes before the lock.\n");
+       rd.in.offset      = 4;
+       rd.in.length      = 0;
+       status = smb2_read(tree, tree, &rd);
+       torture_assert_int_equal_goto(torture, rd.out.data.length, 0, ret, done,
+                                     "zero byte read did not return 0 bytes");
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       /* Try a zero byte read on the lock */
+       torture_comment(torture, "  reading 0 bytes on the lock.\n");
+       rd.in.offset      = 5;
+       rd.in.length      = 0;
+       status = smb2_read(tree, tree, &rd);
+       torture_assert_int_equal_goto(torture, rd.out.data.length, 0, ret, done,
+                                     "zero byte read did not return 0 bytes");
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       /* Try a zero byte read after the lock */
+       torture_comment(torture, "  reading 0 bytes after the lock.\n");
+       rd.in.offset      = 6;
+       rd.in.length      = 0;
+       status = smb2_read(tree, tree, &rd);
+       torture_assert_int_equal_goto(torture, rd.out.data.length, 0, ret, done,
+                                     "zero byte read did not return 0 bytes");
+       CHECK_STATUS(status, NT_STATUS_OK);
+
+       /* Unlock lock */
+       el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
+       status = smb2_lock(tree, &lck);
+       CHECK_STATUS(status, NT_STATUS_OK);
+       CHECK_VALUE(lck.out.reserved, 0);
+
+done:
+       smb2_util_close(tree, h2);
+       smb2_util_close(tree, h);
+       smb2_deltree(tree, BASEDIR);
+       return ret;
+}
+
 static bool test_unlock(struct torture_context *torture,
                        struct smb2_tree *tree)
 {
@@ -2750,6 +2868,8 @@ struct torture_suite *torture_smb2_lock_init(void)
        torture_suite_add_1smb2_test(suite, "ERRORCODE", test_errorcode);
        torture_suite_add_1smb2_test(suite, "ZEROBYTELENGTH",
            test_zerobytelength);
+       torture_suite_add_1smb2_test(suite, "ZEROBYTEREAD",
+           test_zerobyteread);
        torture_suite_add_1smb2_test(suite, "UNLOCK", test_unlock);
        torture_suite_add_1smb2_test(suite, "MULTIPLE-UNLOCK",
            test_multiple_unlock);