From ad9c5a7b881bd28f408a178766a00098bab19157 Mon Sep 17 00:00:00 2001 From: Steven Danneman Date: Mon, 30 Nov 2009 17:05:27 -0800 Subject: 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 --- source4/torture/raw/lock.c | 67 ++++++++++++ source4/torture/smb2/lock.c | 241 ++++++++++++++++++++++++++++++++++++++------ 2 files changed, 279 insertions(+), 29 deletions(-) (limited to 'source4/torture') diff --git a/source4/torture/raw/lock.c b/source4/torture/raw/lock.c index 77de1d8abe..27e37a0e49 100644 --- a/source4/torture/raw/lock.c +++ b/source4/torture/raw/lock.c @@ -1750,6 +1750,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 95b825e83e..ba97a54eae 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. */ + torture_comment(torture, " request unlock and lock. Unlock will " + "succeed, but lock will fail.\n"); + + /* Lock first range */ + lck.in.lock_count = 0x0001; + 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); + + /* Attempt to unlock the first range and lock the second */ + 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); + + /* Neither lock should still be locked */ + 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); + + /* 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); + + /* Test11: SMB2 only test. Request lock and unlock in same packet. + * Neither contend. SMB2 doesn't like lock and unlock requests in the + * same packet. The lock will succeed, the unlock will fail with + * INVALID_PARAMETER, and the lock will be unlocked before return. */ + torture_comment(torture, " request lock and unlock. Both will " + "fail.\n"); + + /* Lock 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); + + /* Attempt to lock the first range and unlock the second */ + lck.in.lock_count = 0x0002; + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + el[1].flags = SMB2_LOCK_FLAG_UNLOCK; + lck.in.locks = el; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + /* First range should be unlocked, second locked. */ + lck.in.lock_count = 0x0001; + 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); + + 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_LOCK_NOT_GRANTED); + + /* 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); + } + done: smb2_util_close(tree, h); smb2_deltree(tree, BASEDIR); -- cgit