The branch, master has been updated via 48358b3... s4/torture: add multiple lock cancel test via ad9c5a7... s4/torture: add addition multiple lock tests via dfbb92e... s4/torture: fix build warnings by removing unecessary const via 438b7c4... s4/torture: Add target functionality parameters to SMBv1 BRL tests from 9a3d9ab... s3-selftest: run LOCAL-NDR when running make selftest.
http://gitweb.samba.org/?p=samba.git;a=shortlog;h=master - Log ----------------------------------------------------------------- commit 48358b3eaa425d8fbfec7bfd8ccf56860b5a1ba0 Author: Steven Danneman <steven.danne...@isilon.com> Date: Wed Nov 25 17:39:42 2009 -0800 s4/torture: add multiple lock cancel test See what happens when we have multiple outstanding lock requests and we try to cancel both of them within a single LockingAndX. On Windows, it seems only the first lock in the array is cancelled, and the second is left pending. Though, this behavior goes against the MS-CIFS spec. commit ad9c5a7b881bd28f408a178766a00098bab19157 Author: Steven Danneman <steven.danne...@isilon.com> Date: Mon Nov 30 17:05:27 2009 -0800 s4/torture: add addition multiple lock tests * test that 2 locks in a single LockAndX are transactional * test that 1 unlock and 1 lock in a single LockAndX are not transactional * test that SMB2 doesn't like mixed lock/unlock in a single PDU commit dfbb92e2a1c3478c9b1263adcc4818afe2acd6f7 Author: Steven Danneman <steven.danne...@isilon.com> Date: Tue Nov 24 18:38:46 2009 -0800 s4/torture: fix build warnings by removing unecessary const commit 438b7c41aecaad55f03d2f19a0f33bb57decefa9 Author: Steven Danneman <steven.danne...@isilon.com> Date: Tue Nov 24 16:58:25 2009 -0800 s4/torture: Add target functionality parameters to SMBv1 BRL tests Abstract the server requirements to pass some BRL tests. * The new default for >64bit lock tests, is that the server should return STATUS_INVALID_LOCK_RANGE. * Add parameter for targets that don't implement DENY_DOS ----------------------------------------------------------------------- Summary of changes: source4/torture/raw/lock.c | 195 ++++++++++++++++++++++++++++++++-- source4/torture/smb2/lock.c | 241 +++++++++++++++++++++++++++++++++++++----- source4/torture/smbtorture.c | 7 +- source4/torture/smbtorture.h | 9 ++ 4 files changed, 413 insertions(+), 39 deletions(-) Changeset truncated at 500 lines: diff --git a/source4/torture/raw/lock.c b/source4/torture/raw/lock.c index 610cac9..f36d492 100644 --- a/source4/torture/raw/lock.c +++ b/source4/torture/raw/lock.c @@ -69,6 +69,19 @@ }} while (0) #define BASEDIR "\\testlock" +#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)) +#define TARGET_SUPPORTS_INVALID_LOCK_RANGE(_tctx) \ + (torture_setting_bool(_tctx, "invalid_lock_range_support", true)) +#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))) +#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 */ @@ -80,6 +93,9 @@ 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; } @@ -361,7 +377,7 @@ 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); - if (TARGET_IS_WIN7(tctx) || TARGET_IS_SAMBA4(tctx)) + if (TARGET_SUPPORTS_INVALID_LOCK_RANGE(tctx)) CHECK_STATUS(status, NT_STATUS_INVALID_LOCK_RANGE); else CHECK_STATUS(status, NT_STATUS_OK); @@ -484,7 +500,7 @@ 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)) { @@ -510,6 +526,9 @@ 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); @@ -552,13 +571,97 @@ static bool test_async(struct torture_context *tctx, torture_assert(tctx,!(time(NULL) > t+2), talloc_asprintf(tctx, "lock cancel was not immediate (%s)\n", __location__)); + /* 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 + * cancels both locks. */ + 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); + if (TARGET_IS_WINDOWS(tctx) || TARGET_IS_SAMBA3(tctx)) { + 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); + if (TARGET_IS_SAMBA3(tctx)) { + /* Samba3 supports multiple cancels in a single PDU. */ + CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, + ERRcancelviolation)); + } else { + 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__)); + } else { + CHECK_STATUS(status, NT_STATUS_DOS(ERRDOS, ERRcancelviolation)); + } + + /* 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_FILE_LOCK_CONFLICT); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); io.lockx.in.timeout = 5000; req = smb_raw_lock_send(cli->tree, &io); @@ -780,6 +883,7 @@ static bool test_errorcode(struct torture_context *tctx, time_t start; int t; int delay; + uint16_t deny_mode = 0; if (!torture_setup_dir(cli, BASEDIR)) { return false; @@ -796,14 +900,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; @@ -1054,7 +1164,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; @@ -1322,7 +1432,7 @@ struct double_lock_test { /** * Tests zero byte locks. */ -static const struct double_lock_test zero_byte_tests[] = { +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: @@ -1729,6 +1839,73 @@ static bool test_multiple_unlock(struct torture_context *tctx, struct smbcli_sta 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); diff --git a/source4/torture/smb2/lock.c b/source4/torture/smb2/lock.c index 95b825e..ba97a54 100644 --- a/source4/torture/smb2/lock.c +++ b/source4/torture/smb2/lock.c @@ -1702,7 +1702,6 @@ static bool test_multiple_unlock(struct torture_context *torture, uint8_t buf[200]; struct smb2_lock lck; struct smb2_lock_element el[2]; - struct smb2_lock_element el0, el1; const char *fname = BASEDIR "\\unlock_multiple.txt"; @@ -1723,41 +1722,37 @@ static bool test_multiple_unlock(struct torture_context *torture, lck.in.lock_count = 0x0002; lck.in.lock_sequence = 0x00000000; lck.in.file.handle = h; - el0.offset = 0; - el0.length = 10; - el0.reserved = 0x00000000; - el1.offset = 10; - el1.length = 10; - el1.reserved = 0x00000000; - el[0] = el0; - el[1] = el1; + el[0].offset = 0; + el[0].length = 10; + el[0].reserved = 0x00000000; + el[1].offset = 10; + el[1].length = 10; + el[1].reserved = 0x00000000; /* Test1: Acquire second lock, but not first. */ torture_comment(torture, " unlock 2 locks, first one not locked. " "Expect no locks unlocked. \n"); lck.in.lock_count = 0x0001; - el1.flags = SMB2_LOCK_FLAG_EXCLUSIVE | + el[1].flags = SMB2_LOCK_FLAG_EXCLUSIVE | SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; - lck.in.locks = &el1; + lck.in.locks = &el[1]; status = smb2_lock(tree, &lck); CHECK_STATUS(status, NT_STATUS_OK); /* Try to unlock both locks */ lck.in.lock_count = 0x0002; - el0.flags = SMB2_LOCK_FLAG_UNLOCK; - el1.flags = SMB2_LOCK_FLAG_UNLOCK; - el[0] = el0; - el[1] = el1; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + el[1].flags = SMB2_LOCK_FLAG_UNLOCK; lck.in.locks = el; status = smb2_lock(tree, &lck); CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); /* Second lock should not be unlocked. */ lck.in.lock_count = 0x0001; - el1.flags = SMB2_LOCK_FLAG_EXCLUSIVE | + el[1].flags = SMB2_LOCK_FLAG_EXCLUSIVE | SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; - lck.in.locks = &el1; + lck.in.locks = &el[1]; status = smb2_lock(tree, &lck); if (TARGET_IS_W2K8(torture)) { CHECK_STATUS(status, NT_STATUS_OK); @@ -1770,8 +1765,8 @@ static bool test_multiple_unlock(struct torture_context *torture, /* cleanup */ lck.in.lock_count = 0x0001; - el1.flags = SMB2_LOCK_FLAG_UNLOCK; - lck.in.locks = &el1; + el[1].flags = SMB2_LOCK_FLAG_UNLOCK; + lck.in.locks = &el[1]; status = smb2_lock(tree, &lck); CHECK_STATUS(status, NT_STATUS_OK); @@ -1780,37 +1775,225 @@ static bool test_multiple_unlock(struct torture_context *torture, "Expect first lock unlocked.\n"); lck.in.lock_count = 0x0001; - el0.flags = SMB2_LOCK_FLAG_EXCLUSIVE | + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; - lck.in.locks = &el0; + lck.in.locks = &el[0]; status = smb2_lock(tree, &lck); CHECK_STATUS(status, NT_STATUS_OK); /* Try to unlock both locks */ lck.in.lock_count = 0x0002; - el0.flags = SMB2_LOCK_FLAG_UNLOCK; - el1.flags = SMB2_LOCK_FLAG_UNLOCK; - el[0] = el0; - el[1] = el1; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + el[1].flags = SMB2_LOCK_FLAG_UNLOCK; lck.in.locks = el; status = smb2_lock(tree, &lck); CHECK_STATUS(status, NT_STATUS_RANGE_NOT_LOCKED); /* First lock should be unlocked. */ lck.in.lock_count = 0x0001; - el0.flags = SMB2_LOCK_FLAG_EXCLUSIVE | + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + lck.in.locks = &el[0]; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + /* cleanup */ + lck.in.lock_count = 0x0001; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + lck.in.locks = &el[0]; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Test3: Request 2 locks, second will contend. What happens to the + * first? */ + torture_comment(torture, " request 2 locks, second one will contend. " + "Expect both to fail.\n"); + + /* Lock the second range */ + lck.in.lock_count = 0x0001; + el[1].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + lck.in.locks = &el[1]; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Request both locks */ + lck.in.lock_count = 0x0002; + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; - lck.in.locks = &el0; + lck.in.locks = el; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + /* First lock should be unlocked. */ + lck.in.lock_count = 0x0001; + lck.in.locks = &el[0]; status = smb2_lock(tree, &lck); CHECK_STATUS(status, NT_STATUS_OK); /* cleanup */ + if (TARGET_IS_W2K8(torture)) { + lck.in.lock_count = 0x0001; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + lck.in.locks = &el[0]; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + torture_warning(torture, "Target has \"pretty please\" bug. " + "A contending lock request on the same handle " + "unlocks the lock.\n"); + } else { + lck.in.lock_count = 0x0002; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + el[1].flags = SMB2_LOCK_FLAG_UNLOCK; + lck.in.locks = el; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + } + + /* Test4: Request unlock and lock. The lock contends, is the unlock + * then relocked? SMB2 doesn't like the lock and unlock requests in the + * same packet. The unlock will succeed, but the lock will return + * INVALID_PARAMETER. This behavior is described in MS-SMB2 + * 3.3.5.14.1 */ + torture_comment(torture, " request unlock and lock, second one will " + "error. Expect the unlock to succeed.\n"); + + /* Lock both ranges */ + lck.in.lock_count = 0x0002; + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + el[1].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + lck.in.locks = el; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Attempt to unlock the first range and lock the second. The lock + * request will error. */ + lck.in.lock_count = 0x0002; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + el[1].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + lck.in.locks = el; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + /* The first lock should've been unlocked */ lck.in.lock_count = 0x0001; - el0.flags = SMB2_LOCK_FLAG_UNLOCK; - lck.in.locks = &el0; + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + lck.in.locks = &el[0]; status = smb2_lock(tree, &lck); CHECK_STATUS(status, NT_STATUS_OK); + /* cleanup */ + lck.in.lock_count = 0x0002; + el[0].flags = SMB2_LOCK_FLAG_UNLOCK; + el[1].flags = SMB2_LOCK_FLAG_UNLOCK; + lck.in.locks = el; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + /* Test10: SMB2 only test. Request unlock and lock in same packet. + * Neither contend. SMB2 doesn't like lock and unlock requests in the + * same packet. The unlock will succeed, but the lock will return + * INVALID_PARAMETER. */ -- Samba Shared Repository