More oplock patches

Volker Lendecke Volker.Lendecke at SerNet.DE
Tue Oct 22 08:09:59 MDT 2013


Hi!

Attached find a set of new oplock patches. There's a few
cosmetic changes among functional changes. The main thing is
that we have to break to NO_OPLOCK if the breaking open
comes in with OVERWRITE_IF.

Patch-wise this depends on the 10216 fix from yesterday, so
I re-send it here.

Please review & push!

Thanks,

Volker

-- 
SerNet GmbH, Bahnhofsallee 1b, 37081 Göttingen
phone: +49-551-370000-0, fax: +49-551-370000-9
AG Göttingen, HRB 2816, GF: Dr. Johannes Loxen
http://www.sernet.de, mailto:kontakt at sernet.de
-------------- next part --------------
From 00b592e6662d42f1f47d95d7e531b2c8c4b90c7e Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Fri, 18 Oct 2013 15:12:35 +0000
Subject: [PATCH 01/15] smbd: Fix bug 10216

While refactoring find_oplock_types to validate_oplock_types I forgot
that stat opens will end up in locking.tdb. So even with a batch oplock
around we can have more than one entry. This means the consistency check
in validate_oplock_types was wrong and too strict.

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/smbd/open.c |   32 ++++++++++++++++++++++++++++----
 1 file changed, 28 insertions(+), 4 deletions(-)

diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index fa52fcc..c3e1a76 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1211,6 +1211,7 @@ static bool validate_oplock_types(files_struct *fsp,
 	bool ex_or_batch = false;
 	bool level2 = false;
 	bool no_oplock = false;
+	uint32_t num_non_stat_opens = 0;
 	uint32_t i;
 
 	/* Ignore stat or internal opens, as is done in
@@ -1235,6 +1236,8 @@ static bool validate_oplock_types(files_struct *fsp,
 			continue;
 		}
 
+		num_non_stat_opens += 1;
+
 		if (BATCH_OPLOCK_TYPE(e->op_type)) {
 			/* batch - can only be one. */
 			if (share_mode_stale_pid(d, i)) {
@@ -1294,7 +1297,7 @@ static bool validate_oplock_types(files_struct *fsp,
 
 	remove_stale_share_mode_entries(d);
 
-	if ((batch || ex_or_batch) && (d->num_share_modes != 1)) {
+	if ((batch || ex_or_batch) && (num_non_stat_opens != 1)) {
 		DEBUG(1, ("got batch (%d) or ex (%d) non-exclusively (%d)\n",
 			  (int)batch, (int)ex_or_batch,
 			  (int)d->num_share_modes));
@@ -1312,17 +1315,38 @@ static bool delay_for_oplock(files_struct *fsp,
 {
 	struct share_mode_data *d = lck->data;
 	struct share_mode_entry *entry;
+	uint32_t num_non_stat_opens = 0;
+	uint32_t i;
 
 	if ((oplock_request & INTERNAL_OPEN_ONLY) || is_stat_open(fsp->access_mask)) {
 		return false;
 	}
-	if (lck->data->num_share_modes != 1) {
+	for (i=0; i<d->num_share_modes; i++) {
+		struct share_mode_entry *e = &d->share_modes[i];
+		if (e->op_type == NO_OPLOCK && is_stat_open(e->access_mask)) {
+			continue;
+		}
+		num_non_stat_opens += 1;
+
+		/*
+		 * We found the a non-stat open, which in the exclusive/batch
+		 * case will be inspected further down.
+		 */
+		entry = e;
+	}
+	if (num_non_stat_opens == 0) {
+		/*
+		 * Nothing to wait for around
+		 */
+		return false;
+	}
+	if (num_non_stat_opens != 1) {
 		/*
-		 * More than one. There can't be any exclusive or batch left.
+		 * More than one open around. There can't be any exclusive or
+		 * batch left, this is all level2.
 		 */
 		return false;
 	}
-	entry = &d->share_modes[0];
 
 	if (server_id_is_disconnected(&entry->pid)) {
 		/*
-- 
1.7.9.5


From 6f6f8cac86c3e8163e2e93770194221962041bcb Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Fri, 18 Oct 2013 13:11:38 +0000
Subject: [PATCH 02/15] torture: Add reproducer for bug 10216

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source4/torture/raw/oplock.c |   12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/source4/torture/raw/oplock.c b/source4/torture/raw/oplock.c
index 34e270d..c0738e9 100644
--- a/source4/torture/raw/oplock.c
+++ b/source4/torture/raw/oplock.c
@@ -556,6 +556,18 @@ static bool test_raw_oplock_exclusive4(struct torture_context *tctx, struct smbc
 	CHECK_VAL(break_info.count, 0);
 	CHECK_VAL(break_info.failures, 0);
 
+	/*
+	 * Open another non-stat open. This reproduces bug 10216. Make sure it
+	 * won't happen again...
+	 */
+	io.ntcreatex.in.flags = 0;
+	io.ntcreatex.in.access_mask = SEC_FILE_READ_DATA;
+	status = smb_raw_open(cli2->tree, tctx, &io);
+	CHECK_STATUS(tctx, status, NT_STATUS_SHARING_VIOLATION);
+	torture_wait_for_oplock_break(tctx);
+	CHECK_VAL(break_info.count, 0);
+	CHECK_VAL(break_info.failures, 0);
+
 	smbcli_close(cli1->tree, fnum);
 	smbcli_close(cli2->tree, fnum2);
 
-- 
1.7.9.5


From 725adf364274f85b5e476aa6d19954925944de63 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 15 Oct 2013 12:41:11 +0000
Subject: [PATCH 03/15] smbd: validate oplock types even for internal and stat
 opens

There's no reason why we should not do this. This has turned into a pure
internal consistency check that should apply fine every time.
---
 source3/smbd/open.c |   21 +++------------------
 1 file changed, 3 insertions(+), 18 deletions(-)

diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index c3e1a76..19de472 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1194,17 +1194,10 @@ static NTSTATUS send_break_message(files_struct *fsp,
 }
 
 /*
- * Return share_mode_entry pointers for :
- * 1). Batch oplock entry.
- * 2). Batch or exclusive oplock entry (may be identical to #1).
- * bool have_level2_oplock
- * bool have_no_oplock.
  * Do internal consistency checks on the share mode for a file.
  */
 
-static bool validate_oplock_types(files_struct *fsp,
-				  int oplock_request,
-				  struct share_mode_lock *lck)
+static bool validate_oplock_types(struct share_mode_lock *lck)
 {
 	struct share_mode_data *d = lck->data;
 	bool batch = false;
@@ -1214,14 +1207,6 @@ static bool validate_oplock_types(files_struct *fsp,
 	uint32_t num_non_stat_opens = 0;
 	uint32_t i;
 
-	/* Ignore stat or internal opens, as is done in
-		delay_for_batch_oplocks() and
-		delay_for_exclusive_oplocks().
-	 */
-	if ((oplock_request & INTERNAL_OPEN_ONLY) || is_stat_open(fsp->access_mask)) {
-		return true;
-	}
-
 	for (i=0; i<d->num_share_modes; i++) {
 		struct share_mode_entry *e = &d->share_modes[i];
 
@@ -2358,7 +2343,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
 			return NT_STATUS_SHARING_VIOLATION;
 		}
 
-		if (!validate_oplock_types(fsp, 0, lck)) {
+		if (!validate_oplock_types(lck)) {
 			smb_panic("validate_oplock_types failed");
 		}
 
@@ -2446,7 +2431,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
 	}
 
 	/* Get the types we need to examine. */
-	if (!validate_oplock_types(fsp, oplock_request, lck)) {
+	if (!validate_oplock_types(lck)) {
 		smb_panic("validate_oplock_types failed");
 	}
 
-- 
1.7.9.5


From 04e96ba7196bad52426aea6aed03ba1bf8649694 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Wed, 25 Sep 2013 15:33:42 -0700
Subject: [PATCH 04/15] torture: Check break level in raw.oplock.exclusive5

This is what Windows does in this case, we don't survive that. We break
to LEVEL2 here. Fixes and more precise test to follow.

We don't survive this anymore. Re-enable later.
---
 selftest/knownfail           |    2 ++
 source4/torture/raw/oplock.c |    1 +
 2 files changed, 3 insertions(+)

diff --git a/selftest/knownfail b/selftest/knownfail
index 1653cea..83e244b 100644
--- a/selftest/knownfail
+++ b/selftest/knownfail
@@ -61,6 +61,7 @@
 ^samba3.raw.acls nfs4acl_xattr-special.inheritance\(s3dc\)
 ^samba3.raw.acls nfs4acl_xattr-special.inherit_creator_owner\(s3dc\)
 ^samba3.raw.acls nfs4acl_xattr-special.inherit_creator_group\(s3dc\)
+^samba3.raw.oplock.exclusive5
 ^samba3.base.delete.deltest16a
 ^samba3.base.delete.deltest17a
 ^samba3.unix.whoami anonymous connection.whoami\(plugin_s4_dc\) # We need to resolve if we should be including SID_NT_WORLD and SID_NT_NETWORK in this token
@@ -140,6 +141,7 @@
 ^samba4.raw.sfileinfo.*.end-of-file\(.*\)$ # bug 6962
 ^samba4.raw.oplock.*.batch22 # bug 6963
 ^samba4.raw.oplock.*.doc1
+^samba4.raw.oplock.*.exclusive5
 ^samba4.raw.lock.*.zerobyteread # bug 6974
 ^samba4.smb2.lock.*.zerobyteread # bug 6974
 ^samba4.raw.streams.*.delete
diff --git a/source4/torture/raw/oplock.c b/source4/torture/raw/oplock.c
index c0738e9..02b976c 100644
--- a/source4/torture/raw/oplock.c
+++ b/source4/torture/raw/oplock.c
@@ -638,6 +638,7 @@ static bool test_raw_oplock_exclusive5(struct torture_context *tctx, struct smbc
 	CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN);
 	torture_wait_for_oplock_break(tctx);
 	CHECK_VAL(break_info.count, get_break_level1_to_none_count(tctx));
+	CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_NONE);
 	CHECK_VAL(break_info.failures, 0);
 
 	smbcli_close(cli1->tree, fnum);
-- 
1.7.9.5


From 5c539a5473aff407abafafc1511543f5f0731cde Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Wed, 25 Sep 2013 16:02:20 -0700
Subject: [PATCH 05/15] torture: Add oplock break to l2/none tests

The level we have to break to depends on the create disposition of the
second opener. If it's overwriting, break to none. If it's not, break
to level2.
---
 selftest/knownfail            |    4 ++
 source4/torture/raw/oplock.c  |   96 +++++++++++++++++++++++++++++++++++++++++
 source4/torture/smb2/oplock.c |   83 +++++++++++++++++++++++++++++++++++
 3 files changed, 183 insertions(+)

diff --git a/selftest/knownfail b/selftest/knownfail
index 83e244b..b364e9c 100644
--- a/selftest/knownfail
+++ b/selftest/knownfail
@@ -62,6 +62,7 @@
 ^samba3.raw.acls nfs4acl_xattr-special.inherit_creator_owner\(s3dc\)
 ^samba3.raw.acls nfs4acl_xattr-special.inherit_creator_group\(s3dc\)
 ^samba3.raw.oplock.exclusive5
+^samba3.raw.oplock.exclusive9
 ^samba3.base.delete.deltest16a
 ^samba3.base.delete.deltest17a
 ^samba3.unix.whoami anonymous connection.whoami\(plugin_s4_dc\) # We need to resolve if we should be including SID_NT_WORLD and SID_NT_NETWORK in this token
@@ -142,6 +143,7 @@
 ^samba4.raw.oplock.*.batch22 # bug 6963
 ^samba4.raw.oplock.*.doc1
 ^samba4.raw.oplock.*.exclusive5
+^samba4.raw.oplock.*.exclusive9
 ^samba4.raw.lock.*.zerobyteread # bug 6974
 ^samba4.smb2.lock.*.zerobyteread # bug 6974
 ^samba4.raw.streams.*.delete
@@ -161,6 +163,7 @@
 ^samba4.smb2.oplock.exclusive2\(.*\)$ # samba 4 oplocks are a mess
 ^samba4.smb2.oplock.exclusive5\(.*\)$ # samba 4 oplocks are a mess
 ^samba4.smb2.oplock.exclusive6\(.*\)$ # samba 4 oplocks are a mess
+^samba4.smb2.oplock.exclusive9\(.*\)$
 ^samba4.smb2.oplock.brl3\(.*\)$ # samba 4 oplocks are a mess
 ^samba4.smb2.oplock.levelii500\(.*\)$ # samba 4 oplocks are a mess
 ^samba4.smb2.oplock.brl1\(.*\)$ # samba 4 oplocks are a mess
@@ -208,6 +211,7 @@
 ^samba3.smb2.lease.multibreak
 ^samba3.smb2.lease.v2_request
 ^samba3.smb2.oplock.batch20
+^samba3.smb2.oplock.exclusive9
 ^samba3.smb2.oplock.stream1
 ^samba3.smb2.streams.rename
 ^samba3.smb2.streams.rename2
diff --git a/source4/torture/raw/oplock.c b/source4/torture/raw/oplock.c
index 02b976c..11e325e 100644
--- a/source4/torture/raw/oplock.c
+++ b/source4/torture/raw/oplock.c
@@ -971,6 +971,100 @@ done:
 	return ret;
 }
 
+static bool test_raw_oplock_exclusive9(struct torture_context *tctx,
+				       struct smbcli_state *cli1,
+				       struct smbcli_state *cli2)
+{
+	const char *fname = BASEDIR "\\test_exclusive9.dat";
+	NTSTATUS status;
+	bool ret = true;
+	union smb_open io;
+	uint16_t fnum=0, fnum2=0;
+	int i;
+
+	struct {
+		uint32_t create_disposition;
+		uint32_t break_level;
+	} levels[] = {
+		{ NTCREATEX_DISP_SUPERSEDE, OPLOCK_BREAK_TO_NONE },
+		{ NTCREATEX_DISP_OPEN, OPLOCK_BREAK_TO_LEVEL_II },
+		{ NTCREATEX_DISP_OVERWRITE_IF, OPLOCK_BREAK_TO_NONE },
+		{ NTCREATEX_DISP_OPEN_IF, OPLOCK_BREAK_TO_LEVEL_II },
+	};
+
+	if (!torture_setup_dir(cli1, BASEDIR)) {
+		return false;
+	}
+
+	/* cleanup */
+	smbcli_unlink(cli1->tree, fname);
+
+	smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given,
+			      cli1->tree);
+	smbcli_oplock_handler(cli2->transport, oplock_handler_ack_to_given,
+			      cli1->tree);
+
+	/*
+	  base ntcreatex parms
+	*/
+	io.generic.level = RAW_OPEN_NTCREATEX;
+	io.ntcreatex.in.root_fid.fnum = 0;
+	io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+	io.ntcreatex.in.alloc_size = 0;
+	io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+	io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+	io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+	io.ntcreatex.in.create_options = 0;
+	io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+	io.ntcreatex.in.security_flags = 0;
+	io.ntcreatex.in.fname = fname;
+
+	ZERO_STRUCT(break_info);
+	smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given,
+			      cli1->tree);
+
+	for (i=0; i<ARRAY_SIZE(levels); i++) {
+
+		io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
+			NTCREATEX_FLAGS_REQUEST_OPLOCK;
+		io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
+			NTCREATEX_SHARE_ACCESS_WRITE|
+			NTCREATEX_SHARE_ACCESS_DELETE;
+		io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+		status = smb_raw_open(cli1->tree, tctx, &io);
+		CHECK_STATUS(tctx, status, NT_STATUS_OK);
+		fnum = io.ntcreatex.out.file.fnum;
+		CHECK_VAL(io.ntcreatex.out.oplock_level,
+			  EXCLUSIVE_OPLOCK_RETURN);
+
+		ZERO_STRUCT(break_info);
+
+		io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
+			NTCREATEX_FLAGS_REQUEST_OPLOCK;
+		io.ntcreatex.in.access_mask = SEC_FILE_READ_DATA;
+		io.ntcreatex.in.open_disposition =
+			levels[i].create_disposition;
+		status = smb_raw_open(cli2->tree, tctx, &io);
+		CHECK_STATUS(tctx, status, NT_STATUS_OK);
+		fnum2 = io.ntcreatex.out.file.fnum;
+		CHECK_VAL(io.ntcreatex.out.oplock_level,
+			  LEVEL_II_OPLOCK_RETURN);
+		torture_wait_for_oplock_break(tctx);
+		CHECK_VAL(break_info.count, 1);
+		CHECK_VAL(break_info.level, levels[i].break_level);
+		CHECK_VAL(break_info.failures, 0);
+
+		smbcli_close(cli1->tree, fnum);
+		smbcli_close(cli2->tree, fnum2);
+	}
+
+done:
+	smb_raw_exit(cli1->session);
+	smb_raw_exit(cli2->session);
+	smbcli_deltree(cli1->tree, BASEDIR);
+	return ret;
+}
+
 static bool test_raw_oplock_batch1(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2)
 {
 	const char *fname = BASEDIR "\\test_batch1.dat";
@@ -4087,6 +4181,8 @@ struct torture_suite *torture_raw_oplock(TALLOC_CTX *mem_ctx)
 	torture_suite_add_2smb_test(suite, "exclusive7", test_raw_oplock_exclusive7);
 	torture_suite_add_2smb_test(suite, "exclusive8",
 				    test_raw_oplock_exclusive8);
+	torture_suite_add_2smb_test(suite, "exclusive9",
+				    test_raw_oplock_exclusive9);
 	torture_suite_add_2smb_test(suite, "batch1", test_raw_oplock_batch1);
 	torture_suite_add_2smb_test(suite, "batch2", test_raw_oplock_batch2);
 	torture_suite_add_2smb_test(suite, "batch3", test_raw_oplock_batch3);
diff --git a/source4/torture/smb2/oplock.c b/source4/torture/smb2/oplock.c
index 5070d00..fb737fe 100644
--- a/source4/torture/smb2/oplock.c
+++ b/source4/torture/smb2/oplock.c
@@ -827,6 +827,87 @@ static bool test_smb2_oplock_exclusive6(struct torture_context *tctx,
 	return ret;
 }
 
+static bool test_smb2_oplock_exclusive9(struct torture_context *tctx,
+					struct smb2_tree *tree1,
+					struct smb2_tree *tree2)
+{
+	const char *fname = BASEDIR "\\test_exclusive9.dat";
+	NTSTATUS status;
+	bool ret = true;
+	union smb_open io;
+	struct smb2_handle h1, h2;
+	int i;
+
+	struct {
+		uint32_t create_disposition;
+		uint32_t break_level;
+	} levels[] = {
+		{ NTCREATEX_DISP_SUPERSEDE, SMB2_OPLOCK_LEVEL_NONE },
+		{ NTCREATEX_DISP_OPEN, SMB2_OPLOCK_LEVEL_II },
+		{ NTCREATEX_DISP_OVERWRITE_IF, SMB2_OPLOCK_LEVEL_NONE },
+		{ NTCREATEX_DISP_OPEN_IF, SMB2_OPLOCK_LEVEL_II },
+	};
+
+
+	status = torture_smb2_testdir(tree1, BASEDIR, &h1);
+	torture_assert_ntstatus_ok(tctx, status, "Error creating directory");
+	smb2_util_close(tree1, h1);
+
+	/* cleanup */
+	smb2_util_unlink(tree1, fname);
+
+	tree1->session->transport->oplock.handler = torture_oplock_handler;
+	tree1->session->transport->oplock.private_data = tree1;
+
+	/*
+	  base ntcreatex parms
+	*/
+	ZERO_STRUCT(io.smb2);
+	io.generic.level = RAW_OPEN_SMB2;
+	io.smb2.in.desired_access = SEC_RIGHTS_FILE_ALL;
+	io.smb2.in.alloc_size = 0;
+	io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
+	io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
+		NTCREATEX_SHARE_ACCESS_WRITE | NTCREATEX_SHARE_ACCESS_DELETE;
+	io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
+	io.smb2.in.create_options = 0;
+	io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS;
+	io.smb2.in.security_flags = 0;
+	io.smb2.in.fname = fname;
+
+	for (i=0; i<ARRAY_SIZE(levels); i++) {
+
+		io.smb2.in.create_flags = NTCREATEX_FLAGS_EXTENDED;
+		io.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE;
+
+		status = smb2_create(tree1, tctx, &(io.smb2));
+		torture_assert_ntstatus_ok(tctx, status,
+					   "Error opening the file");
+		h1 = io.smb2.out.file.handle;
+		CHECK_VAL(io.smb2.out.oplock_level,
+			  SMB2_OPLOCK_LEVEL_EXCLUSIVE);
+
+		ZERO_STRUCT(break_info);
+
+		io.smb2.in.create_disposition = levels[i].create_disposition;
+		status = smb2_create(tree2, tctx, &(io.smb2));
+		torture_assert_ntstatus_ok(tctx, status,
+					   "Error opening the file");
+		h2 = io.smb2.out.file.handle;
+		CHECK_VAL(io.smb2.out.oplock_level, SMB2_OPLOCK_LEVEL_II);
+
+		CHECK_VAL(break_info.count, 1);
+		CHECK_VAL(break_info.level, levels[i].break_level);
+		CHECK_VAL(break_info.failures, 0);
+
+		smb2_util_close(tree2, h2);
+		smb2_util_close(tree1, h1);
+	}
+
+	smb2_deltree(tree1, BASEDIR);
+	return ret;
+}
+
 static bool test_smb2_oplock_batch1(struct torture_context *tctx,
 				    struct smb2_tree *tree1,
 				    struct smb2_tree *tree2)
@@ -3410,6 +3491,8 @@ struct torture_suite *torture_smb2_oplocks_init(void)
 	torture_suite_add_2smb2_test(suite, "exclusive4", test_smb2_oplock_exclusive4);
 	torture_suite_add_2smb2_test(suite, "exclusive5", test_smb2_oplock_exclusive5);
 	torture_suite_add_2smb2_test(suite, "exclusive6", test_smb2_oplock_exclusive6);
+	torture_suite_add_2smb2_test(suite, "exclusive9",
+				     test_smb2_oplock_exclusive9);
 	torture_suite_add_2smb2_test(suite, "batch1", test_smb2_oplock_batch1);
 	torture_suite_add_2smb2_test(suite, "batch2", test_smb2_oplock_batch2);
 	torture_suite_add_2smb2_test(suite, "batch3", test_smb2_oplock_batch3);
-- 
1.7.9.5


From 3519b4ce064e26e842df596bc98111d642f1f383 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Thu, 26 Sep 2013 15:31:12 -0700
Subject: [PATCH 06/15] smbd: Remove unused "oplock_request" arg from
 send_break_message

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/smbd/open.c |    9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index 19de472..35a4877 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1168,9 +1168,8 @@ static NTSTATUS open_mode_check(connection_struct *conn,
  */
 
 static NTSTATUS send_break_message(files_struct *fsp,
-					struct share_mode_entry *exclusive,
-					uint64_t mid,
-					int oplock_request)
+				   struct share_mode_entry *exclusive,
+				   uint64_t mid)
 {
 	NTSTATUS status;
 	char msg[MSG_SMB_SHARE_MODE_ENTRY_SIZE];
@@ -1350,7 +1349,7 @@ static bool delay_for_oplock(files_struct *fsp,
 		if (share_mode_stale_pid(d, 0)) {
 			return false;
 		}
-		send_break_message(fsp, entry, mid, oplock_request);
+		send_break_message(fsp, entry, mid);
 		return true;
 	}
 	if (have_sharing_violation) {
@@ -1370,7 +1369,7 @@ static bool delay_for_oplock(files_struct *fsp,
 		return false;
 	}
 
-	send_break_message(fsp, entry, mid, oplock_request);
+	send_break_message(fsp, entry, mid);
 	return true;
 }
 
-- 
1.7.9.5


From 5d003dcdffdde61b6bcb516b1788447e0ba1a5ca Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Thu, 26 Sep 2013 15:35:05 -0700
Subject: [PATCH 07/15] smbd: Simplify send_break_message

We don't need an fsp here

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source3/smbd/open.c |    8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index 35a4877..b7586aa 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1167,7 +1167,7 @@ static NTSTATUS open_mode_check(connection_struct *conn,
  * our client.
  */
 
-static NTSTATUS send_break_message(files_struct *fsp,
+static NTSTATUS send_break_message(struct messaging_context *msg_ctx,
 				   struct share_mode_entry *exclusive,
 				   uint64_t mid)
 {
@@ -1181,7 +1181,7 @@ static NTSTATUS send_break_message(files_struct *fsp,
 	/* Create the message. */
 	share_mode_entry_to_message(msg, exclusive);
 
-	status = messaging_send_buf(fsp->conn->sconn->msg_ctx, exclusive->pid,
+	status = messaging_send_buf(msg_ctx, exclusive->pid,
 				    MSG_SMB_BREAK_REQUEST,
 				    (uint8 *)msg, sizeof(msg));
 	if (!NT_STATUS_IS_OK(status)) {
@@ -1349,7 +1349,7 @@ static bool delay_for_oplock(files_struct *fsp,
 		if (share_mode_stale_pid(d, 0)) {
 			return false;
 		}
-		send_break_message(fsp, entry, mid);
+		send_break_message(fsp->conn->sconn->msg_ctx, entry, mid);
 		return true;
 	}
 	if (have_sharing_violation) {
@@ -1369,7 +1369,7 @@ static bool delay_for_oplock(files_struct *fsp,
 		return false;
 	}
 
-	send_break_message(fsp, entry, mid);
+	send_break_message(fsp->conn->sconn->msg_ctx, entry, mid);
 	return true;
 }
 
-- 
1.7.9.5


From a728ffdd4af39f9c9645b82b9977004fc202ebb9 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Thu, 26 Sep 2013 15:48:42 -0700
Subject: [PATCH 08/15] smbd: Don't send op_mid in a BREAK message

The callee doesn't use this anyway
---
 source3/smbd/open.c |    8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index b7586aa..f9e6496 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1168,15 +1168,13 @@ static NTSTATUS open_mode_check(connection_struct *conn,
  */
 
 static NTSTATUS send_break_message(struct messaging_context *msg_ctx,
-				   struct share_mode_entry *exclusive,
-				   uint64_t mid)
+				   const struct share_mode_entry *exclusive)
 {
 	NTSTATUS status;
 	char msg[MSG_SMB_SHARE_MODE_ENTRY_SIZE];
 
 	DEBUG(10, ("Sending break request to PID %s\n",
 		   procid_str_static(&exclusive->pid)));
-	exclusive->op_mid = mid;
 
 	/* Create the message. */
 	share_mode_entry_to_message(msg, exclusive);
@@ -1349,7 +1347,7 @@ static bool delay_for_oplock(files_struct *fsp,
 		if (share_mode_stale_pid(d, 0)) {
 			return false;
 		}
-		send_break_message(fsp->conn->sconn->msg_ctx, entry, mid);
+		send_break_message(fsp->conn->sconn->msg_ctx, entry);
 		return true;
 	}
 	if (have_sharing_violation) {
@@ -1369,7 +1367,7 @@ static bool delay_for_oplock(files_struct *fsp,
 		return false;
 	}
 
-	send_break_message(fsp->conn->sconn->msg_ctx, entry, mid);
+	send_break_message(fsp->conn->sconn->msg_ctx, entry);
 	return true;
 }
 
-- 
1.7.9.5


From a1e4ded0d843eacfb79fda76c354d78fced93cc8 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Thu, 26 Sep 2013 15:49:54 -0700
Subject: [PATCH 09/15] smbd: Remove unused "mid" from delay_for_oplock

---
 source3/smbd/open.c |    5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index f9e6496..a9147f8 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1290,7 +1290,6 @@ static bool validate_oplock_types(struct share_mode_lock *lck)
 }
 
 static bool delay_for_oplock(files_struct *fsp,
-			     uint64_t mid,
 			     int oplock_request,
 			     struct share_mode_lock *lck,
 			     bool have_sharing_violation)
@@ -2344,7 +2343,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
 			smb_panic("validate_oplock_types failed");
 		}
 
-		if (delay_for_oplock(fsp, req->mid, 0, lck, false)) {
+		if (delay_for_oplock(fsp, 0, lck, false)) {
 			schedule_defer_open(lck, request_time, req);
 			TALLOC_FREE(lck);
 			DEBUG(10, ("Sent oplock break request to kernel "
@@ -2455,7 +2454,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
 
 	if ((req != NULL) &&
 	    delay_for_oplock(
-		    fsp, req->mid, oplock_request, lck,
+		    fsp, oplock_request, lck,
 		    NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION))) {
 		schedule_defer_open(lck, request_time, req);
 		TALLOC_FREE(lck);
-- 
1.7.9.5


From fb2ced7be6c46c89e6932399bfe85cf41873e1d8 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Thu, 26 Sep 2013 16:15:31 -0700
Subject: [PATCH 10/15] smbd: Fix raw.batch.exclusive[59]

The level we have to break to depend on the breakers create_disposition:
If we overwrite, we have to break to none.

This patch overloads the "op_type" field in the break message we send
across to the smbd holding the oplock with the oplock level we want to
break to. Because it depends on the create_disposition in the breaking
open, only the breaker can make that decision. We might want to use
a different mechanism for this in the future, but for now using the
op_type field seems acceptable to me.
---
 selftest/knownfail    |    3 ---
 source3/smbd/open.c   |   29 +++++++++++++++++++++++------
 source3/smbd/oplock.c |   10 ++++++----
 3 files changed, 29 insertions(+), 13 deletions(-)

diff --git a/selftest/knownfail b/selftest/knownfail
index b364e9c..cfa8b4b 100644
--- a/selftest/knownfail
+++ b/selftest/knownfail
@@ -61,8 +61,6 @@
 ^samba3.raw.acls nfs4acl_xattr-special.inheritance\(s3dc\)
 ^samba3.raw.acls nfs4acl_xattr-special.inherit_creator_owner\(s3dc\)
 ^samba3.raw.acls nfs4acl_xattr-special.inherit_creator_group\(s3dc\)
-^samba3.raw.oplock.exclusive5
-^samba3.raw.oplock.exclusive9
 ^samba3.base.delete.deltest16a
 ^samba3.base.delete.deltest17a
 ^samba3.unix.whoami anonymous connection.whoami\(plugin_s4_dc\) # We need to resolve if we should be including SID_NT_WORLD and SID_NT_NETWORK in this token
@@ -211,7 +209,6 @@
 ^samba3.smb2.lease.multibreak
 ^samba3.smb2.lease.v2_request
 ^samba3.smb2.oplock.batch20
-^samba3.smb2.oplock.exclusive9
 ^samba3.smb2.oplock.stream1
 ^samba3.smb2.streams.rename
 ^samba3.smb2.streams.rename2
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index a9147f8..4941453 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1168,7 +1168,8 @@ static NTSTATUS open_mode_check(connection_struct *conn,
  */
 
 static NTSTATUS send_break_message(struct messaging_context *msg_ctx,
-				   const struct share_mode_entry *exclusive)
+				   const struct share_mode_entry *exclusive,
+				   uint16_t break_to)
 {
 	NTSTATUS status;
 	char msg[MSG_SMB_SHARE_MODE_ENTRY_SIZE];
@@ -1179,6 +1180,9 @@ static NTSTATUS send_break_message(struct messaging_context *msg_ctx,
 	/* Create the message. */
 	share_mode_entry_to_message(msg, exclusive);
 
+	/* Overload entry->op_type */
+	SSVAL(msg,OP_BREAK_MSG_OP_TYPE_OFFSET, break_to);
+
 	status = messaging_send_buf(msg_ctx, exclusive->pid,
 				    MSG_SMB_BREAK_REQUEST,
 				    (uint8 *)msg, sizeof(msg));
@@ -1292,12 +1296,14 @@ static bool validate_oplock_types(struct share_mode_lock *lck)
 static bool delay_for_oplock(files_struct *fsp,
 			     int oplock_request,
 			     struct share_mode_lock *lck,
-			     bool have_sharing_violation)
+			     bool have_sharing_violation,
+			     uint32_t create_disposition)
 {
 	struct share_mode_data *d = lck->data;
 	struct share_mode_entry *entry;
 	uint32_t num_non_stat_opens = 0;
 	uint32_t i;
+	uint16_t break_to;
 
 	if ((oplock_request & INTERNAL_OPEN_ONLY) || is_stat_open(fsp->access_mask)) {
 		return false;
@@ -1342,11 +1348,21 @@ static bool delay_for_oplock(files_struct *fsp,
 		return false;
 	}
 
+	switch (create_disposition) {
+	case FILE_SUPERSEDE:
+	case FILE_OVERWRITE_IF:
+		break_to = NO_OPLOCK;
+		break;
+	default:
+		break_to = LEVEL_II_OPLOCK;
+		break;
+	}
+
 	if (have_sharing_violation && (entry->op_type & BATCH_OPLOCK)) {
 		if (share_mode_stale_pid(d, 0)) {
 			return false;
 		}
-		send_break_message(fsp->conn->sconn->msg_ctx, entry);
+		send_break_message(fsp->conn->sconn->msg_ctx, entry, break_to);
 		return true;
 	}
 	if (have_sharing_violation) {
@@ -1366,7 +1382,7 @@ static bool delay_for_oplock(files_struct *fsp,
 		return false;
 	}
 
-	send_break_message(fsp->conn->sconn->msg_ctx, entry);
+	send_break_message(fsp->conn->sconn->msg_ctx, entry, break_to);
 	return true;
 }
 
@@ -2343,7 +2359,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
 			smb_panic("validate_oplock_types failed");
 		}
 
-		if (delay_for_oplock(fsp, 0, lck, false)) {
+		if (delay_for_oplock(fsp, 0, lck, false, create_disposition)) {
 			schedule_defer_open(lck, request_time, req);
 			TALLOC_FREE(lck);
 			DEBUG(10, ("Sent oplock break request to kernel "
@@ -2455,7 +2471,8 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
 	if ((req != NULL) &&
 	    delay_for_oplock(
 		    fsp, oplock_request, lck,
-		    NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION))) {
+		    NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION),
+		    create_disposition)) {
 		schedule_defer_open(lck, request_time, req);
 		TALLOC_FREE(lck);
 		fd_close(fsp);
diff --git a/source3/smbd/oplock.c b/source3/smbd/oplock.c
index e2880c5..f1b89b4 100644
--- a/source3/smbd/oplock.c
+++ b/source3/smbd/oplock.c
@@ -510,6 +510,7 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx,
 		struct smbd_server_connection);
 	struct server_id self = messaging_server_id(sconn->msg_ctx);
 	struct kernel_oplocks *koplocks = sconn->oplocks.kernel_ops;
+	uint16_t break_to;
 
 	if (data->data == NULL) {
 		DEBUG(0, ("Got NULL buffer\n"));
@@ -523,9 +524,10 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx,
 
 	/* De-linearize incoming message. */
 	message_to_share_mode_entry(&msg, (char *)data->data);
+	break_to = msg.op_type;
 
-	DEBUG(10, ("Got oplock break message from pid %s: %s/%llu\n",
-		   server_id_str(talloc_tos(), &src),
+	DEBUG(10, ("Got oplock break to %u message from pid %s: %s/%llu\n",
+		   (unsigned)break_to, server_id_str(talloc_tos(), &src),
 		   file_id_string_tos(&msg.id),
 		   (unsigned long long)msg.share_file_id));
 
@@ -545,8 +547,7 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx,
 		return;
 	}
 
-	if (EXCLUSIVE_OPLOCK_TYPE(msg.op_type) &&
-	    !EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) {
+	if (break_to == fsp->oplock_type) {
 		DEBUG(3, ("Already downgraded oplock on %s: %s\n",
 			  file_id_string_tos(&fsp->file_id),
 			  fsp_str_dbg(fsp)));
@@ -556,6 +557,7 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx,
 	use_kernel = lp_kernel_oplocks(SNUM(fsp->conn)) && koplocks;
 
 	if ((global_client_caps & CAP_LEVEL_II_OPLOCKS) &&
+	    (break_to != NO_OPLOCK) &&
 	    !(use_kernel && !(koplocks->flags & KOPLOCKS_LEVEL2_SUPPORTED)) &&
 	    lp_level2_oplocks(SNUM(fsp->conn))) {
 		break_to_level2 = True;
-- 
1.7.9.5


From 84f9bd09db635271fafd7720788dc438aa0928da Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 15 Oct 2013 15:22:06 +0000
Subject: [PATCH 11/15] torture: Add a test showing we have to break L2 at
 open time

---
 selftest/knownfail           |    2 +
 source4/torture/raw/oplock.c |  105 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 107 insertions(+)

diff --git a/selftest/knownfail b/selftest/knownfail
index cfa8b4b..25289d2 100644
--- a/selftest/knownfail
+++ b/selftest/knownfail
@@ -61,6 +61,7 @@
 ^samba3.raw.acls nfs4acl_xattr-special.inheritance\(s3dc\)
 ^samba3.raw.acls nfs4acl_xattr-special.inherit_creator_owner\(s3dc\)
 ^samba3.raw.acls nfs4acl_xattr-special.inherit_creator_group\(s3dc\)
+^samba3.raw.oplock.level_ii_1
 ^samba3.base.delete.deltest16a
 ^samba3.base.delete.deltest17a
 ^samba3.unix.whoami anonymous connection.whoami\(plugin_s4_dc\) # We need to resolve if we should be including SID_NT_WORLD and SID_NT_NETWORK in this token
@@ -142,6 +143,7 @@
 ^samba4.raw.oplock.*.doc1
 ^samba4.raw.oplock.*.exclusive5
 ^samba4.raw.oplock.*.exclusive9
+^samba4.raw.oplock.*.level_ii_1
 ^samba4.raw.lock.*.zerobyteread # bug 6974
 ^samba4.smb2.lock.*.zerobyteread # bug 6974
 ^samba4.raw.streams.*.delete
diff --git a/source4/torture/raw/oplock.c b/source4/torture/raw/oplock.c
index 11e325e..26baabc 100644
--- a/source4/torture/raw/oplock.c
+++ b/source4/torture/raw/oplock.c
@@ -1065,6 +1065,109 @@ done:
 	return ret;
 }
 
+static bool test_raw_oplock_level_ii_1(struct torture_context *tctx,
+				       struct smbcli_state *cli1,
+				       struct smbcli_state *cli2)
+{
+	const char *fname = BASEDIR "\\test_level_ii_1.dat";
+	NTSTATUS status;
+	bool ret = true;
+	union smb_open io;
+	uint16_t fnum=0, fnum2=0;
+
+	if (!torture_setup_dir(cli1, BASEDIR)) {
+		return false;
+	}
+
+	/* cleanup */
+	smbcli_unlink(cli1->tree, fname);
+
+	smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_given,
+			      cli1->tree);
+	smbcli_oplock_handler(cli2->transport, oplock_handler_ack_to_given,
+			      cli1->tree);
+
+	/*
+	  base ntcreatex parms
+	*/
+	io.generic.level = RAW_OPEN_NTCREATEX;
+	io.ntcreatex.in.root_fid.fnum = 0;
+	io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+	io.ntcreatex.in.alloc_size = 0;
+	io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+	io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+	io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+	io.ntcreatex.in.create_options = 0;
+	io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+	io.ntcreatex.in.security_flags = 0;
+	io.ntcreatex.in.fname = fname;
+
+	io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
+		NTCREATEX_FLAGS_REQUEST_OPLOCK;
+	io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
+		NTCREATEX_SHARE_ACCESS_WRITE|
+		NTCREATEX_SHARE_ACCESS_DELETE;
+	io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+	status = smb_raw_open(cli1->tree, tctx, &io);
+	CHECK_STATUS(tctx, status, NT_STATUS_OK);
+	fnum = io.ntcreatex.out.file.fnum;
+	CHECK_VAL(io.ntcreatex.out.oplock_level, EXCLUSIVE_OPLOCK_RETURN);
+
+	ZERO_STRUCT(break_info);
+
+	io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
+		NTCREATEX_FLAGS_REQUEST_OPLOCK;
+	io.ntcreatex.in.access_mask = SEC_FILE_READ_DATA|SEC_FILE_WRITE_DATA;
+	io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+	status = smb_raw_open(cli2->tree, tctx, &io);
+	CHECK_STATUS(tctx, status, NT_STATUS_OK);
+	fnum2 = io.ntcreatex.out.file.fnum;
+	CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN);
+	torture_wait_for_oplock_break(tctx);
+	CHECK_VAL(break_info.count, 1);
+	CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II);
+	CHECK_VAL(break_info.failures, 0);
+
+	status = smbcli_close(cli2->tree, fnum2);
+	CHECK_STATUS(tctx, status, NT_STATUS_OK);
+
+	/*
+	 * fnum1 has a level2 oplock now
+	 */
+
+	ZERO_STRUCT(break_info);
+
+	/*
+	 * Don't answer the break to none that will come in
+	 */
+
+	smbcli_oplock_handler(cli1->transport, oplock_handler_timeout,
+			      cli1->tree);
+
+	io.ntcreatex.in.flags = 0;
+	io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OVERWRITE_IF;
+
+	status = smb_raw_open(cli2->tree, tctx, &io);
+	CHECK_STATUS(tctx, status, NT_STATUS_OK);
+	fnum2 = io.ntcreatex.out.file.fnum;
+	CHECK_VAL(io.ntcreatex.out.oplock_level, NO_OPLOCK_RETURN);
+	torture_wait_for_oplock_break(tctx);
+	CHECK_VAL(break_info.count, 1);
+	CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_NONE);
+	CHECK_VAL(break_info.failures, 0);
+
+	status = smbcli_close(cli2->tree, fnum2);
+	CHECK_STATUS(tctx, status, NT_STATUS_OK);
+	status = smbcli_close(cli1->tree, fnum);
+	CHECK_STATUS(tctx, status, NT_STATUS_OK);
+
+done:
+	smb_raw_exit(cli1->session);
+	smb_raw_exit(cli2->session);
+	smbcli_deltree(cli1->tree, BASEDIR);
+	return ret;
+}
+
 static bool test_raw_oplock_batch1(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2)
 {
 	const char *fname = BASEDIR "\\test_batch1.dat";
@@ -4183,6 +4286,8 @@ struct torture_suite *torture_raw_oplock(TALLOC_CTX *mem_ctx)
 				    test_raw_oplock_exclusive8);
 	torture_suite_add_2smb_test(suite, "exclusive9",
 				    test_raw_oplock_exclusive9);
+	torture_suite_add_2smb_test(suite, "level_ii_1",
+				    test_raw_oplock_level_ii_1);
 	torture_suite_add_2smb_test(suite, "batch1", test_raw_oplock_batch1);
 	torture_suite_add_2smb_test(suite, "batch2", test_raw_oplock_batch2);
 	torture_suite_add_2smb_test(suite, "batch3", test_raw_oplock_batch3);
-- 
1.7.9.5


From 59a4c1a12dd480bc9535e0c6af5bc7df6c241b8e Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Wed, 16 Oct 2013 21:21:56 +0200
Subject: [PATCH 12/15] smbd: Fix breaking level2 on OVERWRITE
 create_disposition

This is shown by the new raw.oplock.level_ii_1 test

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 selftest/knownfail  |    1 -
 source3/smbd/open.c |   11 +++++++++++
 2 files changed, 11 insertions(+), 1 deletion(-)

diff --git a/selftest/knownfail b/selftest/knownfail
index 25289d2..ab6d45f 100644
--- a/selftest/knownfail
+++ b/selftest/knownfail
@@ -61,7 +61,6 @@
 ^samba3.raw.acls nfs4acl_xattr-special.inheritance\(s3dc\)
 ^samba3.raw.acls nfs4acl_xattr-special.inherit_creator_owner\(s3dc\)
 ^samba3.raw.acls nfs4acl_xattr-special.inherit_creator_group\(s3dc\)
-^samba3.raw.oplock.level_ii_1
 ^samba3.base.delete.deltest16a
 ^samba3.base.delete.deltest17a
 ^samba3.unix.whoami anonymous connection.whoami\(plugin_s4_dc\) # We need to resolve if we should be including SID_NT_WORLD and SID_NT_NETWORK in this token
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index 4941453..c33a4cf 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1372,6 +1372,17 @@ static bool delay_for_oplock(files_struct *fsp,
 		 */
 		return false;
 	}
+	if (LEVEL_II_OPLOCK_TYPE(entry->op_type) &&
+	    (break_to == NO_OPLOCK)) {
+		if (share_mode_stale_pid(d, 0)) {
+			return false;
+		}
+		DEBUG(10, ("Asynchronously breaking level2 oplock for "
+			   "create_disposition=%u\n",
+			   (unsigned)create_disposition));
+		send_break_message(fsp->conn->sconn->msg_ctx, entry, break_to);
+		return false;
+	}
 	if (!EXCLUSIVE_OPLOCK_TYPE(entry->op_type)) {
 		/*
 		 * No break for NO_OPLOCK or LEVEL2_OPLOCK oplocks
-- 
1.7.9.5


From 4abf4ec8c70bf35fc67f906e3b27686ee8e6e456 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 22 Oct 2013 11:33:42 +0000
Subject: [PATCH 13/15] smbd: Use MSG_SMB_BREAK_REQUEST for async l2 breaks

Now that we transmit the level we want to break to via the msg.op_type
we can unify MSG_SMB_BREAK_REQUEST and MSG_SMB_ASYNC_LEVEL2_BREAK and
thus simplify the code a bit.
---
 source3/smbd/oplock.c |   12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/source3/smbd/oplock.c b/source3/smbd/oplock.c
index f1b89b4..3eaf126 100644
--- a/source3/smbd/oplock.c
+++ b/source3/smbd/oplock.c
@@ -577,8 +577,14 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx,
 			OPLOCKLEVEL_II : OPLOCKLEVEL_NONE);
 	}
 
+	if ((fsp->oplock_type == LEVEL_II_OPLOCK) && (break_to == NO_OPLOCK)) {
+		/*
+		 * This is an async break without a reply and thus no timeout
+		 */
+		remove_oplock(fsp);
+		return;
+	}
 	fsp->sent_oplock_break = break_to_level2 ? LEVEL_II_BREAK_SENT:BREAK_TO_NONE_SENT;
-
 	add_oplock_timeout_handler(fsp);
 }
 
@@ -767,9 +773,11 @@ static void do_break_to_none(struct tevent_context *ctx,
 		}
 
 		share_mode_entry_to_message(msg, share_entry);
+		/* Overload entry->op_type */
+		SSVAL(msg,OP_BREAK_MSG_OP_TYPE_OFFSET, NO_OPLOCK);
 
 		messaging_send_buf(state->sconn->msg_ctx, share_entry->pid,
-				   MSG_SMB_ASYNC_LEVEL2_BREAK,
+				   MSG_SMB_BREAK_REQUEST,
 				   (uint8 *)msg, sizeof(msg));
 	}
 
-- 
1.7.9.5


From 367d37ae08cb502eab3531241a36c4bcb07120e0 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 22 Oct 2013 11:37:45 +0000
Subject: [PATCH 14/15] smbd: Remove MSG_SMB_ASYNC_LEVEL2_BREAK

---
 source3/librpc/idl/messaging.idl |    2 +-
 source3/smbd/oplock.c            |   84 --------------------------------------
 2 files changed, 1 insertion(+), 85 deletions(-)

diff --git a/source3/librpc/idl/messaging.idl b/source3/librpc/idl/messaging.idl
index 9d16570..39532f0 100644
--- a/source3/librpc/idl/messaging.idl
+++ b/source3/librpc/idl/messaging.idl
@@ -67,7 +67,7 @@ interface messaging
 		MSG_SMB_UNLOCK			= 0x0305,
 		MSG_SMB_BREAK_REQUEST		= 0x0306,
 		/* MSG_SMB_BREAK_RESPONSE	= 0x0307,  Obsoleted */
-		MSG_SMB_ASYNC_LEVEL2_BREAK	= 0x0308,
+		/* MSG_SMB_ASYNC_LEVEL2_BREAK	= 0x0308,  Obsoleted */
 		/* MSG_SMB_OPEN_RETRY		= 0x0309,  Obsoleted */
 		MSG_SMB_KERNEL_BREAK		= 0x030A,
 		MSG_SMB_FILE_RENAME		= 0x030B,
diff --git a/source3/smbd/oplock.c b/source3/smbd/oplock.c
index 3eaf126..d30aa4b 100644
--- a/source3/smbd/oplock.c
+++ b/source3/smbd/oplock.c
@@ -410,88 +410,6 @@ static void send_break_message_smb1(files_struct *fsp, int level)
 }
 
 /*******************************************************************
- This handles the case of a write triggering a break to none
- message on a level2 oplock.
- When we get this message we may be in any of two states :
- NO_OPLOCK, LEVEL_II. We only send a message to
- the client for LEVEL2.
-*******************************************************************/
-
-static void process_oplock_async_level2_break_message(struct messaging_context *msg_ctx,
-						      void *private_data,
-						      uint32_t msg_type,
-						      struct server_id src,
-						      DATA_BLOB *data)
-{
-	struct share_mode_entry msg;
-	files_struct *fsp;
-	struct smbd_server_connection *sconn =
-		talloc_get_type_abort(private_data,
-		struct smbd_server_connection);
-	struct server_id self = messaging_server_id(sconn->msg_ctx);
-
-	if (data->data == NULL) {
-		DEBUG(0, ("Got NULL buffer\n"));
-		return;
-	}
-
-	if (data->length != MSG_SMB_SHARE_MODE_ENTRY_SIZE) {
-		DEBUG(0, ("Got invalid msg len %d\n", (int)data->length));
-		return;
-	}
-
-	/* De-linearize incoming message. */
-	message_to_share_mode_entry(&msg, (char *)data->data);
-
-	DEBUG(10, ("Got oplock async level 2 break message from pid %s: "
-		   "%s/%llu\n", server_id_str(talloc_tos(), &src),
-		   file_id_string_tos(&msg.id),
-		   (unsigned long long)msg.share_file_id));
-
-	fsp = initial_break_processing(sconn, msg.id, msg.share_file_id);
-
-	if (fsp == NULL) {
-		/* We hit a race here. Break messages are sent, and before we
-		 * get to process this message, we have closed the file. 
-		 * No need to reply as this is an async message. */
-		DEBUG(3, ("process_oplock_async_level2_break_message: Did not find fsp, ignoring\n"));
-		return;
-	}
-
-
-	if (fsp->oplock_type == NO_OPLOCK) {
-		/* We already got a "break to none" message and we've handled
-		 * it.  just ignore. */
-		DEBUG(3, ("process_oplock_async_level2_break_message: already "
-			  "broken to none, ignoring.\n"));
-		return;
-	}
-
-	/* Ensure we're really at level2 state. */
-	SMB_ASSERT(fsp->oplock_type == LEVEL_II_OPLOCK);
-
-	DEBUG(10,("process_oplock_async_level2_break_message: sending break "
-		  "to none message for %s, file %s\n", fsp_fnum_dbg(fsp),
-		  fsp_str_dbg(fsp)));
-
-	/* Need to wait before sending a break
-	   message if we sent ourselves this message. */
-	if (serverid_equal(&self, &src)) {
-		wait_before_sending_break();
-	}
-
-	/* Now send a break to none message to our client. */
-	if (sconn->using_smb2) {
-		send_break_message_smb2(fsp, OPLOCKLEVEL_NONE);
-	} else {
-		send_break_message_smb1(fsp, OPLOCKLEVEL_NONE);
-	}
-
-	/* Async level2 request, don't send a reply, just remove the oplock. */
-	remove_oplock(fsp);
-}
-
-/*******************************************************************
  This handles the generic oplock break message from another smbd.
 *******************************************************************/
 
@@ -870,8 +788,6 @@ bool init_oplocks(struct smbd_server_connection *sconn)
 
 	messaging_register(sconn->msg_ctx, sconn, MSG_SMB_BREAK_REQUEST,
 			   process_oplock_break_message);
-	messaging_register(sconn->msg_ctx, sconn, MSG_SMB_ASYNC_LEVEL2_BREAK,
-			   process_oplock_async_level2_break_message);
 	messaging_register(sconn->msg_ctx, sconn, MSG_SMB_KERNEL_BREAK,
 			   process_kernel_oplock_break);
 	return true;
-- 
1.7.9.5


From 32576e3488db8407765c6dc725a2fa3d88858064 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Wed, 16 Oct 2013 21:34:15 +0200
Subject: [PATCH 15/15] torture: Extend the raw.oplock.level_ii_1 test

smbd broke to none twice. Make sure this won't happen again :-)

This used to happen before the MSG_SMB_BREAK_RESPONSE merge. In
process_oplock_break_message we did not call remove_oplock, which would
have prevented this.

Signed-off-by: Volker Lendecke <vl at samba.org>
---
 source4/torture/raw/oplock.c |   14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/source4/torture/raw/oplock.c b/source4/torture/raw/oplock.c
index 26baabc..a4f6a05 100644
--- a/source4/torture/raw/oplock.c
+++ b/source4/torture/raw/oplock.c
@@ -1074,6 +1074,8 @@ static bool test_raw_oplock_level_ii_1(struct torture_context *tctx,
 	bool ret = true;
 	union smb_open io;
 	uint16_t fnum=0, fnum2=0;
+	char c = 0;
+	ssize_t written;
 
 	if (!torture_setup_dir(cli1, BASEDIR)) {
 		return false;
@@ -1156,6 +1158,18 @@ static bool test_raw_oplock_level_ii_1(struct torture_context *tctx,
 	CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_NONE);
 	CHECK_VAL(break_info.failures, 0);
 
+	/*
+	 * Check that a write does not cause another break. This used to be a
+	 * bug in smbd.
+	 */
+
+	ZERO_STRUCT(break_info);
+	written = smbcli_write(cli2->tree, fnum2, 0, &c, 0, 1);
+	CHECK_VAL(written, 1);
+	torture_wait_for_oplock_break(tctx);
+	CHECK_VAL(break_info.count, 0);
+	CHECK_VAL(break_info.failures, 0);
+
 	status = smbcli_close(cli2->tree, fnum2);
 	CHECK_STATUS(tctx, status, NT_STATUS_OK);
 	status = smbcli_close(cli1->tree, fnum);
-- 
1.7.9.5



More information about the samba-technical mailing list