Latest leases patchset - getting there !

Stefan (metze) Metzmacher metze at samba.org
Wed Dec 3 18:20:51 MST 2014


Hi Jeremy,

here's the patchset I'd propose for master.

Please review and push.

Thanks!
metze
-------------- next part --------------
From 5c7a0ddae1700406a613925ee52e68a262a84a05 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Mon, 1 Dec 2014 13:57:57 -0800
Subject: [PATCH 01/28] s4:torture:smb2: Add test that shows the client can
 respond to a lease break over a different connection.

Signed-off-by: Jeremy Allison <jra at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 source4/torture/smb2/lease.c | 111 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 111 insertions(+)

diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c
index c7b3592..9b25eb4 100644
--- a/source4/torture/smb2/lease.c
+++ b/source4/torture/smb2/lease.c
@@ -3039,6 +3039,116 @@ static bool test_lease_v2_complex1(struct torture_context *tctx,
 	return ret;
 }
 
+static bool test_lease_v2_complex2(struct torture_context *tctx,
+				   struct smb2_tree *tree1a)
+{
+	TALLOC_CTX *mem_ctx = talloc_new(tctx);
+	struct smb2_create io1;
+	struct smb2_create io2;
+	struct smb2_lease ls1;
+	struct smb2_lease ls2;
+	struct smb2_handle h, h2;
+	struct smb2_request *req2 = NULL;
+	struct smb2_lease_break_ack ack = {};
+	NTSTATUS status;
+	const char *fname = "lease_v2_complex2.dat";
+	bool ret = true;
+	uint32_t caps;
+	enum protocol_types protocol;
+	struct smb2_tree *tree1b = NULL;
+	struct smbcli_options options1;
+
+	options1 = tree1a->session->transport->options;
+
+	caps = smb2cli_conn_server_capabilities(tree1a->session->transport->conn);
+	if (!(caps & SMB2_CAP_LEASING)) {
+		torture_skip(tctx, "leases are not supported");
+	}
+
+	protocol = smbXcli_conn_protocol(tree1a->session->transport->conn);
+	if (protocol < PROTOCOL_SMB3_00) {
+		torture_skip(tctx, "v2 leases are not supported");
+	}
+
+	tree1a->session->transport->lease.handler = torture_lease_handler;
+	tree1a->session->transport->lease.private_data = tree1a;
+	tree1a->session->transport->oplock.handler = torture_oplock_handler;
+	tree1a->session->transport->oplock.private_data = tree1a;
+
+	/* create a new connection (same client_guid) */
+	if (!torture_smb2_connection_ext(tctx, 0, &options1, &tree1b)) {
+		torture_warning(tctx, "couldn't reconnect, bailing\n");
+		ret = false;
+		goto done;
+	}
+
+	tree1b->session->transport->lease.handler = torture_lease_handler;
+	tree1b->session->transport->lease.private_data = tree1b;
+	tree1b->session->transport->oplock.handler = torture_oplock_handler;
+	tree1b->session->transport->oplock.private_data = tree1b;
+
+	smb2_util_unlink(tree1a, fname);
+
+	ZERO_STRUCT(break_info);
+
+	/* Grab RWH lease over connection 1a */
+	smb2_lease_v2_create(&io1, &ls1, false, fname, LEASE1, NULL,
+			     smb2_util_lease_state("RWH"), 0x4711);
+	status = smb2_create(tree1a, mem_ctx, &io1);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h = io1.out.file.handle;
+	CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
+	ls1.lease_epoch += 1;
+	CHECK_LEASE_V2(&io1, "RWH", true, LEASE1,
+		       0, 0, ls1.lease_epoch);
+
+	/*
+	 * we defer acking the lease break.
+	 */
+	ZERO_STRUCT(break_info);
+	break_info.lease_skip_ack = true;
+
+	/* Ask for RWH on connection 1b, different lease. */
+	smb2_lease_v2_create(&io2, &ls2, false, fname, LEASE2, NULL,
+			     smb2_util_lease_state("RWH"), 0x11);
+	req2 = smb2_create_send(tree1b, &io2);
+	torture_assert(tctx, req2 != NULL, "smb2_create_send");
+
+	ls1.lease_epoch += 1;
+
+	CHECK_BREAK_INFO_V2(tree1a->session->transport,
+			    "RWH", "RH", LEASE1, ls1.lease_epoch);
+
+	/* Send the break ACK on tree1b. */
+	ack.in.lease.lease_key =
+		break_info.lease_break.current_lease.lease_key;
+	ack.in.lease.lease_state = SMB2_LEASE_HANDLE|SMB2_LEASE_READ;
+
+	status = smb2_lease_break_ack(tree1b, &ack);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	CHECK_LEASE_BREAK_ACK(&ack, "RH", LEASE1);
+
+	ZERO_STRUCT(break_info);
+
+	status = smb2_create_recv(req2, tctx, &io2);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE_V2(&io2, "RH", true, LEASE2,
+		       0, 0, ls2.lease_epoch+1);
+	h2 = io2.out.file.handle;
+
+ done:
+	smb2_util_close(tree1a, h);
+	smb2_util_close(tree1b, h2);
+
+	smb2_util_unlink(tree1a, fname);
+
+	talloc_free(mem_ctx);
+
+	return ret;
+}
+
+
 static bool test_lease_timeout(struct torture_context *tctx,
                                struct smb2_tree *tree)
 {
@@ -3387,6 +3497,7 @@ struct torture_suite *torture_smb2_lease_init(void)
 	torture_suite_add_1smb2_test(suite, "v2_epoch2", test_lease_v2_epoch2);
 	torture_suite_add_1smb2_test(suite, "v2_epoch3", test_lease_v2_epoch3);
 	torture_suite_add_1smb2_test(suite, "v2_complex1", test_lease_v2_complex1);
+	torture_suite_add_1smb2_test(suite, "v2_complex2", test_lease_v2_complex2);
 	torture_suite_add_1smb2_test(suite, "dynamic_share", test_lease_dynamic_share);
 	torture_suite_add_1smb2_test(suite, "timeout", test_lease_timeout);
 
-- 
1.9.1


From 1f22e81964b7a1174e32cfa7b1486206b199b8c7 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Tue, 2 Dec 2014 12:58:28 -0800
Subject: [PATCH 02/28] s4:torture:smb2: Add smb2.lease.v2_breaking3 test.

This verifies the epoch handling in the multi step break.

Signed-off-by: Jeremy Allison <jra at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 source4/torture/smb2/lease.c | 208 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 207 insertions(+), 1 deletion(-)

diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c
index 9b25eb4..1665586 100644
--- a/source4/torture/smb2/lease.c
+++ b/source4/torture/smb2/lease.c
@@ -2225,6 +2225,212 @@ done:
 	return ret;
 }
 
+static bool test_lease_v2_breaking3(struct torture_context *tctx,
+				 struct smb2_tree *tree)
+{
+	TALLOC_CTX *mem_ctx = talloc_new(tctx);
+	struct smb2_create io1 = {};
+	struct smb2_create io2 = {};
+	struct smb2_create io3 = {};
+	struct smb2_lease ls1 = {};
+	struct smb2_handle h1a = {};
+	struct smb2_handle h1b = {};
+	struct smb2_handle h2 = {};
+	struct smb2_handle h3 = {};
+	struct smb2_request *req2 = NULL;
+	struct smb2_request *req3 = NULL;
+	struct torture_lease_break break_info_tmp = {};
+	struct smb2_lease_break_ack ack = {};
+	const char *fname = "v2_lease_breaking3.dat";
+	bool ret = true;
+	NTSTATUS status;
+	uint32_t caps;
+	enum protocol_types protocol;
+
+	caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
+	if (!(caps & SMB2_CAP_LEASING)) {
+		torture_skip(tctx, "leases are not supported");
+	}
+
+	protocol = smbXcli_conn_protocol(tree->session->transport->conn);
+	if (protocol < PROTOCOL_SMB3_00) {
+		torture_skip(tctx, "v2 leases are not supported");
+	}
+
+	smb2_util_unlink(tree, fname);
+
+	tree->session->transport->lease.handler	= torture_lease_handler;
+	tree->session->transport->lease.private_data = tree;
+	tree->session->transport->oplock.handler = torture_oplock_handler;
+	tree->session->transport->oplock.private_data = tree;
+
+	/*
+	 * we defer acking the lease break.
+	 */
+	ZERO_STRUCT(break_info);
+	break_info.lease_skip_ack = true;
+
+	smb2_lease_v2_create_share(&io1, &ls1, false, fname,
+				   smb2_util_share_access("RWD"),
+				   LEASE1, NULL,
+				   smb2_util_lease_state("RHW"),
+				   0x11);
+	status = smb2_create(tree, mem_ctx, &io1);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h1a = io1.out.file.handle;
+	CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
+	/* Epoch increases on open. */
+	ls1.lease_epoch += 1;
+	CHECK_LEASE_V2(&io1, "RHW", true, LEASE1, 0, 0, ls1.lease_epoch);
+
+	/*
+	 * a conflicting open is blocked until we ack the
+	 * lease break
+	 */
+	smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
+	req2 = smb2_create_send(tree, &io2);
+	torture_assert(tctx, req2 != NULL, "smb2_create_send");
+
+	/*
+	 * we got the lease break, but defer the ack.
+	 */
+	CHECK_BREAK_INFO_V2(tree->session->transport,
+			    "RWH", "RH", LEASE1, ls1.lease_epoch + 1);
+
+	torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending");
+
+	/* On receiving a lease break, we must sync the new epoch. */
+	ls1.lease_epoch = break_info.lease_break.new_epoch;
+
+	/*
+	 * a open using the same lease key is still works,
+	 * but reports SMB2_LEASE_FLAG_BREAK_IN_PROGRESS
+	 */
+	status = smb2_create(tree, mem_ctx, &io1);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h1b = io1.out.file.handle;
+	CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE_V2(&io1, "RHW", true, LEASE1, SMB2_LEASE_FLAG_BREAK_IN_PROGRESS, 0, ls1.lease_epoch);
+	smb2_util_close(tree, h1b);
+
+	/*
+	 * a conflicting open with NTCREATEX_DISP_OVERWRITE
+	 * doesn't trigger an immediate lease break to none.
+	 */
+	break_info_tmp = break_info;
+	ZERO_STRUCT(break_info);
+	smb2_oplock_create(&io3, fname, SMB2_OPLOCK_LEVEL_NONE);
+	io3.in.create_disposition = NTCREATEX_DISP_OVERWRITE;
+	req3 = smb2_create_send(tree, &io3);
+	torture_assert(tctx, req3 != NULL, "smb2_create_send");
+	CHECK_NO_BREAK(tctx);
+	break_info = break_info_tmp;
+
+	torture_assert(tctx, req3->state == SMB2_REQUEST_RECV, "req3 pending");
+
+	ack.in.lease.lease_key =
+		break_info.lease_break.current_lease.lease_key;
+	ack.in.lease.lease_state =
+		break_info.lease_break.new_lease_state;
+	ZERO_STRUCT(break_info);
+
+	/*
+	 * a open using the same lease key is still works,
+	 * but reports SMB2_LEASE_FLAG_BREAK_IN_PROGRESS
+	 */
+	status = smb2_create(tree, mem_ctx, &io1);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h1b = io1.out.file.handle;
+	CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE_V2(&io1, "RHW", true, LEASE1, SMB2_LEASE_FLAG_BREAK_IN_PROGRESS, 0, ls1.lease_epoch);
+	smb2_util_close(tree, h1b);
+
+	CHECK_NO_BREAK(tctx);
+
+	/*
+	 * We ack the lease break, but defer acking the next break (to "R")
+	 */
+	break_info.lease_skip_ack = true;
+	status = smb2_lease_break_ack(tree, &ack);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	CHECK_LEASE_BREAK_ACK(&ack, "RH", LEASE1);
+
+	/*
+	 * We got an additional break downgrading to just "R"
+	 * while we defer the ack.
+	 */
+	CHECK_BREAK_INFO_V2(tree->session->transport,
+			    "RH", "R", LEASE1, ls1.lease_epoch);
+	/* On receiving a lease break, we must sync the new epoch. */
+	ls1.lease_epoch = break_info.lease_break.new_epoch;
+
+	ack.in.lease.lease_key =
+		break_info.lease_break.current_lease.lease_key;
+	ack.in.lease.lease_state =
+		break_info.lease_break.new_lease_state;
+	ZERO_STRUCT(break_info);
+
+	/*
+	 * a open using the same lease key is still works,
+	 * but reports SMB2_LEASE_FLAG_BREAK_IN_PROGRESS
+	 */
+	status = smb2_create(tree, mem_ctx, &io1);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h1b = io1.out.file.handle;
+	CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE_V2(&io1, "RH", true, LEASE1, SMB2_LEASE_FLAG_BREAK_IN_PROGRESS, 0, ls1.lease_epoch);
+	smb2_util_close(tree, h1b);
+
+	CHECK_NO_BREAK(tctx);
+
+	torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending");
+	torture_assert(tctx, req3->state == SMB2_REQUEST_RECV, "req3 pending");
+
+	/*
+	 * We ack the downgrade to "R" and get an immediate break to none
+	 */
+	status = smb2_lease_break_ack(tree, &ack);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	CHECK_LEASE_BREAK_ACK(&ack, "R", LEASE1);
+
+	/*
+	 * We get the downgrade to none.
+	 */
+	CHECK_BREAK_INFO_V2(tree->session->transport,
+			    "R", "", LEASE1, ls1.lease_epoch);
+
+	torture_assert(tctx, req2->cancel.can_cancel,
+		       "req2 can_cancel");
+	torture_assert(tctx, req3->cancel.can_cancel,
+		       "req3 can_cancel");
+
+	ZERO_STRUCT(break_info);
+
+	status = smb2_create_recv(req2, tctx, &io2);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h2 = io2.out.file.handle;
+	CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE);
+
+	status = smb2_create_recv(req3, tctx, &io3);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h3 = io3.out.file.handle;
+	CHECK_CREATED(&io3, TRUNCATED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_VAL(io3.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE);
+
+	CHECK_NO_BREAK(tctx);
+done:
+	smb2_util_close(tree, h1a);
+	smb2_util_close(tree, h1b);
+	smb2_util_close(tree, h2);
+	smb2_util_close(tree, h3);
+
+	smb2_util_unlink(tree, fname);
+	talloc_free(mem_ctx);
+	return ret;
+}
+
+
 static bool test_lease_breaking4(struct torture_context *tctx,
 				 struct smb2_tree *tree)
 {
@@ -3465,7 +3671,6 @@ static bool test_lease_dynamic_share(struct torture_context *tctx,
 	return ret;
 }
 
-
 struct torture_suite *torture_smb2_lease_init(void)
 {
 	struct torture_suite *suite =
@@ -3485,6 +3690,7 @@ struct torture_suite *torture_smb2_lease_init(void)
 	torture_suite_add_1smb2_test(suite, "breaking1", test_lease_breaking1);
 	torture_suite_add_1smb2_test(suite, "breaking2", test_lease_breaking2);
 	torture_suite_add_1smb2_test(suite, "breaking3", test_lease_breaking3);
+	torture_suite_add_1smb2_test(suite, "v2_breaking3", test_lease_v2_breaking3);
 	torture_suite_add_1smb2_test(suite, "breaking4", test_lease_breaking4);
 	torture_suite_add_1smb2_test(suite, "breaking5", test_lease_breaking5);
 	torture_suite_add_1smb2_test(suite, "breaking6", test_lease_breaking6);
-- 
1.9.1


From 9af2ccee9e1ea523bfa481c82d1007d85a1e7139 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Wed, 3 Dec 2014 22:32:33 +0100
Subject: [PATCH 03/28] s4:torture:smb2: let smb2.lease.[v2_]complex1 check the
 R->NONE breaks

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/torture/smb2/lease.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c
index 1665586..9d14aeb 100644
--- a/source4/torture/smb2/lease.c
+++ b/source4/torture/smb2/lease.c
@@ -3040,12 +3040,12 @@ static bool test_lease_complex1(struct torture_context *tctx,
 	CHECK_STATUS(status, NT_STATUS_OK);
 
 	/* Contend with LEASE2. */
-	smb2_lease_create(&io2, &ls2, false, fname, LEASE2, smb2_util_lease_state("RHW"));
+	smb2_lease_create(&io2, &ls2, false, fname, LEASE2, smb2_util_lease_state("R"));
 	status = smb2_create(tree1b, mem_ctx, &io2);
 	CHECK_STATUS(status, NT_STATUS_OK);
 	h3 = io2.out.file.handle;
 	CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
-	CHECK_LEASE(&io2, "RH", true, LEASE2, 0);
+	CHECK_LEASE(&io2, "R", true, LEASE2, 0);
 
 	/* Verify that we were only sent one break. */
 	CHECK_BREAK_INFO("RHW", "RH", LEASE1);
@@ -3073,7 +3073,7 @@ static bool test_lease_complex1(struct torture_context *tctx,
 	CHECK_STATUS(status, NT_STATUS_OK);
 
 	ls2.lease_epoch += 1;
-	CHECK_BREAK_INFO("RH", "", LEASE2);
+	CHECK_BREAK_INFO("R", "", LEASE2);
 
 	ZERO_STRUCT(break_info);
 
@@ -3178,13 +3178,13 @@ static bool test_lease_v2_complex1(struct torture_context *tctx,
 
 	/* Contend with LEASE2. */
 	smb2_lease_v2_create(&io2, &ls2, false, fname, LEASE2, NULL,
-			     smb2_util_lease_state("RWH"), 0x11);
+			     smb2_util_lease_state("R"), 0x11);
 	status = smb2_create(tree1b, mem_ctx, &io2);
 	CHECK_STATUS(status, NT_STATUS_OK);
 	h3 = io2.out.file.handle;
 	CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
 	ls2.lease_epoch += 1;
-	CHECK_LEASE_V2(&io2, "RH", true, LEASE2,
+	CHECK_LEASE_V2(&io2, "R", true, LEASE2,
 		       0, 0, ls2.lease_epoch);
 
 	/* Verify that we were only sent one break. */
@@ -3217,7 +3217,7 @@ static bool test_lease_v2_complex1(struct torture_context *tctx,
 
 	ls2.lease_epoch += 1;
 	CHECK_BREAK_INFO_V2(tree1a->session->transport,
-			    "RH", "", LEASE2, ls2.lease_epoch);
+			    "R", "", LEASE2, ls2.lease_epoch);
 
 	ZERO_STRUCT(break_info);
 
-- 
1.9.1


From fcd2233f885f99af52b9f2fba9521f92b7e2174f Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 28 Oct 2014 15:31:46 -0700
Subject: [PATCH 04/28] s3:smbd: factor out a send_break_to_none() helper
 function

Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Jeremy Allison <jra at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 source3/smbd/oplock.c | 40 ++++++++++++++++++++++++----------------
 1 file changed, 24 insertions(+), 16 deletions(-)

diff --git a/source3/smbd/oplock.c b/source3/smbd/oplock.c
index aa99f68..62858c6 100644
--- a/source3/smbd/oplock.c
+++ b/source3/smbd/oplock.c
@@ -667,14 +667,28 @@ static void contend_level2_oplocks_begin_default(files_struct *fsp,
 	tevent_schedule_immediate(im, sconn->ev_ctx, do_break_to_none, state);
 }
 
+static void send_break_to_none(struct messaging_context *msg_ctx,
+			       const struct share_mode_entry *e)
+{
+	char msg[MSG_SMB_SHARE_MODE_ENTRY_SIZE];
+
+	share_mode_entry_to_message(msg, e);
+	/* Overload entry->op_type */
+	SSVAL(msg, OP_BREAK_MSG_OP_TYPE_OFFSET, NO_OPLOCK);
+
+	messaging_send_buf(msg_ctx, e->pid, MSG_SMB_BREAK_REQUEST,
+			   (uint8 *)msg, sizeof(msg));
+}
+
 static void do_break_to_none(struct tevent_context *ctx,
 			     struct tevent_immediate *im,
 			     void *private_data)
 {
 	struct break_to_none_state *state = talloc_get_type_abort(
 		private_data, struct break_to_none_state);
-	int i;
+	uint32_t i;
 	struct share_mode_lock *lck;
+	struct share_mode_data *d;
 
 	lck = get_existing_share_mode_lock(talloc_tos(), state->id);
 	if (lck == NULL) {
@@ -682,15 +696,15 @@ static void do_break_to_none(struct tevent_context *ctx,
 			  __func__, file_id_string_tos(&state->id)));
 		goto done;
 	}
+	d = lck->data;
 
 	DEBUG(10,("%s: num_share_modes = %d\n", __func__,
 		  lck->data->num_share_modes ));
 
-	for(i = 0; i < lck->data->num_share_modes; i++) {
-		struct share_mode_entry *share_entry = &lck->data->share_modes[i];
-		char msg[MSG_SMB_SHARE_MODE_ENTRY_SIZE];
+	for(i = 0; i < d->num_share_modes; i++) {
+		struct share_mode_entry *e = &d->share_modes[i];
 
-		if (!is_valid_share_mode_entry(share_entry)) {
+		if (!is_valid_share_mode_entry(e)) {
 			continue;
 		}
 
@@ -705,15 +719,15 @@ static void do_break_to_none(struct tevent_context *ctx,
 		 * NO_OPLOCK states. JRA.
 		 */
 
-		DEBUG(10,("%s: share_entry[%i]->op_type == %d\n", __func__,
-			  i, share_entry->op_type ));
+		DEBUG(10, ("%s: share_entry[%i]->op_type == %d\n", __func__,
+			   i, e->op_type ));
 
-		if (share_entry->op_type == NO_OPLOCK) {
+		if (e->op_type == NO_OPLOCK) {
 			continue;
 		}
 
 		/* Paranoia .... */
-		if (EXCLUSIVE_OPLOCK_TYPE(share_entry->op_type)) {
+		if (EXCLUSIVE_OPLOCK_TYPE(e->op_type)) {
 			DEBUG(0,("%s: PANIC. "
 				 "share mode entry %d is an exlusive "
 				 "oplock !\n", __func__, i ));
@@ -721,13 +735,7 @@ static void do_break_to_none(struct tevent_context *ctx,
 			abort();
 		}
 
-		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_BREAK_REQUEST,
-				   (uint8 *)msg, sizeof(msg));
+		send_break_to_none(state->sconn->msg_ctx, e);
 	}
 
 	/* We let the message receivers handle removing the oplock state
-- 
1.9.1


From df2c17aaf4171cb2902f61ee01ba1e7fa6a4ff58 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Tue, 4 Nov 2014 21:46:14 -0800
Subject: [PATCH 05/28] s3:smbd: Add fsp_client_guid() utility function to
 return the connected client guid.

Signed-off-by: Jeremy Allison <jra at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 source3/smbd/files.c | 5 +++++
 source3/smbd/proto.h | 1 +
 2 files changed, 6 insertions(+)

diff --git a/source3/smbd/files.c b/source3/smbd/files.c
index a9e8357..7116628 100644
--- a/source3/smbd/files.c
+++ b/source3/smbd/files.c
@@ -740,3 +740,8 @@ NTSTATUS fsp_set_smb_fname(struct files_struct *fsp,
 			smb_fname_str_dbg(fsp->fsp_name),
 			&fsp->name_hash);
 }
+
+const struct GUID *fsp_client_guid(const files_struct *fsp)
+{
+	return &fsp->conn->sconn->client->connections->smb2.client.guid;
+}
diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
index 1080895..0670597 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -387,6 +387,7 @@ NTSTATUS file_name_hash(connection_struct *conn,
 			const char *name, uint32_t *p_name_hash);
 NTSTATUS fsp_set_smb_fname(struct files_struct *fsp,
 			   const struct smb_filename *smb_fname_in);
+const struct GUID *fsp_client_guid(const files_struct *fsp);
 
 /* The following definitions come from smbd/ipc.c  */
 
-- 
1.9.1


From 4d66ba3f548a80b34fcbead5bdefdddc231ac094 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 13 Nov 2014 11:50:14 +0100
Subject: [PATCH 06/28] s3:smb2_server: allow smbd_smb2_send_break() with
 session == NULL and tcon == NULL

In future we want to use this for lease breaks and they're not attached
to a session.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source3/smbd/smb2_server.c | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c
index 138765e..4a3ea7d 100644
--- a/source3/smbd/smb2_server.c
+++ b/source3/smbd/smb2_server.c
@@ -2735,14 +2735,19 @@ static NTSTATUS smbd_smb2_send_break(struct smbXsrv_connection *xconn,
 				     size_t body_len)
 {
 	struct smbd_smb2_send_break_state *state;
-	bool do_encryption = session->global->encryption_required;
+	bool do_encryption = false;
+	uint64_t session_wire_id = 0;
 	uint64_t nonce_high = 0;
 	uint64_t nonce_low = 0;
 	NTSTATUS status;
 	size_t statelen;
 
-	if (tcon->global->encryption_required) {
-		do_encryption = true;
+	if (session != NULL) {
+		session_wire_id = session->global->session_wire_id;
+		do_encryption = session->global->encryption_required;
+		if (tcon->global->encryption_required) {
+			do_encryption = true;
+		}
 	}
 
 	statelen = offsetof(struct smbd_smb2_send_break_state, body) +
@@ -2768,7 +2773,7 @@ static NTSTATUS smbd_smb2_send_break(struct smbXsrv_connection *xconn,
 	SIVAL(state->tf, SMB2_TF_PROTOCOL_ID, SMB2_TF_MAGIC);
 	SBVAL(state->tf, SMB2_TF_NONCE+0, nonce_low);
 	SBVAL(state->tf, SMB2_TF_NONCE+8, nonce_high);
-	SBVAL(state->tf, SMB2_TF_SESSION_ID, session->global->session_wire_id);
+	SBVAL(state->tf, SMB2_TF_SESSION_ID, session_wire_id);
 
 	SIVAL(state->hdr, 0,				SMB2_MAGIC);
 	SSVAL(state->hdr, SMB2_HDR_LENGTH,		SMB2_HDR_BODY);
-- 
1.9.1


From e6a30c75311978662b001a97319fd18baea5eeeb Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 28 Oct 2014 15:31:46 -0700
Subject: [PATCH 07/28] s3:smb2_server: add smbd_smb2_send_lease_break() helper
 function

Pair-Programmed-With: Stefan Metzmacher <metze at samba.org>

Signed-off-by: Volker Lendecke <vl at samba.org>
Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source3/smbd/globals.h     |  6 ++++++
 source3/smbd/smb2_server.c | 23 +++++++++++++++++++++++
 2 files changed, 29 insertions(+)

diff --git a/source3/smbd/globals.h b/source3/smbd/globals.h
index c67c3c3..d85e686 100644
--- a/source3/smbd/globals.h
+++ b/source3/smbd/globals.h
@@ -251,6 +251,12 @@ NTSTATUS smbd_smb2_send_oplock_break(struct smbXsrv_connection *xconn,
 				     struct smbXsrv_tcon *tcon,
 				     struct smbXsrv_open *op,
 				     uint8_t oplock_level);
+NTSTATUS smbd_smb2_send_lease_break(struct smbXsrv_connection *xconn,
+				    uint16_t new_epoch,
+				    uint32_t lease_flags,
+				    struct smb2_lease_key *lease_key,
+				    uint32_t current_lease_state,
+				    uint32_t new_lease_state);
 
 NTSTATUS smbd_smb2_request_pending_queue(struct smbd_smb2_request *req,
 					 struct tevent_req *subreq,
diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c
index 4a3ea7d..3f23e2a 100644
--- a/source3/smbd/smb2_server.c
+++ b/source3/smbd/smb2_server.c
@@ -2868,6 +2868,29 @@ NTSTATUS smbd_smb2_send_oplock_break(struct smbXsrv_connection *xconn,
 	return smbd_smb2_send_break(xconn, session, tcon, body, sizeof(body));
 }
 
+NTSTATUS smbd_smb2_send_lease_break(struct smbXsrv_connection *xconn,
+				    uint16_t new_epoch,
+				    uint32_t lease_flags,
+				    struct smb2_lease_key *lease_key,
+				    uint32_t current_lease_state,
+				    uint32_t new_lease_state)
+{
+	uint8_t body[0x2c];
+
+	SSVAL(body, 0x00, sizeof(body));
+	SSVAL(body, 0x02, new_epoch);
+	SIVAL(body, 0x04, lease_flags);
+	SBVAL(body, 0x08, lease_key->data[0]);
+	SBVAL(body, 0x10, lease_key->data[1]);
+	SIVAL(body, 0x18, current_lease_state);
+	SIVAL(body, 0x1c, new_lease_state);
+	SIVAL(body, 0x20, 0);		/* BreakReason, MUST be 0 */
+	SIVAL(body, 0x24, 0);		/* AccessMaskHint, MUST be 0 */
+	SIVAL(body, 0x28, 0);		/* ShareMaskHint, MUST be 0 */
+
+	return smbd_smb2_send_break(xconn, NULL, NULL, body, sizeof(body));
+}
+
 static bool is_smb2_recvfile_write(struct smbd_smb2_request_read_state *state)
 {
 	NTSTATUS status;
-- 
1.9.1


From 03aeda3b4c0b416876047085818681752c576cf4 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Wed, 3 Dec 2014 17:06:08 -0800
Subject: [PATCH 08/28] s3: leases: libsmbsharemodes no longer works with SMB2
 leases inside our locking.tdb.

Remove it until a maintainer can be found.

Signed-off-by: Jeremy Allison <jra at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 packaging/RHEL-CTDB/configure.rpm   |  1 -
 packaging/RHEL-CTDB/samba.spec.tmpl |  3 ---
 packaging/RHEL/samba.spec.tmpl      |  2 --
 source3/libsmb/smb_share_modes.c    |  8 ++++++++
 source3/libsmb/smbsharemodes.pc.in  | 11 -----------
 source3/wscript_build               |  8 --------
 6 files changed, 8 insertions(+), 25 deletions(-)
 delete mode 100644 source3/libsmb/smbsharemodes.pc.in

diff --git a/packaging/RHEL-CTDB/configure.rpm b/packaging/RHEL-CTDB/configure.rpm
index 398a3f9..62a326c 100755
--- a/packaging/RHEL-CTDB/configure.rpm
+++ b/packaging/RHEL-CTDB/configure.rpm
@@ -53,7 +53,6 @@ CC="$CC" CFLAGS="-Wall -g -D_GNU_SOURCE -O3" ./configure -C \
 	--enable-fhs \
 	--with-pam_smbpass \
 	--with-libsmbclient \
-	--with-libsmbsharemodes \
 	--without-smbwrapper \
 	--with-pam \
 	--with-quotas \
diff --git a/packaging/RHEL-CTDB/samba.spec.tmpl b/packaging/RHEL-CTDB/samba.spec.tmpl
index c1789f2..056c00f 100644
--- a/packaging/RHEL-CTDB/samba.spec.tmpl
+++ b/packaging/RHEL-CTDB/samba.spec.tmpl
@@ -171,7 +171,6 @@ CFLAGS="$RPM_OPT_FLAGS $EXTRA -D_GNU_SOURCE" ./configure \
 	--enable-fhs \
 	--with-pam_smbpass \
 	--with-libsmbclient \
-	--with-libsmbsharemodes \
 	--without-smbwrapper \
 	--with-pam \
 	--with-quotas \
@@ -544,8 +543,6 @@ exit 0
 %{_includedir}/libsmbclient.h
 %{_libarchdir}/libsmbclient.*
 %{_includedir}/smb_share_modes.h
-%{_libarchdir}/libsmbsharemodes.so
-%{_libarchdir}/libsmbsharemodes.so.0
 
 %{_includedir}/netapi.h
 %{_includedir}/wbclient.h
diff --git a/packaging/RHEL/samba.spec.tmpl b/packaging/RHEL/samba.spec.tmpl
index 05e46e1..ed37994 100644
--- a/packaging/RHEL/samba.spec.tmpl
+++ b/packaging/RHEL/samba.spec.tmpl
@@ -167,7 +167,6 @@ CC="$CC" CFLAGS="$RPM_OPT_FLAGS $EXTRA -D_GNU_SOURCE" ./configure \
         --with-fhs \
 	--with-pam_smbpass \
 	--with-libsmbclient \
-	--with-libsmbsharemodes \
         --without-smbwrapper \
 	--with-pam \
 	--with-quotas \
@@ -470,7 +469,6 @@ fi
 %{_includedir}/libsmbclient.h
 %{_libarchdir}/libsmbclient.*
 %{_includedir}/smb_share_modes.h
-%{_libarchdir}/libsmbsharemodes.*
 
 %{_libarchdir}/samba/*.dat
 %{_libarchdir}/samba/*.msg
diff --git a/source3/libsmb/smb_share_modes.c b/source3/libsmb/smb_share_modes.c
index f2decc1..bf21bf5 100644
--- a/source3/libsmb/smb_share_modes.c
+++ b/source3/libsmb/smb_share_modes.c
@@ -2,6 +2,14 @@
    Samba share mode database library external interface library.
    Used by non-Samba products needing access to the Samba share mode db.
 
+   NOTICE FOR SAMBA 4.2.0
+
+   THIS CODE IS NON-FUNCTIONAL IN SAMBA 4.2.0 AND ABOVE DUE TO THE CHANGES IN
+   SHARE MODE DATABASE SCHEMA FOR SMB2 LEASES.
+
+   CONTACT THE AUTHOR jra at samba.org IF YOU WISH TO RE-ENABLE
+   THIS CODE.
+
    Copyright (C) Jeremy Allison 2005 - 2006
 
    sharemodes_procid functions (C) Copyright (C) Volker Lendecke 2005
diff --git a/source3/libsmb/smbsharemodes.pc.in b/source3/libsmb/smbsharemodes.pc.in
deleted file mode 100644
index fadc481..0000000
--- a/source3/libsmb/smbsharemodes.pc.in
+++ /dev/null
@@ -1,11 +0,0 @@
-prefix=@prefix@
-exec_prefix=@exec_prefix@
-libdir=@libdir@
-includedir=@includedir@
-
-Name: Samba libsmbsharemodes
-Description: A library
-Version: @PACKAGE_VERSION@
-Libs: @LIB_RPATH@ -L${libdir} -lsmbsharemodes
-Cflags: -I${includedir}
-URL: http://www.samba.org/
diff --git a/source3/wscript_build b/source3/wscript_build
index ff058f22..caa5c9b 100755
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -55,14 +55,6 @@ bld.SAMBA3_LIBRARY('netapi',
                     pc_files='libnet/netapi.pc',
                     vnum='0')
 
-bld.SAMBA3_LIBRARY('smbsharemodes',
-                    source='libsmb/smb_share_modes.c',
-                    public_deps='''talloc tdb_compat''',
-                    deps='''ccan-hash''',
-                    public_headers='include/smb_share_modes.h',
-                    pc_files='libsmb/smbsharemodes.pc',
-                    vnum='0')
-
 bld.SAMBA3_LIBRARY('nss_wins',
                   source='../nsswitch/wins.c',
                   deps='''param libsmb LIBTSOCKET''',
-- 
1.9.1


From 7bcef6a353367168fc848cfe15c70234604047a0 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Fri, 10 Oct 2014 16:36:54 -0700
Subject: [PATCH 09/28] s3:locking: add leases_db infrastructure

Will enable us to solve the dynamic share path problem
with leases on [homes].

We're also able to give the correct error codes when a
lease key is re-used with a different file name.

Pair-Programmed-With: Jeremy Allison <jra at samba.org>

Signed-off-by: Volker Lendecke <vl at samba.org>
Signed-off-by: Jeremy Allison <jra at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 source3/librpc/idl/leases_db.idl |  23 +++
 source3/librpc/idl/wscript_build |   1 +
 source3/librpc/wscript_build     |   5 +
 source3/locking/leases_db.c      | 387 +++++++++++++++++++++++++++++++++++++++
 source3/locking/leases_db.h      |  46 +++++
 source3/smbd/server.c            |   5 +
 source3/wscript_build            |   6 +
 7 files changed, 473 insertions(+)
 create mode 100644 source3/librpc/idl/leases_db.idl
 create mode 100644 source3/locking/leases_db.c
 create mode 100644 source3/locking/leases_db.h

diff --git a/source3/librpc/idl/leases_db.idl b/source3/librpc/idl/leases_db.idl
new file mode 100644
index 0000000..2ab1591
--- /dev/null
+++ b/source3/librpc/idl/leases_db.idl
@@ -0,0 +1,23 @@
+#include "idl_types.h"
+
+import "misc.idl";
+import "smb2_lease_struct.idl";
+import "file_id.idl";
+
+[
+	pointer_default(unique)
+]
+interface leases_db
+{
+	typedef [public] struct {
+		GUID client_guid;
+		smb2_lease_key lease_key;
+	} leases_db_key;
+
+	typedef [public] struct {
+		uint32 num_file_ids;
+		[size_is(num_file_ids)] file_id ids[];
+		[string,charset(UTF8)] char *filename;
+		[string,charset(UTF8)] char *stream_name;
+	} leases_db_value;
+}
diff --git a/source3/librpc/idl/wscript_build b/source3/librpc/idl/wscript_build
index c38fe7b..f9b1bd7 100644
--- a/source3/librpc/idl/wscript_build
+++ b/source3/librpc/idl/wscript_build
@@ -8,6 +8,7 @@ bld.SAMBA_PIDL_LIST('PIDL',
                     '''messaging.idl libnetapi.idl open_files.idl
                        perfcount.idl secrets.idl libnet_join.idl
                        smbXsrv.idl
+                       leases_db.idl
                     ''',
                     options='--includedir=%s --header --ndr-parser' % topinclude,
                     output_dir='../gen_ndr')
diff --git a/source3/librpc/wscript_build b/source3/librpc/wscript_build
index 77ae048..38d9a81 100644
--- a/source3/librpc/wscript_build
+++ b/source3/librpc/wscript_build
@@ -25,6 +25,11 @@ bld.SAMBA3_SUBSYSTEM('NDR_SMBXSRV',
 	public_deps='ndr NDR_SERVER_ID NDR_SECURITY NDR_AUTH'
 	)
 
+bld.SAMBA3_SUBSYSTEM('NDR_LEASES_DB',
+	source='gen_ndr/ndr_leases_db.c',
+	public_deps='ndr'
+	)
+
 bld.SAMBA3_SUBSYSTEM('NDR_SECRETS',
 	source='gen_ndr/ndr_secrets.c',
 	public_deps='ndr'
diff --git a/source3/locking/leases_db.c b/source3/locking/leases_db.c
new file mode 100644
index 0000000..67c93ff
--- /dev/null
+++ b/source3/locking/leases_db.c
@@ -0,0 +1,387 @@
+/*
+   Unix SMB/CIFS implementation.
+   Map lease keys to file ids
+   Copyright (C) Volker Lendecke 2013
+
+   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 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   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, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "locking/leases_db.h"
+#include "dbwrap/dbwrap.h"
+#include "dbwrap/dbwrap_open.h"
+#include "util_tdb.h"
+#include "ndr.h"
+#include "librpc/gen_ndr/ndr_leases_db.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_LOCKING
+
+/* the leases database handle */
+static struct db_context *leases_db;
+
+bool leases_db_init(bool read_only)
+{
+	if (leases_db) {
+		return true;
+	}
+
+	leases_db = db_open(NULL, lock_path("leases.tdb"), 0,
+			    TDB_DEFAULT|TDB_VOLATILE|TDB_CLEAR_IF_FIRST|
+			    TDB_INCOMPATIBLE_HASH,
+			    read_only ? O_RDONLY : O_RDWR|O_CREAT, 0644,
+			    DBWRAP_LOCK_ORDER_2, DBWRAP_FLAG_NONE);
+
+	if (leases_db == NULL) {
+		DEBUG(1, ("ERROR: Failed to initialise leases database\n"));
+		return false;
+	}
+
+	return true;
+}
+
+static bool leases_db_key(TALLOC_CTX *mem_ctx,
+			  const struct GUID *client_guid,
+			  const struct smb2_lease_key *lease_key,
+			  TDB_DATA *key)
+{
+	struct leases_db_key db_key = {
+		.client_guid = *client_guid,
+		.lease_key = *lease_key };
+	DATA_BLOB blob;
+	enum ndr_err_code ndr_err;
+
+	if (DEBUGLEVEL >= 10) {
+		DEBUG(10, ("%s:\n", __func__));
+		NDR_PRINT_DEBUG(leases_db_key, &db_key);
+	}
+
+	ndr_err = ndr_push_struct_blob(
+		&blob, mem_ctx, &db_key,
+		(ndr_push_flags_fn_t)ndr_push_leases_db_key);
+	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+		DEBUG(10, ("%s: ndr_push_struct_blob_failed: %s\n",
+			   __func__, ndr_errstr(ndr_err)));
+		return false;
+	}
+
+	*key = make_tdb_data(blob.data, blob.length);
+	return true;
+}
+
+NTSTATUS leases_db_add(const struct GUID *client_guid,
+		       const struct smb2_lease_key *lease_key,
+		       const struct file_id *id,
+		       const char *filename,
+		       const char *stream_name)
+{
+	TDB_DATA db_key, db_value;
+	DATA_BLOB blob;
+	struct db_record *rec;
+	NTSTATUS status;
+	bool ok;
+	struct leases_db_value new_value;
+	struct leases_db_value *value = NULL;
+	enum ndr_err_code ndr_err;
+
+	if (!leases_db_init(false)) {
+		return NT_STATUS_INTERNAL_ERROR;
+	}
+
+	ok = leases_db_key(talloc_tos(), client_guid, lease_key, &db_key);
+	if (!ok) {
+		DEBUG(10, ("%s: leases_db_key failed\n", __func__));
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	rec = dbwrap_fetch_locked(leases_db, talloc_tos(), db_key);
+	TALLOC_FREE(db_key.dptr);
+	if (rec == NULL) {
+		return NT_STATUS_INTERNAL_ERROR;
+	}
+
+	db_value = dbwrap_record_get_value(rec);
+	if (db_value.dsize != 0) {
+		uint32_t i;
+
+		DEBUG(10, ("%s: record exists\n", __func__));
+
+		value = talloc(talloc_tos(), struct leases_db_value);
+		if (value == NULL) {
+			status = NT_STATUS_NO_MEMORY;
+			goto out;
+		}
+
+		blob.data = db_value.dptr;
+		blob.length = db_value.dsize;
+
+		ndr_err = ndr_pull_struct_blob_all(
+			&blob, value, value,
+			(ndr_pull_flags_fn_t)ndr_pull_leases_db_value);
+		if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+			DEBUG(10, ("%s: ndr_pull_struct_blob_failed: %s\n",
+				   __func__, ndr_errstr(ndr_err)));
+			status = ndr_map_error2ntstatus(ndr_err);
+			goto out;
+		}
+
+		/* id must be unique. */
+		for (i = 0; i < value->num_file_ids; i++) {
+			if (file_id_equal(id, &value->ids[i])) {
+				status = NT_STATUS_OBJECT_NAME_COLLISION;
+				goto out;
+			}
+		}
+
+		value->ids = talloc_realloc(value, value->ids, struct file_id,
+					value->num_file_ids + 1);
+		if (value->ids == NULL) {
+			status = NT_STATUS_NO_MEMORY;
+			goto out;
+		}
+		value->ids[value->num_file_ids] = *id;
+		value->num_file_ids += 1;
+
+	} else {
+		DEBUG(10, ("%s: new record\n", __func__));
+
+		new_value = (struct leases_db_value) {
+			.num_file_ids = 1,
+			.ids = discard_const_p(struct file_id, id),
+			.filename = filename,
+			.stream_name = stream_name,
+		};
+		value = &new_value;
+	}
+
+	ndr_err = ndr_push_struct_blob(
+		&blob, talloc_tos(), value,
+		(ndr_push_flags_fn_t)ndr_push_leases_db_value);
+	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+		DEBUG(10, ("%s: ndr_push_struct_blob_failed: %s\n",
+			   __func__, ndr_errstr(ndr_err)));
+		status = ndr_map_error2ntstatus(ndr_err);
+		goto out;
+	}
+
+	if (DEBUGLEVEL >= 10) {
+		DEBUG(10, ("%s:\n", __func__));
+		NDR_PRINT_DEBUG(leases_db_value, value);
+	}
+
+	db_value = make_tdb_data(blob.data, blob.length);
+
+	status = dbwrap_record_store(rec, db_value, 0);
+	if (!NT_STATUS_IS_OK(status)) {
+		DEBUG(10, ("%s: dbwrap_record_store returned %s\n",
+			   __func__, nt_errstr(status)));
+	}
+
+  out:
+
+	if (value != &new_value) {
+		TALLOC_FREE(value);
+	}
+	TALLOC_FREE(rec);
+	return status;
+}
+
+NTSTATUS leases_db_del(const struct GUID *client_guid,
+		       const struct smb2_lease_key *lease_key,
+		       const struct file_id *id)
+{
+	TDB_DATA db_key, db_value;
+	struct db_record *rec;
+	NTSTATUS status;
+	struct leases_db_value *value;
+	enum ndr_err_code ndr_err;
+	DATA_BLOB blob;
+	uint32_t i;
+	bool ok;
+
+	if (!leases_db_init(false)) {
+		return NT_STATUS_INTERNAL_ERROR;
+	}
+
+	ok = leases_db_key(talloc_tos(), client_guid, lease_key, &db_key);
+	if (!ok) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	rec = dbwrap_fetch_locked(leases_db, talloc_tos(), db_key);
+	TALLOC_FREE(db_key.dptr);
+	if (rec == NULL) {
+		return NT_STATUS_NOT_FOUND;
+	}
+	db_value = dbwrap_record_get_value(rec);
+	if (db_value.dsize == 0) {
+		status = NT_STATUS_INTERNAL_ERROR;
+		goto out;
+	}
+
+	value = talloc(talloc_tos(), struct leases_db_value);
+	if (value == NULL) {
+		status = NT_STATUS_NO_MEMORY;
+		goto out;
+	}
+
+	blob.data = db_value.dptr;
+	blob.length = db_value.dsize;
+
+	ndr_err = ndr_pull_struct_blob_all(
+		&blob, value, value,
+		(ndr_pull_flags_fn_t)ndr_pull_leases_db_value);
+	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+		DEBUG(10, ("%s: ndr_pull_struct_blob_failed: %s\n",
+			   __func__, ndr_errstr(ndr_err)));
+		status = ndr_map_error2ntstatus(ndr_err);
+		goto out;
+	}
+
+	/* id must exist. */
+	for (i = 0; i < value->num_file_ids; i++) {
+		if (file_id_equal(id, &value->ids[i])) {
+			break;
+		}
+	}
+
+	if (i == value->num_file_ids) {
+		status = NT_STATUS_NOT_FOUND;
+		goto out;
+	}
+
+	value->ids[i] = value->ids[value->num_file_ids-1];
+	value->num_file_ids -= 1;
+
+	if (value->num_file_ids == 0) {
+		DEBUG(10, ("%s: deleting record\n", __func__));
+		status = dbwrap_record_delete(rec);
+	} else {
+		DEBUG(10, ("%s: updating record\n", __func__));
+		ndr_err = ndr_push_struct_blob(
+			&blob, talloc_tos(), value,
+			(ndr_push_flags_fn_t)ndr_push_leases_db_value);
+		if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+			DEBUG(10, ("%s: ndr_push_struct_blob_failed: %s\n",
+				   __func__, ndr_errstr(ndr_err)));
+			status = ndr_map_error2ntstatus(ndr_err);
+			goto out;
+		}
+
+		if (DEBUGLEVEL >= 10) {
+			DEBUG(10, ("%s:\n", __func__));
+			NDR_PRINT_DEBUG(leases_db_value, value);
+		}
+
+		db_value = make_tdb_data(blob.data, blob.length);
+
+		status = dbwrap_record_store(rec, db_value, 0);
+		if (!NT_STATUS_IS_OK(status)) {
+			DEBUG(10, ("%s: dbwrap_record_store returned %s\n",
+				   __func__, nt_errstr(status)));
+		}
+	}
+
+  out:
+
+	TALLOC_FREE(value);
+	TALLOC_FREE(rec);
+	return status;
+}
+
+struct leases_db_fetch_state {
+	void (*parser)(uint32_t num_file_ids,
+			struct file_id *ids, const char *filename,
+			const char *stream_name, void *private_data);
+	void *private_data;
+	NTSTATUS status;
+};
+
+static void leases_db_parser(TDB_DATA key, TDB_DATA data, void *private_data)
+{
+	struct leases_db_fetch_state *state =
+		(struct leases_db_fetch_state *)private_data;
+	DATA_BLOB blob = { .data = data.dptr, .length = data.dsize };
+	enum ndr_err_code ndr_err;
+	struct leases_db_value *value;
+
+	value = talloc(talloc_tos(), struct leases_db_value);
+	if (value == NULL) {
+		state->status = NT_STATUS_NO_MEMORY;
+		return;
+	}
+
+	ndr_err = ndr_pull_struct_blob_all(
+		&blob, value, value,
+		(ndr_pull_flags_fn_t)ndr_pull_leases_db_value);
+	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+		DEBUG(10, ("%s: ndr_pull_struct_blob_failed: %s\n",
+			   __func__, ndr_errstr(ndr_err)));
+		TALLOC_FREE(value);
+		state->status = ndr_map_error2ntstatus(ndr_err);
+		return;
+	}
+
+	if (DEBUGLEVEL >= 10) {
+		DEBUG(10, ("%s:\n", __func__));
+		NDR_PRINT_DEBUG(leases_db_value, value);
+	}
+
+	state->parser(value->num_file_ids,
+			value->ids, value->filename, value->stream_name,
+			state->private_data);
+
+	TALLOC_FREE(value);
+	state->status = NT_STATUS_OK;
+}
+
+NTSTATUS leases_db_parse(const struct GUID *client_guid,
+			 const struct smb2_lease_key *lease_key,
+			 void (*parser)(uint32_t num_file_ids,
+					struct file_id *ids,
+					const char *filename,
+					const char *stream_name,
+					void *private_data),
+			 void *private_data)
+{
+	TDB_DATA db_key;
+	struct leases_db_fetch_state state;
+	NTSTATUS status;
+	bool ok;
+
+	if (!leases_db_init(true)) {
+		return NT_STATUS_INTERNAL_ERROR;
+	}
+
+	ok = leases_db_key(talloc_tos(), client_guid, lease_key, &db_key);
+	if (!ok) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	state = (struct leases_db_fetch_state) {
+		.parser = parser,
+		.private_data = private_data,
+		.status = NT_STATUS_OK
+	};
+
+	status = dbwrap_parse_record(leases_db, db_key, leases_db_parser,
+				     &state);
+	TALLOC_FREE(db_key.dptr);
+	if (!NT_STATUS_IS_OK(status)) {
+		return status;
+	}
+	return state.status;
+}
diff --git a/source3/locking/leases_db.h b/source3/locking/leases_db.h
new file mode 100644
index 0000000..f570356
--- /dev/null
+++ b/source3/locking/leases_db.h
@@ -0,0 +1,46 @@
+/*
+ *  Unix SMB/CIFS implementation.
+ *  leases.tdb functions
+ *
+ *  Copyright (C) Volker Lendecke 2014
+ *
+ *  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 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _LEASES_DB_H_
+#define _LEASES_DB_H_
+
+struct GUID;
+struct smb2_lease_key;
+struct file_id;
+
+bool leases_db_init(bool read_only);
+NTSTATUS leases_db_add(const struct GUID *client_guid,
+		       const struct smb2_lease_key *lease_key,
+		       const struct file_id *id,
+		       const char *filename,
+		       const char *stream_name);
+NTSTATUS leases_db_del(const struct GUID *client_guid,
+		       const struct smb2_lease_key *lease_key,
+		       const struct file_id *id);
+NTSTATUS leases_db_parse(const struct GUID *client_guid,
+			 const struct smb2_lease_key *lease_key,
+			 void (*parser)(uint32_t num_file_ids,
+					struct file_id *ids,
+					const char *filename,
+					const char *stream_name,
+					void *private_data),
+			 void *private_data);
+
+#endif /* _LEASES_DB_H_ */
diff --git a/source3/smbd/server.c b/source3/smbd/server.c
index 05104da..8207bf1 100644
--- a/source3/smbd/server.c
+++ b/source3/smbd/server.c
@@ -47,6 +47,7 @@
 #include "../lib/util/pidfile.h"
 #include "lib/smbd_shim.h"
 #include "scavenger.h"
+#include "locking/leases_db.h"
 
 struct smbd_open_socket;
 struct smbd_child_pid;
@@ -1452,6 +1453,10 @@ extern void build_options(bool screen);
 	if (!locking_init())
 		exit_daemon("Samba cannot init locking", EACCES);
 
+	if (!leases_db_init(false)) {
+		exit_daemon("Samba cannot init leases", EACCES);
+	}
+
 	if (!smbd_parent_notify_init(NULL, msg_ctx, ev_ctx)) {
 		exit_daemon("Samba cannot init notification", EACCES);
 	}
diff --git a/source3/wscript_build b/source3/wscript_build
index caa5c9b..480bedc 100755
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -611,6 +611,7 @@ bld.SAMBA3_LIBRARY('smbd_base',
                    LIBAFS
                    RPC_SERVICE
                    NDR_SMBXSRV
+                   LEASES_DB
                    LIBASYS
                    sysquotas
                    ccan-hash
@@ -629,9 +630,14 @@ bld.SAMBA3_SUBSYSTEM('LOCKING',
                     deps='''
                     tdb_compat
                     talloc
+                    LEASES_DB
                     NDR_OPEN_FILES
                     FNAME_UTIL''')
 
+bld.SAMBA3_SUBSYSTEM('LEASES_DB',
+                    source='locking/leases_db.c',
+                    deps='NDR_LEASES_DB')
+
 if bld.CONFIG_GET("WITH_PROFILE"):
     bld.SAMBA3_SUBSYSTEM('PROFILE',
                          source='profile/profile.c',
-- 
1.9.1


From 016123af6ca0a7e75ac9694e8f802753275f2f13 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 27 Nov 2014 18:34:56 +0100
Subject: [PATCH 10/28] s3:open_files.idl: add data structures for SMB2.1 and
 SMB3.0 leases.

Pair-Programmed-With: Volker Lendecke <vl at samba.org>

Signed-off-by: Volker Lendecke <vl at samba.org>
Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source3/include/smb.h             |  1 +
 source3/librpc/idl/open_files.idl | 36 ++++++++++++++++++++++++++++++++++++
 source3/librpc/wscript_build      |  2 +-
 source3/locking/locking.c         | 17 +++++++++++++++--
 source3/locking/proto.h           |  5 +++--
 source3/locking/share_mode_lock.c | 12 +++++++++++-
 source3/smbd/open.c               | 10 +++++++---
 source3/utils/status.c            |  9 +++++++++
 8 files changed, 83 insertions(+), 9 deletions(-)

diff --git a/source3/include/smb.h b/source3/include/smb.h
index 53d3edc..a6589db 100644
--- a/source3/include/smb.h
+++ b/source3/include/smb.h
@@ -577,6 +577,7 @@ enum remote_arch_types {RA_UNKNOWN, RA_WFWG, RA_OS2, RA_WIN95, RA_WINNT,
 #define EXCLUSIVE_OPLOCK 		OPLOCK_EXCLUSIVE
 #define BATCH_OPLOCK 			OPLOCK_BATCH
 #define LEVEL_II_OPLOCK 		OPLOCK_LEVEL_II
+#define LEASE_OPLOCK			0x100
 
 /* The following are Samba-private. */
 #define INTERNAL_OPEN_ONLY 		0x8
diff --git a/source3/librpc/idl/open_files.idl b/source3/librpc/idl/open_files.idl
index 4278301..0a9d5fd 100644
--- a/source3/librpc/idl/open_files.idl
+++ b/source3/librpc/idl/open_files.idl
@@ -3,6 +3,8 @@
 import "server_id.idl";
 import "security.idl";
 import "file_id.idl";
+import "smb2_lease_struct.idl";
+import "misc.idl";
 
 [
 	pointer_default(unique)
@@ -10,10 +12,41 @@ import "file_id.idl";
 
 interface open_files
 {
+	typedef [public,flag(NDR_PAHEX)] struct {
+		GUID			client_guid;
+		smb2_lease_key		lease_key;
+		smb2_lease_state	current_state;
+		/*
+		 * 'breaking' indicates that we're waiting
+		 * for a lease break ack from the client
+		 * and breaking_to_requested and breaking_to_required
+		 * have a meaning.
+		 *
+		 * breaking_to_requested is the value already sent to
+		 * the client, the client needs to ack to this (or less).
+		 *
+		 * breaking_to_required is the internal value that needs to
+		 * be reached before we can reset breaking = false, this
+		 * may requires multiple roundtrips to the client, e.g.
+		 * when the lease broken to a more reduced value, while
+		 * the lease break is still in progress.
+		 *
+		 * The following can be assumed (if breaking == true):
+		 *
+		 * current_state > breaking_to_requested >= breaking_to_required
+		 */
+		boolean8		breaking;
+		smb2_lease_state	breaking_to_requested;
+		smb2_lease_state	breaking_to_required;
+		uint16			lease_version;
+		uint16			epoch;
+	} share_mode_lease;
+
 	typedef [public] struct {
 		server_id	pid;
 		hyper		op_mid;
 		uint16		op_type;
+		uint32		lease_idx;
 		uint32		access_mask;
 		uint32		share_access;
 		uint32		private_options;
@@ -29,6 +62,7 @@ interface open_files
 		 * to store this share_mode_entry on disk.
 		 */
 		[skip] boolean8	stale;
+		[skip] share_mode_lease *lease;
 	} share_mode_entry;
 
 	typedef [public] struct {
@@ -43,6 +77,8 @@ interface open_files
 		[string,charset(UTF8)] char *stream_name;
 		uint32 num_share_modes;
 		[size_is(num_share_modes)] share_mode_entry share_modes[];
+		uint32 num_leases;
+		[size_is(num_leases)] share_mode_lease leases[];
 		uint32 num_delete_tokens;
 		[size_is(num_delete_tokens)] delete_token delete_tokens[];
 		timespec old_write_time;
diff --git a/source3/librpc/wscript_build b/source3/librpc/wscript_build
index 38d9a81..5c83cf2 100644
--- a/source3/librpc/wscript_build
+++ b/source3/librpc/wscript_build
@@ -17,7 +17,7 @@ bld.SAMBA3_SUBSYSTEM('NDR_MESSAGING',
 
 bld.SAMBA3_SUBSYSTEM('NDR_OPEN_FILES',
 	source='gen_ndr/ndr_open_files.c',
-	public_deps='ndr NDR_SERVER_ID NDR_FILE_ID NDR_SECURITY'
+	public_deps='ndr NDR_SERVER_ID NDR_FILE_ID NDR_SECURITY NDR_SMB2_LEASE_STRUCT'
 	)
 
 bld.SAMBA3_SUBSYSTEM('NDR_SMBXSRV',
diff --git a/source3/locking/locking.c b/source3/locking/locking.c
index a320068..9194dd3 100644
--- a/source3/locking/locking.c
+++ b/source3/locking/locking.c
@@ -608,6 +608,7 @@ bool is_valid_share_mode_entry(const struct share_mode_entry *e)
 	num_props += ((e->op_type == NO_OPLOCK) ? 1 : 0);
 	num_props += (EXCLUSIVE_OPLOCK_TYPE(e->op_type) ? 1 : 0);
 	num_props += (LEVEL_II_OPLOCK_TYPE(e->op_type) ? 1 : 0);
+	num_props += (e->op_type == LEASE_OPLOCK);
 
 	if ((num_props > 1) && serverid_exists(&e->pid)) {
 		smb_panic("Invalid share mode entry");
@@ -693,11 +694,21 @@ void remove_stale_share_mode_entries(struct share_mode_data *d)
 	}
 }
 
-bool set_share_mode(struct share_mode_lock *lck, files_struct *fsp,
-		    uid_t uid, uint64_t mid, uint16 op_type)
+bool set_share_mode(struct share_mode_lock *lck, struct files_struct *fsp,
+		    uid_t uid, uint64_t mid, uint16_t op_type,
+		    uint32_t lease_idx)
 {
 	struct share_mode_data *d = lck->data;
 	struct share_mode_entry *tmp, *e;
+	struct share_mode_lease *lease = NULL;
+
+	if (lease_idx == UINT32_MAX) {
+		lease = NULL;
+	} else if (lease_idx >= d->num_leases) {
+		return false;
+	} else {
+		lease = &d->leases[lease_idx];
+	}
 
 	tmp = talloc_realloc(d, d->share_modes, struct share_mode_entry,
 			     d->num_share_modes+1);
@@ -716,6 +727,8 @@ bool set_share_mode(struct share_mode_lock *lck, files_struct *fsp,
 	e->access_mask = fsp->access_mask;
 	e->op_mid = mid;
 	e->op_type = op_type;
+	e->lease_idx = lease_idx;
+	e->lease = lease;
 	e->time.tv_sec = fsp->open_time.tv_sec;
 	e->time.tv_usec = fsp->open_time.tv_usec;
 	e->id = fsp->file_id;
diff --git a/source3/locking/proto.h b/source3/locking/proto.h
index 8eccff8..6b60330 100644
--- a/source3/locking/proto.h
+++ b/source3/locking/proto.h
@@ -166,8 +166,9 @@ void get_file_infos(struct file_id id,
 		    struct timespec *write_time);
 bool is_valid_share_mode_entry(const struct share_mode_entry *e);
 bool share_mode_stale_pid(struct share_mode_data *d, uint32_t idx);
-bool set_share_mode(struct share_mode_lock *lck, files_struct *fsp,
-		    uid_t uid, uint64_t mid, uint16 op_type);
+bool set_share_mode(struct share_mode_lock *lck, struct files_struct *fsp,
+		    uid_t uid, uint64_t mid, uint16_t op_type,
+		    uint32_t lease_idx);
 void remove_stale_share_mode_entries(struct share_mode_data *d);
 bool del_share_mode(struct share_mode_lock *lck, files_struct *fsp);
 bool mark_share_mode_disconnected(struct share_mode_lock *lck,
diff --git a/source3/locking/share_mode_lock.c b/source3/locking/share_mode_lock.c
index 6447c61..2ae7c33 100644
--- a/source3/locking/share_mode_lock.c
+++ b/source3/locking/share_mode_lock.c
@@ -154,7 +154,17 @@ static struct share_mode_data *parse_share_modes(TALLOC_CTX *mem_ctx,
 	 */
 
 	for (i=0; i<d->num_share_modes; i++) {
-		d->share_modes[i].stale = false;
+		struct share_mode_entry *e = &d->share_modes[i];
+
+		e->stale = false;
+		e->lease = NULL;
+		if (e->op_type != LEASE_OPLOCK) {
+			continue;
+		}
+		if (e->lease_idx >= d->num_leases) {
+			continue;
+		}
+		e->lease = &d->leases[e->lease_idx];
 	}
 	d->modified = false;
 	d->fresh = false;
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index 1952823..c14d2eb 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1568,7 +1568,8 @@ static NTSTATUS grant_fsp_oplock_type(struct smb_request *req,
 
 	ok = set_share_mode(lck, fsp, get_current_uid(fsp->conn),
 			    req ? req->mid : 0,
-			    fsp->oplock_type);
+			    fsp->oplock_type,
+			    UINT32_MAX);
 	if (!ok) {
 		return NT_STATUS_NO_MEMORY;
 	}
@@ -3080,6 +3081,7 @@ static NTSTATUS open_directory(connection_struct *conn,
 	NTSTATUS status;
 	struct timespec mtimespec;
 	int info = 0;
+	bool ok;
 
 	if (is_ntfs_stream_smb_fname(smb_dname)) {
 		DEBUG(2, ("open_directory: %s is a stream name!\n",
@@ -3336,8 +3338,10 @@ static NTSTATUS open_directory(connection_struct *conn,
 		return status;
 	}
 
-	if (!set_share_mode(lck, fsp, get_current_uid(conn),
-			    req ? req->mid : 0, NO_OPLOCK)) {
+	ok = set_share_mode(lck, fsp, get_current_uid(conn),
+			    req ? req->mid : 0, NO_OPLOCK,
+			    UINT32_MAX);
+	if (!ok) {
 		TALLOC_FREE(lck);
 		fd_close(fsp);
 		file_free(req, fsp);
diff --git a/source3/utils/status.c b/source3/utils/status.c
index 4e1dae7..3864b7e 100644
--- a/source3/utils/status.c
+++ b/source3/utils/status.c
@@ -175,6 +175,15 @@ static int print_share_mode(const struct share_mode_entry *e,
 			d_printf("BATCH           ");
 		} else if (e->op_type & LEVEL_II_OPLOCK) {
 			d_printf("LEVEL_II        ");
+		} else if (e->op_type == LEASE_OPLOCK) {
+			uint32_t lstate = e->lease->current_state;
+			d_printf("LEASE(%s%s%s)%s%s%s",
+				 (lstate & SMB2_LEASE_READ)?"R":"",
+				 (lstate & SMB2_LEASE_WRITE)?"W":"",
+				 (lstate & SMB2_LEASE_HANDLE)?"H":"",
+				 (lstate & SMB2_LEASE_READ)?"":" ",
+				 (lstate & SMB2_LEASE_WRITE)?"":" ",
+				 (lstate & SMB2_LEASE_HANDLE)?"":" ");
 		} else {
 			d_printf("NONE            ");
 		}
-- 
1.9.1


From 7d6c93d0a07372ac14c0e34300275a2919257e15 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Fri, 10 Oct 2014 14:32:19 -0700
Subject: [PATCH 11/28] s3:locking: ensure all share mode removal functions go
 through a common lease refcount manager.

Pair-Programmed-With: Stefan Metzmacher <metze at samba.org>

Signed-off-by: Jeremy Allison <jra at samba.org>
Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source3/locking/locking.c | 91 +++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 88 insertions(+), 3 deletions(-)

diff --git a/source3/locking/locking.c b/source3/locking/locking.c
index 9194dd3..f96887e 100644
--- a/source3/locking/locking.c
+++ b/source3/locking/locking.c
@@ -46,6 +46,7 @@
 #include "messages.h"
 #include "util_tdb.h"
 #include "../librpc/gen_ndr/ndr_open_files.h"
+#include "locking/leases_db.h"
 
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_LOCKING
@@ -617,6 +618,86 @@ bool is_valid_share_mode_entry(const struct share_mode_entry *e)
 }
 
 /*
+ * See if we need to remove a lease being referred to by a
+ * share mode that is being marked stale or deleted.
+ */
+
+static void remove_share_mode_lease(struct share_mode_data *d,
+				    struct share_mode_entry *e)
+{
+	struct GUID client_guid;
+	struct smb2_lease_key lease_key;
+	uint16_t op_type;
+	uint32_t lease_idx;
+	uint32_t i;
+
+	op_type = e->op_type;
+	e->op_type = NO_OPLOCK;
+
+	d->modified = true;
+
+	if (op_type != LEASE_OPLOCK) {
+		return;
+	}
+
+	/*
+	 * This used to reference a lease. If there's no other one referencing
+	 * it, remove it.
+	 */
+
+	lease_idx = e->lease_idx;
+	e->lease_idx = UINT32_MAX;
+
+	for (i=0; i<d->num_share_modes; i++) {
+		if (d->share_modes[i].stale) {
+			continue;
+		}
+		if (e == &d->share_modes[i]) {
+			/* Not ourselves. */
+			continue;
+		}
+		if (d->share_modes[i].lease_idx == lease_idx) {
+			break;
+		}
+	}
+	if (i < d->num_share_modes) {
+		/*
+		 * Found another one
+		 */
+		return;
+	}
+
+	memcpy(&client_guid,
+		&d->leases[lease_idx].client_guid,
+		sizeof(client_guid));
+	lease_key = d->leases[lease_idx].lease_key;
+
+	d->num_leases -= 1;
+	d->leases[lease_idx] = d->leases[d->num_leases];
+
+	/*
+	 * We changed the lease array. Fix all references to it.
+	 */
+	for (i=0; i<d->num_share_modes; i++) {
+		if (d->share_modes[i].lease_idx == d->num_leases) {
+			d->share_modes[i].lease_idx = lease_idx;
+			d->share_modes[i].lease = &d->leases[lease_idx];
+		}
+	}
+
+	{
+		NTSTATUS status;
+
+		status = leases_db_del(&client_guid,
+					&lease_key,
+					&e->id);
+
+		DEBUG(10, ("%s: leases_db_del returned %s\n", __func__,
+			   nt_errstr(status)));
+	}
+}
+
+/*
  * In case d->share_modes[i] conflicts with something or otherwise is
  * being used, we need to make sure the corresponding process still
  * exists.
@@ -674,6 +755,8 @@ bool share_mode_stale_pid(struct share_mode_data *d, uint32_t idx)
 		}
 	}
 
+	remove_share_mode_lease(d, e);
+
 	d->modified = true;
 	return true;
 }
@@ -782,6 +865,7 @@ bool del_share_mode(struct share_mode_lock *lck, files_struct *fsp)
 	if (e == NULL) {
 		return False;
 	}
+	remove_share_mode_lease(lck->data, e);
 	*e = lck->data->share_modes[lck->data->num_share_modes-1];
 	lck->data->num_share_modes -= 1;
 	lck->data->modified = True;
@@ -829,6 +913,7 @@ bool mark_share_mode_disconnected(struct share_mode_lock *lck,
 
 bool remove_share_oplock(struct share_mode_lock *lck, files_struct *fsp)
 {
+	struct share_mode_data *d = lck->data;
 	struct share_mode_entry *e;
 
 	e = find_share_mode_entry(lck, fsp);
@@ -836,9 +921,9 @@ bool remove_share_oplock(struct share_mode_lock *lck, files_struct *fsp)
 		return False;
 	}
 
-	e->op_type = NO_OPLOCK;
-	lck->data->modified = True;
-	return True;
+	remove_share_mode_lease(d, e);
+	d->modified = True;
+	return true;
 }
 
 /*******************************************************************
-- 
1.9.1


From 90dfc565f0fecc377822b0f15664bcda531183de Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 27 Nov 2014 19:32:46 +0100
Subject: [PATCH 12/28] s3:locking: cleanup leases_db from
 share_mode_cleanup_disconnected()

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source3/locking/share_mode_lock.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/source3/locking/share_mode_lock.c b/source3/locking/share_mode_lock.c
index 2ae7c33..138950f 100644
--- a/source3/locking/share_mode_lock.c
+++ b/source3/locking/share_mode_lock.c
@@ -47,6 +47,7 @@
 #include "util_tdb.h"
 #include "../librpc/gen_ndr/ndr_open_files.h"
 #include "source3/lib/dbwrap/dbwrap_watch.h"
+#include "locking/leases_db.h"
 
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_LOCKING
@@ -638,6 +639,16 @@ bool share_mode_cleanup_disconnected(struct file_id fid,
 		}
 	}
 
+	for (n=0; n < data->num_leases; n++) {
+		struct share_mode_lease *l = &data->leases[n];
+		NTSTATUS status;
+
+		status = leases_db_del(&l->client_guid, &l->lease_key, &fid);
+
+		DEBUG(10, ("%s: leases_db_del returned %s\n", __func__,
+			   nt_errstr(status)));
+	}
+
 	ok = brl_cleanup_disconnected(fid, open_persistent_id);
 	if (!ok) {
 		DEBUG(10, ("share_mode_cleanup_disconnected: "
@@ -672,6 +683,7 @@ bool share_mode_cleanup_disconnected(struct file_id fid,
 		   (unsigned long long)open_persistent_id));
 
 	data->num_share_modes = 0;
+	data->num_leases = 0;
 	data->modified = true;
 
 	ret = true;
-- 
1.9.1


From 5d55de98465981539f84a3ae560c01f38a85cb04 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 28 Oct 2014 15:31:46 -0700
Subject: [PATCH 13/28] s3:locking: add downgrade_share_lease() helper function

Pair-Programmed-With: Jeremy Allison <jra at samba.org>
Pair-Programmed-With: Stefan Metzmacher <metze at samba.org>

Signed-off-by: Volker Lendecke <vl at samba.org>
Signed-off-by: Jeremy Allison <jra at samba.org>
Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source3/locking/locking.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++
 source3/locking/proto.h   |  6 ++++
 2 files changed, 86 insertions(+)

diff --git a/source3/locking/locking.c b/source3/locking/locking.c
index f96887e..d144f5c 100644
--- a/source3/locking/locking.c
+++ b/source3/locking/locking.c
@@ -944,6 +944,86 @@ bool downgrade_share_oplock(struct share_mode_lock *lck, files_struct *fsp)
 	return True;
 }
 
+NTSTATUS downgrade_share_lease(struct smbd_server_connection *sconn,
+			       struct share_mode_lock *lck,
+			       const struct smb2_lease_key *key,
+			       uint32_t new_lease_state,
+			       struct share_mode_lease **_l)
+{
+	struct share_mode_data *d = lck->data;
+	struct share_mode_lease *l;
+	uint32_t i;
+
+	*_l = NULL;
+
+	for (i=0; i<d->num_leases; i++) {
+		if (smb2_lease_equal(&sconn->client->connections->smb2.client.guid,
+				     key,
+				     &d->leases[i].client_guid,
+				     &d->leases[i].lease_key)) {
+			break;
+		}
+	}
+	if (i == d->num_leases) {
+		DEBUG(10, ("lease not found\n"));
+		return NT_STATUS_INVALID_PARAMETER;
+	}
+
+	l = &d->leases[i];
+
+	if (!l->breaking) {
+		DEBUG(1, ("Attempt to break from %d to %d - but we're not in breaking state\n",
+			   (int)l->current_state, (int)new_lease_state));
+		return NT_STATUS_UNSUCCESSFUL;
+	}
+
+	/*
+	 * Can't upgrade anything: l->breaking_to_requested (and l->current_state)
+	 * must be a strict bitwise superset of new_lease_state
+	 */
+	if ((new_lease_state & l->breaking_to_requested) != new_lease_state) {
+		DEBUG(1, ("Attempt to upgrade from %d to %d - expected %d\n",
+			   (int)l->current_state, (int)new_lease_state,
+			   (int)l->breaking_to_requested));
+		return NT_STATUS_REQUEST_NOT_ACCEPTED;
+	}
+
+	if (l->current_state != new_lease_state) {
+		l->current_state = new_lease_state;
+		d->modified = true;
+	}
+
+	if ((new_lease_state & ~l->breaking_to_required) != 0) {
+		DEBUG(5, ("lease state %d not fully broken from %d to %d\n",
+			   (int)new_lease_state,
+			   (int)l->current_state,
+			   (int)l->breaking_to_required));
+		l->breaking_to_requested = l->breaking_to_required;
+		if (l->current_state & (~SMB2_LEASE_READ)) {
+			/*
+			 * Here we break in steps, as windows does
+			 * see the breaking3 and v2_breaking3 tests.
+			 */
+			l->breaking_to_requested |= SMB2_LEASE_READ;
+		}
+		d->modified = true;
+		*_l = l;
+		return NT_STATUS_OPLOCK_BREAK_IN_PROGRESS;
+	}
+
+	DEBUG(10, ("breaking from %d to %d - expected %d\n",
+		   (int)l->current_state, (int)new_lease_state,
+		   (int)l->breaking_to_requested));
+
+	l->breaking_to_requested = 0;
+	l->breaking_to_required = 0;
+	l->breaking = false;
+
+	d->modified = true;
+
+	return NT_STATUS_OK;
+}
+
 /****************************************************************************
  Adds a delete on close token.
 ****************************************************************************/
diff --git a/source3/locking/proto.h b/source3/locking/proto.h
index 6b60330..c4ea198 100644
--- a/source3/locking/proto.h
+++ b/source3/locking/proto.h
@@ -175,6 +175,12 @@ bool mark_share_mode_disconnected(struct share_mode_lock *lck,
 				  struct files_struct *fsp);
 bool remove_share_oplock(struct share_mode_lock *lck, files_struct *fsp);
 bool downgrade_share_oplock(struct share_mode_lock *lck, files_struct *fsp);
+struct share_mode_lease;
+NTSTATUS downgrade_share_lease(struct smbd_server_connection *sconn,
+			       struct share_mode_lock *lck,
+			       const struct smb2_lease_key *key,
+			       uint32_t new_lease_state,
+			       struct share_mode_lease **_l);
 bool get_delete_on_close_token(struct share_mode_lock *lck,
 				uint32_t name_hash,
 				const struct security_token **pp_nt_tok,
-- 
1.9.1


From 316f745c8a75d5deb5f7626f7419b8e6bc070d36 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Wed, 3 Dec 2014 17:57:37 +0100
Subject: [PATCH 14/28] s3:vfs.h: add more elements to struct fsp_lease

We'll need a reference to the smbd_server_connection as well
as a timeout handler.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source3/include/vfs.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/source3/include/vfs.h b/source3/include/vfs.h
index b0f00e8..e7dc079 100644
--- a/source3/include/vfs.h
+++ b/source3/include/vfs.h
@@ -205,6 +205,8 @@ struct fd_handle {
 
 struct fsp_lease {
 	size_t ref_count;
+	struct smbd_server_connection *sconn;
+	struct tevent_timer *timeout;
 	struct smb2_lease lease;
 };
 
-- 
1.9.1


From 698a0fba7cb5d7370a0113a7478e46c2d9173112 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 28 Oct 2014 15:31:46 -0700
Subject: [PATCH 15/28] s3:smbd: add fsp_lease_type() and get_lease_type()
 helper functions

These convert the oplock state into SMB2_LEASE_ flags.

Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Jeremy Allison <jra at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 source3/smbd/files.c  |  8 ++++++++
 source3/smbd/oplock.c | 30 ++++++++++++++++++++++++++++++
 source3/smbd/proto.h  |  3 +++
 3 files changed, 41 insertions(+)

diff --git a/source3/smbd/files.c b/source3/smbd/files.c
index 7116628..d866d63 100644
--- a/source3/smbd/files.c
+++ b/source3/smbd/files.c
@@ -745,3 +745,11 @@ const struct GUID *fsp_client_guid(const files_struct *fsp)
 {
 	return &fsp->conn->sconn->client->connections->smb2.client.guid;
 }
+
+uint32_t fsp_lease_type(struct files_struct *fsp)
+{
+	if (fsp->oplock_type == LEASE_OPLOCK) {
+		return fsp->lease->lease.lease_state;
+	}
+	return map_oplock_to_lease_type(fsp->oplock_type);
+}
diff --git a/source3/smbd/oplock.c b/source3/smbd/oplock.c
index 62858c6..5a12883 100644
--- a/source3/smbd/oplock.c
+++ b/source3/smbd/oplock.c
@@ -148,6 +148,36 @@ static void downgrade_file_oplock(files_struct *fsp)
 	TALLOC_FREE(fsp->oplock_timeout);
 }
 
+uint32_t map_oplock_to_lease_type(uint16_t op_type)
+{
+	uint32_t ret;
+
+	switch(op_type) {
+	case BATCH_OPLOCK:
+	case BATCH_OPLOCK|EXCLUSIVE_OPLOCK:
+		ret = SMB2_LEASE_READ|SMB2_LEASE_WRITE|SMB2_LEASE_HANDLE;
+		break;
+	case EXCLUSIVE_OPLOCK:
+		ret = SMB2_LEASE_READ|SMB2_LEASE_WRITE;
+		break;
+	case LEVEL_II_OPLOCK:
+		ret = SMB2_LEASE_READ;
+		break;
+	default:
+		ret = SMB2_LEASE_NONE;
+		break;
+	}
+	return ret;
+}
+
+uint32_t get_lease_type(struct share_mode_data *d, struct share_mode_entry *e)
+{
+	if (e->op_type == LEASE_OPLOCK) {
+		return d->leases[e->lease_idx].current_state;
+	}
+	return map_oplock_to_lease_type(e->op_type);
+}
+
 bool update_num_read_oplocks(files_struct *fsp, struct share_mode_lock *lck)
 {
 	struct share_mode_data *d = lck->data;
diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
index 0670597..67adbba 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -388,6 +388,7 @@ NTSTATUS file_name_hash(connection_struct *conn,
 NTSTATUS fsp_set_smb_fname(struct files_struct *fsp,
 			   const struct smb_filename *smb_fname_in);
 const struct GUID *fsp_client_guid(const files_struct *fsp);
+uint32_t fsp_lease_type(struct files_struct *fsp);
 
 /* The following definitions come from smbd/ipc.c  */
 
@@ -648,6 +649,8 @@ NTSTATUS get_relative_fid_filename(connection_struct *conn,
 
 /* The following definitions come from smbd/oplock.c  */
 
+uint32_t map_oplock_to_lease_type(uint16_t op_type);
+uint32_t get_lease_type(struct share_mode_data *d, struct share_mode_entry *e);
 bool update_num_read_oplocks(files_struct *fsp, struct share_mode_lock *lck);
 
 void break_kernel_oplock(struct messaging_context *msg_ctx, files_struct *fsp);
-- 
1.9.1


From d3f1913d26cd3267696b2580aaf2ddc023e1d95c Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 28 Oct 2014 15:31:46 -0700
Subject: [PATCH 16/28] s3:smb2_create: allow durable handles with
 SMB2_LEASE_HANDLE

We don't support real lease yet, but this makes use of fsp_lease_type()
which converts a batch oplock into and RWH lease.

Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Jeremy Allison <jra at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 source3/smbd/durable.c     | 2 +-
 source3/smbd/smb2_create.c | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/source3/smbd/durable.c b/source3/smbd/durable.c
index 9489cf1..c0d1883 100644
--- a/source3/smbd/durable.c
+++ b/source3/smbd/durable.c
@@ -168,7 +168,7 @@ NTSTATUS vfs_default_durable_disconnect(struct files_struct *fsp,
 		return NT_STATUS_INVALID_PARAMETER;
 	}
 
-	if (!BATCH_OPLOCK_TYPE(fsp->oplock_type)) {
+	if ((fsp_lease_type(fsp) & SMB2_LEASE_HANDLE) == 0) {
 		return NT_STATUS_NOT_SUPPORTED;
 	}
 
diff --git a/source3/smbd/smb2_create.c b/source3/smbd/smb2_create.c
index 1e31cbf..f006a96 100644
--- a/source3/smbd/smb2_create.c
+++ b/source3/smbd/smb2_create.c
@@ -998,7 +998,7 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 		}
 
 		if (durable_requested &&
-		    BATCH_OPLOCK_TYPE(result->oplock_type))
+		    (fsp_lease_type(result) & SMB2_LEASE_HANDLE))
 		{
 			status = SMB_VFS_DURABLE_COOKIE(result,
 						op,
-- 
1.9.1


From b7ecf189ef2f3cd4bb78294b7a97eb0698dbf1a0 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 28 Oct 2014 15:31:46 -0700
Subject: [PATCH 17/28] s3:smb2_create: validate durable reconnects with leases

We don't support leases yet, but prepares for the comming commits.

Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Jeremy Allison <jra at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 source3/smbd/smb2_create.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 63 insertions(+)

diff --git a/source3/smbd/smb2_create.c b/source3/smbd/smb2_create.c
index f006a96..e96e84a 100644
--- a/source3/smbd/smb2_create.c
+++ b/source3/smbd/smb2_create.c
@@ -368,6 +368,57 @@ static void smbd_smb2_request_create_done(struct tevent_req *tsubreq)
 	}
 }
 
+static NTSTATUS smbd_smb2_create_durable_lease_check(
+	const char *requested_filename, const struct files_struct *fsp,
+	const struct smb2_lease *lease_ptr)
+{
+	struct smb_filename *smb_fname = NULL;
+	NTSTATUS status;
+
+	if (lease_ptr == NULL) {
+		if (fsp->oplock_type != LEASE_OPLOCK) {
+			return NT_STATUS_OK;
+		}
+		DEBUG(10, ("Reopened file has lease, but no lease "
+			   "requested\n"));
+		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+	}
+
+	if (fsp->oplock_type != LEASE_OPLOCK) {
+		DEBUG(10, ("Lease requested, but reopened file has no "
+			   "lease\n"));
+		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+	}
+
+	if (!smb2_lease_key_equal(&lease_ptr->lease_key,
+				  &fsp->lease->lease.lease_key)) {
+		DEBUG(10, ("Different lease key requested than found "
+			   "in reopened file\n"));
+		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+	}
+
+	status = filename_convert(talloc_tos(), fsp->conn, false,
+				  requested_filename, UCF_PREP_CREATEFILE,
+				  NULL, &smb_fname);
+	if (!NT_STATUS_IS_OK(status)) {
+		DEBUG(10, ("filename_convert returned %s\n",
+			   nt_errstr(status)));
+		return status;
+	}
+
+	if (!strequal(fsp->fsp_name->base_name, smb_fname->base_name)) {
+		DEBUG(10, ("Lease requested for file %s, reopened file "
+			   "is named %s\n", smb_fname->base_name,
+			   fsp->fsp_name->base_name));
+		TALLOC_FREE(smb_fname);
+		return NT_STATUS_INVALID_PARAMETER;
+	}
+
+	TALLOC_FREE(smb_fname);
+
+	return NT_STATUS_OK;
+}
+
 struct smbd_smb2_create_state {
 	struct smbd_smb2_request *smb2req;
 	struct smb_request *smb1req;
@@ -599,6 +650,7 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 		uint32_t durable_timeout_msec = 0;
 		bool do_durable_reconnect = false;
 		uint64_t persistent_id = 0;
+		struct smb2_lease *lease_ptr = NULL;
 
 		exta = smb2_create_blob_find(&in_context_blobs,
 					     SMB2_CREATE_TAG_EXTA);
@@ -857,6 +909,17 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 				return tevent_req_post(req, ev);
 			}
 
+			DEBUG(10, ("result->oplock_type=%u, lease_ptr==%p\n",
+				   (unsigned)result->oplock_type, lease_ptr));
+
+			status = smbd_smb2_create_durable_lease_check(
+				fname, result, lease_ptr);
+			if (!NT_STATUS_IS_OK(status)) {
+				close_file(smb1req, result, SHUTDOWN_CLOSE);
+				tevent_req_nterror(req, status);
+				return tevent_req_post(req, ev);
+			}
+
 			data_blob_free(&op->global->backend_cookie);
 			op->global->backend_cookie = new_cookie;
 
-- 
1.9.1


From 0b7489b9d304af1cbbfc3e5e6d9a059ac3cf865a Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 28 Oct 2014 15:31:46 -0700
Subject: [PATCH 18/28] s3:smbd: add file_find_one_fsp_from_lease_key() helper
 function

Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Jeremy Allison <jra at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 source3/smbd/files.c | 18 ++++++++++++++++++
 source3/smbd/proto.h |  3 +++
 2 files changed, 21 insertions(+)

diff --git a/source3/smbd/files.c b/source3/smbd/files.c
index d866d63..91eddb8 100644
--- a/source3/smbd/files.c
+++ b/source3/smbd/files.c
@@ -387,6 +387,24 @@ files_struct *file_find_di_next(files_struct *start_fsp)
 	return NULL;
 }
 
+struct files_struct *file_find_one_fsp_from_lease_key(
+	struct smbd_server_connection *sconn,
+	const struct smb2_lease_key *lease_key)
+{
+	struct files_struct *fsp;
+
+	for (fsp = sconn->files; fsp; fsp=fsp->next) {
+		if ((fsp->lease != NULL) &&
+		    (fsp->lease->lease.lease_key.data[0] ==
+		     lease_key->data[0]) &&
+		    (fsp->lease->lease.lease_key.data[1] ==
+		     lease_key->data[1])) {
+			return fsp;
+		}
+	}
+	return NULL;
+}
+
 /****************************************************************************
  Find any fsp open with a pathname below that of an already open path.
 ****************************************************************************/
diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
index 67adbba..7a4b4c9 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -369,6 +369,9 @@ files_struct *file_find_dif(struct smbd_server_connection *sconn,
 files_struct *file_find_di_first(struct smbd_server_connection *sconn,
 				 struct file_id id);
 files_struct *file_find_di_next(files_struct *start_fsp);
+struct files_struct *file_find_one_fsp_from_lease_key(
+	struct smbd_server_connection *sconn,
+	const struct smb2_lease_key *lease_key);
 bool file_find_subpath(files_struct *dir_fsp);
 void file_sync_all(connection_struct *conn);
 void fsp_free(files_struct *fsp);
-- 
1.9.1


From e817512d05e1ee030d024eb6446c6bb60a1cd498 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 28 Oct 2014 15:31:46 -0700
Subject: [PATCH 19/28] s3:smbd: add lease related helper functions to open.c

Pair-Programmed-With: Jeremy Allison <jra at samba.org>
Pair-Programmed-With: Stefan Metzmacher <metze at samba.org>

Signed-off-by: Volker Lendecke <vl at samba.org>
Signed-off-by: Jeremy Allison <jra at samba.org>
Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source3/smbd/open.c  | 208 +++++++++++++++++++++++++++++++++++++++++++++++++++
 source3/smbd/proto.h |   7 ++
 2 files changed, 215 insertions(+)

diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index c14d2eb..1055c94 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -35,6 +35,7 @@
 #include "serverid.h"
 #include "messages.h"
 #include "source3/lib/dbwrap/dbwrap_watch.h"
+#include "locking/leases_db.h"
 
 extern const struct generic_mapping file_generic_mapping;
 
@@ -1490,6 +1491,213 @@ static bool file_has_brlocks(files_struct *fsp)
 	return (brl_num_locks(br_lck) > 0);
 }
 
+int find_share_mode_lease(struct share_mode_data *d,
+			  const struct GUID *client_guid,
+			  const struct smb2_lease_key *key)
+{
+	uint32_t i;
+
+	for (i=0; i<d->num_leases; i++) {
+		struct share_mode_lease *l = &d->leases[i];
+
+		if (smb2_lease_equal(client_guid,
+				     key,
+				     &l->client_guid,
+				     &l->lease_key)) {
+			return i;
+		}
+	}
+
+	return -1;
+}
+
+struct fsp_lease *find_fsp_lease(struct files_struct *new_fsp,
+				 const struct smb2_lease_key *key,
+				 const struct share_mode_lease *l)
+{
+	struct files_struct *fsp;
+
+	/*
+	 * TODO: Measure how expensive this loop is with thousands of open
+	 * handles...
+	 */
+
+	for (fsp = file_find_di_first(new_fsp->conn->sconn, new_fsp->file_id);
+	     fsp != NULL;
+	     fsp = file_find_di_next(fsp)) {
+
+		if (fsp == new_fsp) {
+			continue;
+		}
+		if (fsp->oplock_type != LEASE_OPLOCK) {
+			continue;
+		}
+		if (smb2_lease_key_equal(&fsp->lease->lease.lease_key, key)) {
+			fsp->lease->ref_count += 1;
+			return fsp->lease;
+		}
+	}
+
+	/* Not found - must be leased in another smbd. */
+	new_fsp->lease = talloc_zero(new_fsp->conn->sconn, struct fsp_lease);
+	if (new_fsp->lease == NULL) {
+		return NULL;
+	}
+	new_fsp->lease->ref_count = 1;
+	new_fsp->lease->sconn = new_fsp->conn->sconn;
+	new_fsp->lease->lease.lease_key = *key;
+	new_fsp->lease->lease.lease_state = l->current_state;
+	/*
+	 * We internally treat all leases as V2 and update
+	 * the epoch, but when sending breaks it matters if
+	 * the requesting lease was v1 or v2.
+	 */
+	new_fsp->lease->lease.lease_version = l->lease_version;
+	new_fsp->lease->lease.lease_epoch = l->epoch;
+	return new_fsp->lease;
+}
+
+#if 0
+static NTSTATUS grant_fsp_lease(struct files_struct *fsp,
+				struct share_mode_lock *lck,
+				const struct smb2_lease *lease,
+				uint32_t *p_lease_idx,
+				uint32_t granted)
+{
+	struct share_mode_data *d = lck->data;
+	const struct GUID *client_guid = fsp_client_guid(fsp);
+	struct share_mode_lease *tmp;
+	NTSTATUS status;
+	int idx;
+
+	idx = find_share_mode_lease(d, client_guid, &lease->lease_key);
+
+	if (idx != -1) {
+		struct share_mode_lease *l = &d->leases[idx];
+		bool do_upgrade;
+		uint32_t existing, requested;
+
+		fsp->lease = find_fsp_lease(fsp, &lease->lease_key, l);
+		if (fsp->lease == NULL) {
+			DEBUG(1, ("Did not find existing lease for file %s\n",
+				  fsp_str_dbg(fsp)));
+			return NT_STATUS_NO_MEMORY;
+		}
+
+		*p_lease_idx = idx;
+
+		/*
+		 * Upgrade only if the requested lease is a strict upgrade.
+		 */
+		existing = l->current_state;
+		requested = lease->lease_state;
+
+		/*
+		 * Tricky: This test makes sure that "requested" is a
+		 * strict bitwise superset of "existing".
+		 */
+		do_upgrade = ((existing & requested) == existing);
+
+		/*
+		 * Upgrade only if there's a change.
+		 */
+		do_upgrade &= (granted != existing);
+
+		/*
+		 * Upgrade only if other leases don't prevent what was asked
+		 * for.
+		 */
+		do_upgrade &= (granted == requested);
+
+		/*
+		 * only upgrade if we are not in breaking state
+		 */
+		do_upgrade &= !l->breaking;
+
+		DEBUG(10, ("existing=%"PRIu32", requested=%"PRIu32", "
+			   "granted=%"PRIu32", do_upgrade=%d\n",
+			   existing, requested, granted, (int)do_upgrade));
+
+		if (do_upgrade) {
+			l->current_state = granted;
+			l->epoch += 1;
+		}
+
+		/* Ensure we're in sync with current lease state. */
+		fsp_lease_update(lck, fsp_client_guid(fsp), fsp->lease);
+		return NT_STATUS_OK;
+	}
+
+	/*
+	 * Create new lease
+	 */
+
+	tmp = talloc_realloc(d, d->leases, struct share_mode_lease,
+			     d->num_leases+1);
+	if (tmp == NULL) {
+		/*
+		 * See [MS-SMB2]
+		 */
+		return NT_STATUS_INSUFFICIENT_RESOURCES;
+	}
+	d->leases = tmp;
+
+	fsp->lease = talloc_zero(fsp->conn->sconn, struct fsp_lease);
+	if (fsp->lease == NULL) {
+		return NT_STATUS_INSUFFICIENT_RESOURCES;
+	}
+	fsp->lease->ref_count = 1;
+	fsp->lease->sconn = fsp->conn->sconn;
+	fsp->lease->lease.lease_version = lease->lease_version;
+	fsp->lease->lease.lease_key = lease->lease_key;
+	fsp->lease->lease.lease_state = granted;
+	fsp->lease->lease.lease_epoch = lease->lease_epoch + 1;
+
+	*p_lease_idx = d->num_leases;
+
+	d->leases[d->num_leases] = (struct share_mode_lease) {
+		.client_guid = *client_guid,
+		.lease_key = fsp->lease->lease.lease_key,
+		.current_state = fsp->lease->lease.lease_state,
+		.lease_version = fsp->lease->lease.lease_version,
+		.epoch = fsp->lease->lease.lease_epoch,
+	};
+
+	status = leases_db_add(client_guid, &lease->lease_key,
+			       &fsp->file_id, fsp->fsp_name->base_name,
+			       fsp->fsp_name->stream_name);
+	if (!NT_STATUS_IS_OK(status)) {
+		DEBUG(10, ("%s: leases_db_add failed: %s\n", __func__,
+			   nt_errstr(status)));
+		TALLOC_FREE(fsp->lease);
+		return NT_STATUS_INSUFFICIENT_RESOURCES;
+	}
+
+	d->num_leases += 1;
+	d->modified = true;
+
+	return NT_STATUS_OK;
+}
+
+static bool is_same_lease(const files_struct *fsp,
+			  const struct share_mode_data *d,
+			  const struct share_mode_entry *e,
+			  const struct smb2_lease *lease)
+{
+	if (e->op_type != LEASE_OPLOCK) {
+		return false;
+	}
+	if (lease == NULL) {
+		return false;
+	}
+
+	return smb2_lease_equal(fsp_client_guid(fsp),
+				&lease->lease_key,
+				&d->leases[e->lease_idx].client_guid,
+				&d->leases[e->lease_idx].lease_key);
+}
+#endif
+
 static NTSTATUS grant_fsp_oplock_type(struct smb_request *req,
 				      struct files_struct *fsp,
 				      struct share_mode_lock *lck,
diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
index 7a4b4c9..55fbd3b 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -626,6 +626,13 @@ void msg_file_was_renamed(struct messaging_context *msg,
 			  DATA_BLOB *data);
 NTSTATUS open_streams_for_delete(connection_struct *conn,
 				 const char *fname);
+int find_share_mode_lease(struct share_mode_data *d,
+			  const struct GUID *client_guid,
+			  const struct smb2_lease_key *key);
+struct share_mode_lease;
+struct fsp_lease *find_fsp_lease(files_struct *new_fsp,
+				 const struct smb2_lease_key *key,
+				 const struct share_mode_lease *l);
 NTSTATUS create_file_default(connection_struct *conn,
 			     struct smb_request *req,
 			     uint16_t root_dir_fid,
-- 
1.9.1


From df694f0756f8f9bbe231758ba390cb0fff96f275 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 28 Oct 2014 15:31:46 -0700
Subject: [PATCH 20/28] s3:smbd: add lease key validation functions to open.c

This makes sure a lease key can only be used for one specific
file.

This also handles the dynamic share file case [homes].

Pair-Programmed-With: Jeremy Allison <jra at samba.org>

Signed-off-by: Volker Lendecke <vl at samba.org>
Signed-off-by: Jeremy Allison <jra at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 source3/smbd/open.c | 192 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 192 insertions(+)

diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index 1055c94..28f7408 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -4034,6 +4034,179 @@ static NTSTATUS inherit_new_acl(files_struct *fsp)
 }
 
 /*
+ * If we already have a lease, it must match the new file id. [MS-SMB2]
+ * 3.3.5.9.8 speaks about INVALID_PARAMETER if an already used lease key is
+ * used for a different file name.
+ */
+
+struct lease_fname_match_state {
+	/* Input parameters. */
+	const struct smb_filename *fname;
+	bool file_existed;
+	struct file_id id;
+	/* Return parameters. */
+	uint32_t num_file_ids;
+	struct file_id *ids;
+	NTSTATUS match_status;
+};
+
+static void lease_fname_match_parser(
+	uint32_t num_file_ids,
+	struct file_id *ids, const char *filename, const char *stream_name,
+	void *private_data)
+{
+	struct lease_fname_match_state *state =
+		(struct lease_fname_match_state *)private_data;
+
+	if (!strequal(filename, state->fname->base_name) ||
+	    !strequal(stream_name, state->fname->stream_name))
+	{
+		/* Names don't match lease key. */
+		state->match_status = NT_STATUS_INVALID_PARAMETER;
+		return;
+	}
+
+	if (state->file_existed &&
+	    num_file_ids == 1 &&
+	    file_id_equal(&ids[0],&state->id))
+	{
+		/* Common case - non-dynamic share. We're ok.. */
+		state->match_status = NT_STATUS_OK;
+		return;
+	}
+
+	/*
+	 * More than one file id, or not equal, or new file
+	 * being created and there's already an existing lease
+	 * on this (client_guid, lease id) pair.
+	 * Don't allow leases.
+	 */
+
+	state->match_status = NT_STATUS_OPLOCK_NOT_GRANTED;
+	state->num_file_ids = num_file_ids;
+	state->ids = talloc_memdup(talloc_tos(),
+				ids,
+				num_file_ids * sizeof(struct file_id));
+	if (state->ids == NULL) {
+		state->match_status = NT_STATUS_NO_MEMORY;
+	}
+}
+
+static NTSTATUS lease_match(connection_struct *conn,
+			    struct smb_request *req,
+			    struct smb2_lease_key *lease_key,
+			    const struct smb_filename *fname,
+			    uint16_t *p_version,
+			    uint16_t *p_epoch)
+{
+	struct smbd_server_connection *sconn = req->sconn;
+	struct lease_fname_match_state state = {
+		.fname = fname,
+		.match_status = NT_STATUS_OK
+	};
+	uint32_t i;
+	NTSTATUS status;
+
+	state.file_existed = VALID_STAT(fname->st);
+	if (state.file_existed) {
+		state.id = vfs_file_id_from_sbuf(conn, &fname->st);
+	} else {
+		memset(&state.id, '\0', sizeof(state.id));
+	}
+
+	status = leases_db_parse(&sconn->client->connections->smb2.client.guid,
+				 lease_key, lease_fname_match_parser, &state);
+	if (!NT_STATUS_IS_OK(status)) {
+		/*
+		 * Not found or error means okay: We can make the lease pass
+		 */
+		return NT_STATUS_OK;
+	}
+	if (!NT_STATUS_EQUAL(state.match_status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
+		/*
+		 * Anything but NT_STATUS_OPLOCK_NOT_GRANTED, let the caller
+		 * deal with it.
+		 */
+		return state.match_status;
+	}
+
+	/* We have to break all existing leases. */
+	for (i = 0; i < state.num_file_ids; i++) {
+		struct share_mode_lock *lck;
+		struct share_mode_data *d;
+		uint32_t j;
+
+		if (file_id_equal(&state.ids[i], &state.id)) {
+			/* Don't need to break our own file. */
+			continue;
+		}
+
+		lck = get_existing_share_mode_lock(talloc_tos(), state.ids[i]);
+		if (lck == NULL) {
+			/* Race condition - file already closed. */
+			continue;
+		}
+		d = lck->data;
+		for (j=0; j<d->num_share_modes; j++) {
+			struct share_mode_entry *e = &d->share_modes[j];
+			uint32_t e_lease_type = get_lease_type(d, e);
+			struct share_mode_lease *l = NULL;
+
+			if (share_mode_stale_pid(d, j)) {
+				continue;
+			}
+
+			if (e->op_type == LEASE_OPLOCK) {
+				l = &lck->data->leases[e->lease_idx];
+				if (!smb2_lease_key_equal(&l->lease_key,
+							  lease_key)) {
+					continue;
+				}
+				*p_epoch = l->epoch;
+				*p_version = l->lease_version;
+			}
+
+			if (e_lease_type == SMB2_LEASE_NONE) {
+				continue;
+			}
+
+			send_break_message(conn->sconn->msg_ctx, e,
+					   SMB2_LEASE_NONE);
+
+			/*
+			 * Windows 7 and 8 lease clients
+			 * are broken in that they will not
+			 * respond to lease break requests
+			 * whilst waiting for an outstanding
+			 * open request on that lease handle
+			 * on the same TCP connection, due
+			 * to holding an internal inode lock.
+			 *
+			 * This means we can't reschedule
+			 * ourselves here, but must return
+			 * from the create.
+			 *
+			 * Work around:
+			 *
+			 * Send the breaks and then return
+			 * SMB2_LEASE_NONE in the lease handle
+			 * to cause them to acknowledge the
+			 * lease break. Consulatation with
+			 * Microsoft engineering confirmed
+			 * this approach is safe.
+			 */
+
+		}
+		TALLOC_FREE(lck);
+	}
+	/*
+	 * Ensure we don't grant anything more so we
+	 * never upgrade.
+	 */
+	return NT_STATUS_OPLOCK_NOT_GRANTED;
+}
+
+/*
  * Wrapper around open_file_ntcreate and open_directory
  */
 
@@ -4089,6 +4262,25 @@ static NTSTATUS create_file_unixpath(connection_struct *conn,
 		oplock_request |= INTERNAL_OPEN_ONLY;
 	}
 
+	if (lease != NULL) {
+		uint16_t epoch = lease->lease_epoch;
+		uint16_t version = lease->lease_version;
+		status = lease_match(conn,
+				req,
+				&lease->lease_key,
+				smb_fname,
+				&version,
+				&epoch);
+		if (NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
+			/* Dynamic share file. No leases and update epoch... */
+			lease->lease_state = SMB2_LEASE_NONE;
+			lease->lease_epoch = epoch;
+			lease->lease_version = version;
+		} else if (!NT_STATUS_IS_OK(status)) {
+			goto fail;
+		}
+	}
+
 	if ((conn->fs_capabilities & FILE_NAMED_STREAMS)
 	    && (access_mask & DELETE_ACCESS)
 	    && !is_ntfs_stream_smb_fname(smb_fname)) {
-- 
1.9.1


From 8dee5c41b9dcbfe7587d391a8dbf4afe8c31c952 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 28 Oct 2014 15:31:46 -0700
Subject: [PATCH 21/28] s3:smbd: add lease related helper functions to oplock.c

Pair-Programmed-With: Jeremy Allison <jra at samba.org>
Pair-Programmed-With: Stefan Metzmacher <metze at samba.org>

Signed-off-by: Volker Lendecke <vl at samba.org>
Signed-off-by: Jeremy Allison <jra at samba.org>
Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source3/smbd/oplock.c | 295 ++++++++++++++++++++++++++++++++++++++++++++++++++
 source3/smbd/proto.h  |   8 ++
 2 files changed, 303 insertions(+)

diff --git a/source3/smbd/oplock.c b/source3/smbd/oplock.c
index 5a12883..5ad881d 100644
--- a/source3/smbd/oplock.c
+++ b/source3/smbd/oplock.c
@@ -306,6 +306,301 @@ bool downgrade_oplock(files_struct *fsp)
 	return ret;
 }
 
+static void lease_timeout_handler(struct tevent_context *ctx,
+				  struct tevent_timer *te,
+				  struct timeval now,
+				  void *private_data)
+{
+	struct fsp_lease *lease =
+		talloc_get_type_abort(private_data,
+		struct fsp_lease);
+	struct files_struct *fsp;
+	struct share_mode_lock *lck;
+	uint16_t old_epoch = lease->lease.lease_epoch;
+
+	fsp = file_find_one_fsp_from_lease_key(lease->sconn,
+					       &lease->lease.lease_key);
+	if (fsp == NULL) {
+		/* race? */
+		TALLOC_FREE(lease->timeout);
+		return;
+	}
+
+	lck = get_existing_share_mode_lock(
+			talloc_tos(), fsp->file_id);
+	if (lck == NULL) {
+		/* race? */
+		TALLOC_FREE(lease->timeout);
+		return;
+	}
+
+	fsp_lease_update(lck, fsp_client_guid(fsp), lease);
+
+	if (lease->lease.lease_epoch != old_epoch) {
+		/*
+		 * If the epoch changed we need to wait for
+		 * the next timeout to happen.
+		 */
+		DEBUG(10, ("lease break timeout race (epoch) for file %s - ignoring\n",
+			   fsp_str_dbg(fsp)));
+		TALLOC_FREE(lck);
+		return;
+	}
+
+	if (!(lease->lease.lease_flags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS)) {
+		/*
+		 * If the epoch changed we need to wait for
+		 * the next timeout to happen.
+		 */
+		DEBUG(10, ("lease break timeout race (flags) for file %s - ignoring\n",
+			   fsp_str_dbg(fsp)));
+		TALLOC_FREE(lck);
+		return;
+	}
+
+	DEBUG(1, ("lease break timed out for file %s -- replying anyway\n",
+		  fsp_str_dbg(fsp)));
+	(void)downgrade_lease(lease->sconn->client->connections,
+			1,
+			&fsp->file_id,
+			&lease->lease.lease_key,
+			SMB2_LEASE_NONE);
+
+	TALLOC_FREE(lck);
+}
+
+bool fsp_lease_update(struct share_mode_lock *lck,
+		      const struct GUID *client_guid,
+		      struct fsp_lease *lease)
+{
+	struct share_mode_data *d = lck->data;
+	int idx;
+	struct share_mode_lease *l = NULL;
+
+	idx = find_share_mode_lease(d, client_guid, &lease->lease.lease_key);
+	if (idx != -1) {
+		l = &d->leases[idx];
+	}
+
+	if (l == NULL) {
+		DEBUG(1, ("%s: Could not find lease entry\n", __func__));
+		TALLOC_FREE(lease->timeout);
+		lease->lease.lease_state = SMB2_LEASE_NONE;
+		lease->lease.lease_epoch += 1;
+		lease->lease.lease_flags = 0;
+		return false;
+	}
+
+	DEBUG(10,("%s: refresh lease state\n", __func__));
+
+	/* Ensure we're in sync with current lease state. */
+	if (lease->lease.lease_epoch != l->epoch) {
+		DEBUG(10,("%s: cancel outdated timeout\n", __func__));
+		TALLOC_FREE(lease->timeout);
+	}
+	lease->lease.lease_epoch = l->epoch;
+	lease->lease.lease_state = l->current_state;
+
+	if (l->breaking) {
+		lease->lease.lease_flags |= SMB2_LEASE_FLAG_BREAK_IN_PROGRESS;
+
+		if (lease->timeout == NULL) {
+			struct timeval t = timeval_current_ofs(OPLOCK_BREAK_TIMEOUT, 0);
+
+			DEBUG(10,("%s: setup timeout handler\n", __func__));
+
+			lease->timeout = tevent_add_timer(lease->sconn->ev_ctx,
+							  lease, t,
+							  lease_timeout_handler,
+							  lease);
+			if (lease->timeout == NULL) {
+				DEBUG(0, ("%s: Could not add lease timeout handler\n",
+					  __func__));
+			}
+		}
+	} else {
+		lease->lease.lease_flags &= ~SMB2_LEASE_FLAG_BREAK_IN_PROGRESS;
+		TALLOC_FREE(lease->timeout);
+	}
+
+	return true;
+}
+
+struct downgrade_lease_additional_state {
+	struct tevent_immediate *im;
+	struct smbXsrv_connection *xconn;
+	uint32_t break_flags;
+	struct smb2_lease_key lease_key;
+	uint32_t break_from;
+	uint32_t break_to;
+	uint16_t new_epoch;
+};
+
+static void downgrade_lease_additional_trigger(struct tevent_context *ev,
+					       struct tevent_immediate *im,
+					       void *private_data)
+{
+	struct downgrade_lease_additional_state *state =
+		talloc_get_type_abort(private_data,
+		struct downgrade_lease_additional_state);
+	struct smbXsrv_connection *xconn = state->xconn;
+	NTSTATUS status;
+
+	status = smbd_smb2_send_lease_break(xconn,
+					    state->new_epoch,
+					    state->break_flags,
+					    &state->lease_key,
+					    state->break_from,
+					    state->break_to);
+	TALLOC_FREE(state);
+	if (!NT_STATUS_IS_OK(status)) {
+		smbd_server_connection_terminate(xconn,
+						 nt_errstr(status));
+		return;
+	}
+}
+
+struct downgrade_lease_fsps_state {
+	struct file_id id;
+	struct share_mode_lock *lck;
+	const struct smb2_lease_key *key;
+};
+
+static struct files_struct *downgrade_lease_fsps(struct files_struct *fsp,
+						 void *private_data)
+{
+	struct downgrade_lease_fsps_state *state =
+		(struct downgrade_lease_fsps_state *)private_data;
+
+	if (fsp->oplock_type != LEASE_OPLOCK) {
+		return NULL;
+	}
+	if (!smb2_lease_key_equal(&fsp->lease->lease.lease_key, state->key)) {
+		return NULL;
+	}
+	if (!file_id_equal(&fsp->file_id, &state->id)) {
+		return NULL;
+	}
+
+	fsp_lease_update(state->lck, fsp_client_guid(fsp), fsp->lease);
+
+	return NULL;
+}
+
+NTSTATUS downgrade_lease(struct smbXsrv_connection *xconn,
+			 uint32_t num_file_ids,
+			 const struct file_id *ids,
+			 const struct smb2_lease_key *key,
+			 uint32_t lease_state)
+{
+	struct smbd_server_connection *sconn = xconn->client->sconn;
+	struct share_mode_lock *lck;
+	struct share_mode_lease *l = NULL;
+	const struct file_id id = ids[0];
+	uint32_t i;
+	NTSTATUS status;
+
+	DEBUG(10, ("%s: Downgrading %s to %x\n", __func__,
+		   file_id_string_tos(&id), (unsigned)lease_state));
+
+	lck = get_existing_share_mode_lock(talloc_tos(), id);
+	if (lck == NULL) {
+		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+	}
+	status = downgrade_share_lease(sconn, lck, key, lease_state, &l);
+
+	DEBUG(10, ("%s: Downgrading %s to %x => %s\n", __func__,
+		   file_id_string_tos(&id), (unsigned)lease_state, nt_errstr(status)));
+
+	if (NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_BREAK_IN_PROGRESS)) {
+		struct downgrade_lease_additional_state *state;
+
+		state = talloc_zero(xconn,
+				    struct downgrade_lease_additional_state);
+		if (state == NULL) {
+			TALLOC_FREE(lck);
+			return NT_STATUS_NO_MEMORY;
+		}
+
+		state->im = tevent_create_immediate(state);
+		if (state->im == NULL) {
+			TALLOC_FREE(state);
+			TALLOC_FREE(lck);
+			return NT_STATUS_NO_MEMORY;
+		}
+
+		state->xconn = xconn;
+		if (l->current_state & (~SMB2_LEASE_READ)) {
+			state->break_flags = SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED;
+		}
+		state->lease_key = l->lease_key;
+		state->break_from = l->current_state;
+		state->break_to = l->breaking_to_requested;
+		if (l->lease_version > 1) {
+			state->new_epoch = l->epoch;
+		}
+
+		if (state->break_flags == 0) {
+			/*
+			 * This is an async break without
+			 * SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED
+			 *
+			 * we need to store NONE state in the
+			 * database.
+			 */
+			l->current_state = 0;
+			l->breaking_to_requested = 0;
+			l->breaking_to_required = 0;
+			l->breaking = false;
+
+			lck->data->modified = true;
+		}
+
+		tevent_schedule_immediate(state->im, xconn->ev_ctx,
+					  downgrade_lease_additional_trigger,
+					  state);
+	}
+
+	{
+		struct downgrade_lease_fsps_state state = {
+			.id = id, .lck = lck, .key = key,
+		};
+
+		files_forall(sconn, downgrade_lease_fsps, &state);
+	}
+
+	TALLOC_FREE(lck);
+	DEBUG(10, ("%s: Downgrading %s to %x => %s\n", __func__,
+		   file_id_string_tos(&id), (unsigned)lease_state, nt_errstr(status)));
+
+	/*
+	 * Dynamic share case. Ensure other opens are copies.
+	 * This will only be breaking to NONE.
+	 */
+
+	for (i = 1; i < num_file_ids; i++) {
+		lck = get_existing_share_mode_lock(talloc_tos(), ids[i]);
+		if (lck == NULL) {
+			return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+		}
+
+		{
+			struct downgrade_lease_fsps_state state = {
+				.id = ids[i], .lck = lck, .key = key,
+			};
+
+			files_forall(sconn, downgrade_lease_fsps, &state);
+		}
+
+		DEBUG(10, ("%s: Downgrading %s to %x => %s\n", __func__,
+			file_id_string_tos(&ids[i]), (unsigned)lease_state, nt_errstr(status)));
+
+		TALLOC_FREE(lck);
+	}
+
+	return status;
+}
+
 /****************************************************************************
  Set up an oplock break message.
 ****************************************************************************/
diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
index 55fbd3b..702da2e 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -667,6 +667,14 @@ void break_kernel_oplock(struct messaging_context *msg_ctx, files_struct *fsp);
 NTSTATUS set_file_oplock(files_struct *fsp);
 bool remove_oplock(files_struct *fsp);
 bool downgrade_oplock(files_struct *fsp);
+bool fsp_lease_update(struct share_mode_lock *lck,
+		      const struct GUID *client_guid,
+		      struct fsp_lease *lease);
+NTSTATUS downgrade_lease(struct smbXsrv_connection *xconn,
+			uint32_t num_file_ids,
+			const struct file_id *ids,
+			const struct smb2_lease_key *key,
+			uint32_t lease_state);
 void contend_level2_oplocks_begin(files_struct *fsp,
 				  enum level2_contention_type type);
 void contend_level2_oplocks_end(files_struct *fsp,
-- 
1.9.1


From 1693f6e6d73f7b9142ed3d1d5ad0a39cdc7acae0 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 28 Oct 2014 15:31:46 -0700
Subject: [PATCH 22/28] s3:smbd: Implementation of SMB2.1 and SMB3.0 leases.

Pair-Programmed-With: Jeremy Allison <jra at samba.org>
Pair-Programmed-With: Stefan Metzmacher <metze at samba.org>

Signed-off-by: Volker Lendecke <vl at samba.org>
Signed-off-by: Jeremy Allison <jra at samba.org>
Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source3/smbd/durable.c    |  26 ++++
 source3/smbd/files.c      |   8 ++
 source3/smbd/globals.h    |   4 +-
 source3/smbd/open.c       | 353 ++++++++++++++++++++++++++++------------------
 source3/smbd/oplock.c     | 217 ++++++++++++++++++++++++----
 source3/smbd/smb2_break.c | 251 ++++++++++++++++++++++++++++++--
 6 files changed, 681 insertions(+), 178 deletions(-)

diff --git a/source3/smbd/durable.c b/source3/smbd/durable.c
index c0d1883..d9b88a8 100644
--- a/source3/smbd/durable.c
+++ b/source3/smbd/durable.c
@@ -724,6 +724,32 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
 	fsp->aio_write_behind = false;
 	fsp->oplock_type = e->op_type;
 
+	if (fsp->oplock_type == LEASE_OPLOCK) {
+		struct share_mode_lease *l = &lck->data->leases[e->lease_idx];
+		struct smb2_lease_key key;
+
+		key.data[0] = l->lease_key.data[0];
+		key.data[1] = l->lease_key.data[1];
+
+		fsp->lease = find_fsp_lease(fsp, &key, l);
+		if (fsp->lease == NULL) {
+			TALLOC_FREE(lck);
+			fsp_free(fsp);
+			return NT_STATUS_NO_MEMORY;
+		}
+
+		/*
+		 * Ensure the existing client guid matches the
+		 * stored one in the share_mode_lease.
+		 */
+		if (!GUID_equal(fsp_client_guid(fsp),
+				&l->client_guid)) {
+			TALLOC_FREE(lck);
+			fsp_free(fsp);
+			return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+		}
+	}
+
 	fsp->initial_allocation_size = cookie.initial_allocation_size;
 	fsp->fh->position_information = cookie.position_information;
 	fsp->update_write_time_triggered = cookie.update_write_time_triggered;
diff --git a/source3/smbd/files.c b/source3/smbd/files.c
index 91eddb8..1ad601a 100644
--- a/source3/smbd/files.c
+++ b/source3/smbd/files.c
@@ -490,6 +490,14 @@ void fsp_free(files_struct *fsp)
 		fsp->fh->ref_count--;
 	}
 
+	if (fsp->lease != NULL) {
+		if (fsp->lease->ref_count == 1) {
+			TALLOC_FREE(fsp->lease);
+		} else {
+			fsp->lease->ref_count--;
+		}
+	}
+
 	fsp->conn->num_files_open--;
 
 	/* this is paranoia, just in case someone tries to reuse the
diff --git a/source3/smbd/globals.h b/source3/smbd/globals.h
index d85e686..7726c24 100644
--- a/source3/smbd/globals.h
+++ b/source3/smbd/globals.h
@@ -305,7 +305,9 @@ void smbd_smb2_request_dispatch_immediate(struct tevent_context *ctx,
 struct deferred_open_record;
 
 /* SMB1 -> SMB2 glue. */
-void send_break_message_smb2(files_struct *fsp, int level);
+void send_break_message_smb2(files_struct *fsp,
+			     uint32_t break_from,
+			     uint32_t break_to);
 struct blocking_lock_record *get_pending_smb2req_blr(struct smbd_smb2_request *smb2req);
 bool push_blocking_lock_request_smb2( struct byte_range_lock *br_lck,
 				struct smb_request *req,
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index 28f7408..f5ad900 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1258,6 +1258,10 @@ static NTSTATUS send_break_message(struct messaging_context *msg_ctx,
 	share_mode_entry_to_message(msg, exclusive);
 
 	/* Overload entry->op_type */
+	/*
+	 * This is a cut from uint32 to uint16, but so far only the lower 3
+	 * bits (LEASE_WRITE/HANDLE/READ are used anyway.
+	 */
 	SSVAL(msg,OP_BREAK_MSG_OP_TYPE_OFFSET, break_to);
 
 	status = messaging_send_buf(msg_ctx, exclusive->pid,
@@ -1377,56 +1381,19 @@ static bool validate_oplock_types(struct share_mode_lock *lck)
 
 static bool delay_for_oplock(files_struct *fsp,
 			     int oplock_request,
+			     const struct smb2_lease *lease,
 			     struct share_mode_lock *lck,
 			     bool have_sharing_violation,
-			     uint32_t create_disposition)
+			     uint32_t create_disposition,
+			     bool first_open_attempt)
 {
 	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;
-	}
-	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 open around. There can't be any exclusive or
-		 * batch left, this is all level2.
-		 */
-		return false;
-	}
+	bool delay = false;
+	bool will_overwrite;
 
-	if (server_id_is_disconnected(&entry->pid)) {
-		/*
-		 * TODO: clean up.
-		 * This could be achieved by sending a break message
-		 * to ourselves. Special considerations for files
-		 * with delete_on_close flag set!
-		 *
-		 * For now we keep it simple and do not
-		 * allow delete on close for durable handles.
-		 */
+	if ((oplock_request & INTERNAL_OPEN_ONLY) ||
+	    is_stat_open(fsp->access_mask)) {
 		return false;
 	}
 
@@ -1434,50 +1401,98 @@ static bool delay_for_oplock(files_struct *fsp,
 	case FILE_SUPERSEDE:
 	case FILE_OVERWRITE:
 	case FILE_OVERWRITE_IF:
-		break_to = NO_OPLOCK;
+		will_overwrite = true;
 		break;
 	default:
-		break_to = LEVEL_II_OPLOCK;
+		will_overwrite = false;
 		break;
 	}
 
-	if (have_sharing_violation && (entry->op_type & BATCH_OPLOCK)) {
-		if (share_mode_stale_pid(d, 0)) {
-			return false;
+	for (i=0; i<d->num_share_modes; i++) {
+		struct share_mode_entry *e = &d->share_modes[i];
+		struct share_mode_lease *l = NULL;
+		uint32_t e_lease_type = get_lease_type(d, e);
+		uint32_t break_to;
+		uint32_t delay_mask = 0;
+
+		if (e->op_type == LEASE_OPLOCK) {
+			l = &d->leases[e->lease_idx];
 		}
-		send_break_message(fsp->conn->sconn->msg_ctx, entry, break_to);
-		return true;
-	}
-	if (have_sharing_violation) {
-		/*
-		 * Non-batch exclusive is not broken if we have a sharing
-		 * violation
-		 */
-		return false;
-	}
-	if (LEVEL_II_OPLOCK_TYPE(entry->op_type) &&
-	    (break_to == NO_OPLOCK)) {
-		if (share_mode_stale_pid(d, 0)) {
-			return false;
+
+		if (have_sharing_violation) {
+			delay_mask = SMB2_LEASE_HANDLE;
+		} else {
+			delay_mask = SMB2_LEASE_WRITE;
 		}
-		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
-		 */
-		return false;
-	}
-	if (share_mode_stale_pid(d, 0)) {
-		return false;
+
+		break_to = e_lease_type & ~delay_mask;
+
+		if (will_overwrite) {
+			/*
+			 * we'll decide about SMB2_LEASE_READ later.
+			 *
+			 * Maybe the break will be defered
+			 */
+			break_to &= ~SMB2_LEASE_HANDLE;
+		}
+
+		DEBUG(10, ("entry %u: e_lease_type %u, will_overwrite: %u\n",
+			   (unsigned)i, (unsigned)e_lease_type,
+			   (unsigned)will_overwrite));
+
+		if (lease != NULL && l != NULL) {
+			bool ign;
+
+			ign = smb2_lease_equal(fsp_client_guid(fsp),
+					       &lease->lease_key,
+					       &l->client_guid,
+					       &l->lease_key);
+			if (ign) {
+				continue;
+			}
+		}
+
+		if ((e_lease_type & ~break_to) == 0) {
+			if (l != NULL && l->breaking) {
+				delay = true;
+			}
+			continue;
+		}
+
+		if (share_mode_stale_pid(d, i)) {
+			continue;
+		}
+
+		if (will_overwrite) {
+			/*
+			 * If we break anyway break to NONE directly.
+			 * Otherwise vfs_set_filelen() will trigger the
+			 * break.
+			 */
+			break_to &= ~(SMB2_LEASE_READ|SMB2_LEASE_WRITE);
+		}
+
+		if (e->op_type != LEASE_OPLOCK) {
+			/*
+			 * Oplocks only support breaking to R or NONE.
+			 */
+			break_to &= ~(SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
+		}
+
+		DEBUG(10, ("breaking from %d to %d\n",
+			   (int)e_lease_type, (int)break_to));
+		send_break_message(fsp->conn->sconn->msg_ctx, e,
+				   break_to);
+		if (e_lease_type & delay_mask) {
+			delay = true;
+		}
+		if (l != NULL && l->breaking && !first_open_attempt) {
+			delay = true;
+		}
+		continue;
 	}
 
-	send_break_message(fsp->conn->sconn->msg_ctx, entry, break_to);
-	return true;
+	return delay;
 }
 
 static bool file_has_brlocks(files_struct *fsp)
@@ -1557,7 +1572,6 @@ struct fsp_lease *find_fsp_lease(struct files_struct *new_fsp,
 	return new_fsp->lease;
 }
 
-#if 0
 static NTSTATUS grant_fsp_lease(struct files_struct *fsp,
 				struct share_mode_lock *lck,
 				const struct smb2_lease *lease,
@@ -1696,88 +1710,151 @@ static bool is_same_lease(const files_struct *fsp,
 				&d->leases[e->lease_idx].client_guid,
 				&d->leases[e->lease_idx].lease_key);
 }
-#endif
 
 static NTSTATUS grant_fsp_oplock_type(struct smb_request *req,
 				      struct files_struct *fsp,
 				      struct share_mode_lock *lck,
-				      int oplock_request)
+				      int oplock_request,
+				      struct smb2_lease *lease)
 {
-	bool allow_level2 = (global_client_caps & CAP_LEVEL_II_OPLOCKS) &&
-		            lp_level2_oplocks(SNUM(fsp->conn));
-	bool got_level2_oplock, got_a_none_oplock;
+	struct share_mode_data *d = lck->data;
+	bool got_handle_lease = false;
+	bool got_oplock = false;
 	uint32_t i;
+	uint32_t granted;
+	uint32_t lease_idx = UINT32_MAX;
 	bool ok;
 	NTSTATUS status;
 
-	/* Start by granting what the client asked for,
-	   but ensure no SAMBA_PRIVATE bits can be set. */
-	fsp->oplock_type = (oplock_request & ~SAMBA_PRIVATE_OPLOCK_MASK);
-
-	if (fsp->oplock_type == NO_OPLOCK) {
-		goto type_selected;
-	}
-
 	if (oplock_request & INTERNAL_OPEN_ONLY) {
 		/* No oplocks on internal open. */
-		fsp->oplock_type = NO_OPLOCK;
-		goto type_selected;
+		oplock_request = NO_OPLOCK;
+		DEBUG(10,("grant_fsp_oplock_type: oplock type 0x%x on file %s\n",
+			fsp->oplock_type, fsp_str_dbg(fsp)));
+	}
+
+	if (oplock_request == LEASE_OPLOCK) {
+		if (lease == NULL) {
+			/*
+			 * The SMB2 layer should have checked this
+			 */
+			return NT_STATUS_INTERNAL_ERROR;
+		}
+
+		granted = lease->lease_state;
+
+		if (lp_kernel_oplocks(SNUM(fsp->conn))) {
+			DEBUG(10, ("No lease granted because kernel oplocks are enabled\n"));
+			granted = SMB2_LEASE_NONE;
+		}
+		if ((granted & (SMB2_LEASE_READ|SMB2_LEASE_WRITE)) == 0) {
+			DEBUG(10, ("No read or write lease requested\n"));
+			granted = SMB2_LEASE_NONE;
+		}
+		if (granted == SMB2_LEASE_WRITE) {
+			DEBUG(10, ("pure write lease requested\n"));
+			granted = SMB2_LEASE_NONE;
+		}
+		if (granted == (SMB2_LEASE_WRITE|SMB2_LEASE_HANDLE)) {
+			DEBUG(10, ("write and handle lease requested\n"));
+			granted = SMB2_LEASE_NONE;
+		}
+	} else {
+		granted = map_oplock_to_lease_type(
+			oplock_request & ~SAMBA_PRIVATE_OPLOCK_MASK);
 	}
 
 	if (lp_locking(fsp->conn->params) && file_has_brlocks(fsp)) {
 		DEBUG(10,("grant_fsp_oplock_type: file %s has byte range locks\n",
 			fsp_str_dbg(fsp)));
-		fsp->oplock_type = NO_OPLOCK;
-		goto type_selected;
+		granted &= ~SMB2_LEASE_READ;
 	}
 
-	got_level2_oplock = false;
-	got_a_none_oplock = false;
+	for (i=0; i<d->num_share_modes; i++) {
+		struct share_mode_entry *e = &d->share_modes[i];
+		uint32_t e_lease_type;
 
-	for (i=0; i<lck->data->num_share_modes; i++) {
-		int op_type = lck->data->share_modes[i].op_type;
+		e_lease_type = get_lease_type(d, e);
 
-		if (LEVEL_II_OPLOCK_TYPE(op_type)) {
-			got_level2_oplock = true;
+		if ((granted & SMB2_LEASE_WRITE) &&
+		    !is_same_lease(fsp, d, e, lease) &&
+		    !share_mode_stale_pid(d, i)) {
+			/*
+			 * Can grant only one writer
+			 */
+			granted &= ~SMB2_LEASE_WRITE;
 		}
-		if (op_type == NO_OPLOCK) {
-			got_a_none_oplock = true;
+
+		if ((e_lease_type & SMB2_LEASE_HANDLE) && !got_handle_lease &&
+		    !share_mode_stale_pid(d, i)) {
+			got_handle_lease = true;
+		}
+
+		if ((e->op_type != LEASE_OPLOCK) && !got_oplock &&
+		    !share_mode_stale_pid(d, i)) {
+			got_oplock = true;
 		}
 	}
 
-	/*
-	 * Match what was requested (fsp->oplock_type) with
- 	 * what was found in the existing share modes.
- 	 */
+	if ((granted & SMB2_LEASE_READ) && !(granted & SMB2_LEASE_WRITE)) {
+		bool allow_level2 =
+			(global_client_caps & CAP_LEVEL_II_OPLOCKS) &&
+			lp_level2_oplocks(SNUM(fsp->conn));
 
-	if (got_level2_oplock || got_a_none_oplock) {
-		if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) {
-			fsp->oplock_type = LEVEL_II_OPLOCK;
+		if (!allow_level2) {
+			granted = SMB2_LEASE_NONE;
 		}
 	}
 
-	/*
-	 * Don't grant level2 to clients that don't want them
-	 * or if we've turned them off.
-	 */
-	if (fsp->oplock_type == LEVEL_II_OPLOCK && !allow_level2) {
-		fsp->oplock_type = NO_OPLOCK;
-		goto type_selected;
-	}
+	if (oplock_request == LEASE_OPLOCK) {
+		if (got_oplock) {
+			granted &= ~SMB2_LEASE_HANDLE;
+		}
 
- type_selected:
-	status = set_file_oplock(fsp);
-	if (!NT_STATUS_IS_OK(status)) {
-		/*
-		 * Could not get the kernel oplock
-		 */
-		fsp->oplock_type = NO_OPLOCK;
+		fsp->oplock_type = LEASE_OPLOCK;
+
+		status = grant_fsp_lease(fsp, lck, lease, &lease_idx,
+					 granted);
+		if (!NT_STATUS_IS_OK(status)) {
+			return status;
+
+		}
+		*lease = fsp->lease->lease;
+		DEBUG(10, ("lease_state=%d\n", lease->lease_state));
+	} else {
+		if (got_handle_lease) {
+			granted = SMB2_LEASE_NONE;
+		}
+
+		switch (granted) {
+		case SMB2_LEASE_READ|SMB2_LEASE_WRITE|SMB2_LEASE_HANDLE:
+			fsp->oplock_type = BATCH_OPLOCK|EXCLUSIVE_OPLOCK;
+			break;
+		case SMB2_LEASE_READ|SMB2_LEASE_WRITE:
+			fsp->oplock_type = EXCLUSIVE_OPLOCK;
+			break;
+		case SMB2_LEASE_READ|SMB2_LEASE_HANDLE:
+		case SMB2_LEASE_READ:
+			fsp->oplock_type = LEVEL_II_OPLOCK;
+			break;
+		default:
+			fsp->oplock_type = NO_OPLOCK;
+			break;
+		}
+
+		status = set_file_oplock(fsp);
+		if (!NT_STATUS_IS_OK(status)) {
+			/*
+			 * Could not get the kernel oplock
+			 */
+			fsp->oplock_type = NO_OPLOCK;
+		}
 	}
 
 	ok = set_share_mode(lck, fsp, get_current_uid(fsp->conn),
 			    req ? req->mid : 0,
 			    fsp->oplock_type,
-			    UINT32_MAX);
+			    lease_idx);
 	if (!ok) {
 		return NT_STATUS_NO_MEMORY;
 	}
@@ -2683,8 +2760,10 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
 			smb_panic("validate_oplock_types failed");
 		}
 
-		if (delay_for_oplock(fsp, 0, lck, false, create_disposition)) {
-			schedule_defer_open(lck, fsp->file_id, request_time, req);
+		if (delay_for_oplock(fsp, 0, lease, lck, false,
+				     create_disposition, first_open_attempt)) {
+			schedule_defer_open(lck, fsp->file_id, request_time,
+					    req);
 			TALLOC_FREE(lck);
 			DEBUG(10, ("Sent oplock break request to kernel "
 				   "oplock holder\n"));
@@ -2805,9 +2884,9 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
 
 	if ((req != NULL) &&
 	    delay_for_oplock(
-		    fsp, oplock_request, lck,
+		    fsp, oplock_request, lease, lck,
 		    NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION),
-		    create_disposition)) {
+		    create_disposition, first_open_attempt)) {
 		schedule_defer_open(lck, fsp->file_id, request_time, req);
 		TALLOC_FREE(lck);
 		fd_close(fsp);
@@ -3016,7 +3095,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
 
 	if (file_existed) {
 		/*
-		 * stat opens on existing files don't get oplocks.
+		 * stat opens on existing files don't get oplocks or leases.
 		 *
 		 * Note that we check for stat open on the *open_access_mask*,
 		 * i.e. the access mask we actually used to do the open,
@@ -3026,7 +3105,11 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
 		 * which adds FILE_WRITE_DATA to open_access_mask.
 		 */
 		if (is_stat_open(open_access_mask)) {
-			oplock_request = NO_OPLOCK;
+			if (lease) {
+				lease->lease_state = SMB2_LEASE_NONE;
+			} else {
+				oplock_request = NO_OPLOCK;
+			}
 		}
 	}
 
@@ -3048,7 +3131,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
 	 * Setup the oplock info in both the shared memory and
 	 * file structs.
 	 */
-	status = grant_fsp_oplock_type(req, fsp, lck, oplock_request);
+	status = grant_fsp_oplock_type(req, fsp, lck, oplock_request, lease);
 	if (!NT_STATUS_IS_OK(status)) {
 		TALLOC_FREE(lck);
 		fd_close(fsp);
diff --git a/source3/smbd/oplock.c b/source3/smbd/oplock.c
index 5ad881d..8f318f5 100644
--- a/source3/smbd/oplock.c
+++ b/source3/smbd/oplock.c
@@ -196,15 +196,10 @@ bool update_num_read_oplocks(files_struct *fsp, struct share_mode_lock *lck)
 
 	for (i=0; i<d->num_share_modes; i++) {
 		struct share_mode_entry *e = &d->share_modes[i];
+		uint32_t e_lease_type = get_lease_type(d, e);
 
-		if (EXCLUSIVE_OPLOCK_TYPE(e->op_type)) {
+		if (e_lease_type & SMB2_LEASE_READ) {
 			num_read_oplocks += 1;
-			continue;
-		}
-
-		if (LEVEL_II_OPLOCK_TYPE(e->op_type)) {
-			num_read_oplocks += 1;
-			continue;
 		}
 	}
 
@@ -772,14 +767,15 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx,
 {
 	struct share_mode_entry msg;
 	files_struct *fsp;
-	bool break_to_level2 = False;
 	bool use_kernel;
 	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);
 	struct kernel_oplocks *koplocks = sconn->oplocks.kernel_ops;
+	uint16_t break_from;
 	uint16_t break_to;
+	bool break_needed = true;
 
 	if (data->data == NULL) {
 		DEBUG(0, ("Got NULL buffer\n"));
@@ -809,27 +805,118 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx,
 		return;
 	}
 
-	if (fsp->sent_oplock_break != NO_BREAK_SENT) {
-		/*
-		 * Nothing to do anymore
-		 */
+	break_from = fsp_lease_type(fsp);
+
+	if (fsp->oplock_type != LEASE_OPLOCK) {
+		if (fsp->sent_oplock_break != NO_BREAK_SENT) {
+			/*
+			 * Nothing to do anymore
+			 */
+			DEBUG(10, ("fsp->sent_oplock_break = %d\n",
+				   fsp->sent_oplock_break));
+			return;
+		}
+	}
+
+	if (!(global_client_caps & CAP_LEVEL_II_OPLOCKS)) {
+		DEBUG(10, ("client_caps without level2 oplocks\n"));
+		break_to &= ~SMB2_LEASE_READ;
+	}
+
+	use_kernel = lp_kernel_oplocks(SNUM(fsp->conn)) && koplocks;
+	if (use_kernel && !(koplocks->flags & KOPLOCKS_LEVEL2_SUPPORTED)) {
+		DEBUG(10, ("Kernel oplocks don't allow level2\n"));
+		break_to &= ~SMB2_LEASE_READ;
+	}
+
+	if (!lp_level2_oplocks(SNUM(fsp->conn))) {
+		DEBUG(10, ("no level2 oplocks by config\n"));
+		break_to &= ~SMB2_LEASE_READ;
+	}
+
+	if (fsp->oplock_type == LEASE_OPLOCK) {
+		struct share_mode_lock *lck;
+		int idx;
+
+		lck = get_existing_share_mode_lock(
+			talloc_tos(), fsp->file_id);
+		if (lck == NULL) {
+			/*
+			 * We hit a race here. Break messages are sent, and
+			 * before we get to process this message, we have closed
+			 * the file.
+			 */
+			DEBUG(3, ("Did not find share_mode\n"));
+			return;
+		}
+
+		idx = find_share_mode_lease(
+			lck->data,
+			fsp_client_guid(fsp),
+			&fsp->lease->lease.lease_key);
+		if (idx != -1) {
+			struct share_mode_lease *l;
+			l = &lck->data->leases[idx];
+
+			break_from = l->current_state;
+			break_to &= l->current_state;
+
+			if (l->breaking) {
+				break_to &= l->breaking_to_required;
+				if (l->breaking_to_required != break_to) {
+					/*
+					 * Note we don't increment the epoch
+					 * here, which might be a bug in
+					 * Windows too...
+					 */
+					l->breaking_to_required = break_to;
+					lck->data->modified = true;
+				}
+				break_needed = false;
+			} else if (l->current_state == break_to) {
+				break_needed = false;
+			} else if (l->current_state == SMB2_LEASE_READ) {
+				l->current_state = SMB2_LEASE_NONE;
+				/* Need to increment the epoch */
+				l->epoch += 1;
+				lck->data->modified = true;
+			} else {
+				l->breaking = true;
+				l->breaking_to_required = break_to;
+				l->breaking_to_requested = break_to;
+				/* Need to increment the epoch */
+				l->epoch += 1;
+				lck->data->modified = true;
+			}
+
+			/* Ensure we're in sync with current lease state. */
+			fsp_lease_update(lck, fsp_client_guid(fsp), fsp->lease);
+		}
+
+		TALLOC_FREE(lck);
+	}
+
+	if (!break_needed) {
+		DEBUG(10,("%s: skip break\n", __func__));
 		return;
 	}
 
-	if (break_to == fsp->oplock_type) {
-		DEBUG(3, ("Already downgraded oplock on %s: %s\n",
+	if ((break_from == SMB2_LEASE_NONE) && !break_needed) {
+		DEBUG(3, ("Already downgraded oplock to none on %s: %s\n",
 			  file_id_string_tos(&fsp->file_id),
 			  fsp_str_dbg(fsp)));
 		return;
 	}
 
-	use_kernel = lp_kernel_oplocks(SNUM(fsp->conn)) && koplocks;
+	DEBUG(10, ("break_from=%u, break_to=%u\n",
+		   (unsigned)break_from, (unsigned)break_to));
 
-	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;
+	if ((break_from == break_to) && !break_needed) {
+		DEBUG(3, ("Already downgraded oplock to %u on %s: %s\n",
+			  (unsigned)break_to,
+			  file_id_string_tos(&fsp->file_id),
+			  fsp_str_dbg(fsp)));
+		return;
 	}
 
 	/* Need to wait before sending a break
@@ -839,21 +926,31 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx,
 	}
 
 	if (sconn->using_smb2) {
-		send_break_message_smb2(fsp, break_to_level2 ?
-			OPLOCKLEVEL_II : OPLOCKLEVEL_NONE);
+		send_break_message_smb2(fsp, break_from, break_to);
 	} else {
-		send_break_message_smb1(fsp, break_to_level2 ?
-			OPLOCKLEVEL_II : OPLOCKLEVEL_NONE);
+		send_break_message_smb1(fsp, (break_to & SMB2_LEASE_READ) ?
+					OPLOCKLEVEL_II : OPLOCKLEVEL_NONE);
 	}
 
-	if ((fsp->oplock_type == LEVEL_II_OPLOCK) && (break_to == NO_OPLOCK)) {
+	if ((break_from == SMB2_LEASE_READ) &&
+	    (break_to == SMB2_LEASE_NONE)) {
 		/*
 		 * This is an async break without a reply and thus no timeout
+		 *
+		 * leases are handled above.
 		 */
-		remove_oplock(fsp);
+		if (fsp->oplock_type != LEASE_OPLOCK) {
+			remove_oplock(fsp);
+		}
+		return;
+	}
+	if (fsp->oplock_type == LEASE_OPLOCK) {
 		return;
 	}
-	fsp->sent_oplock_break = break_to_level2 ? LEVEL_II_BREAK_SENT:BREAK_TO_NONE_SENT;
+
+	fsp->sent_oplock_break = (break_to & SMB2_LEASE_READ) ?
+		LEVEL_II_BREAK_SENT:BREAK_TO_NONE_SENT;
+
 	add_oplock_timeout_handler(fsp);
 }
 
@@ -908,7 +1005,7 @@ static void process_kernel_oplock_break(struct messaging_context *msg_ctx,
 	}
 
 	if (sconn->using_smb2) {
-		send_break_message_smb2(fsp, OPLOCKLEVEL_NONE);
+		send_break_message_smb2(fsp, 0, OPLOCKLEVEL_NONE);
 	} else {
 		send_break_message_smb1(fsp, OPLOCKLEVEL_NONE);
 	}
@@ -921,6 +1018,8 @@ static void process_kernel_oplock_break(struct messaging_context *msg_ctx,
 struct break_to_none_state {
 	struct smbd_server_connection *sconn;
 	struct file_id id;
+	struct smb2_lease_key lease_key;
+	struct GUID client_guid;
 };
 static void do_break_to_none(struct tevent_context *ctx,
 			     struct tevent_immediate *im,
@@ -975,7 +1074,7 @@ static void contend_level2_oplocks_begin_default(files_struct *fsp,
 	 * anyway, so we postpone this into an immediate event.
 	 */
 
-	state = talloc(sconn, struct break_to_none_state);
+	state = talloc_zero(sconn, struct break_to_none_state);
 	if (state == NULL) {
 		DEBUG(1, ("talloc failed\n"));
 		return;
@@ -983,6 +1082,14 @@ static void contend_level2_oplocks_begin_default(files_struct *fsp,
 	state->sconn = sconn;
 	state->id = fsp->file_id;
 
+	if (fsp->oplock_type == LEASE_OPLOCK) {
+		state->client_guid = *fsp_client_guid(fsp);
+		state->lease_key = fsp->lease->lease.lease_key;
+		DEBUG(10, ("Breaking through lease key %"PRIu64"/%"PRIu64"\n",
+			   state->lease_key.data[0],
+			   state->lease_key.data[1]));
+	}
+
 	im = tevent_create_immediate(state);
 	if (im == NULL) {
 		DEBUG(1, ("tevent_create_immediate failed\n"));
@@ -1023,8 +1130,50 @@ static void do_break_to_none(struct tevent_context *ctx,
 	}
 	d = lck->data;
 
-	DEBUG(10,("%s: num_share_modes = %d\n", __func__,
-		  lck->data->num_share_modes ));
+	/*
+	 * Walk leases and oplocks separately: We have to send one break per
+	 * lease. If we have multiple share_mode_entry having a common lease,
+	 * we would break the lease twice if we don't walk the leases list
+	 * separately.
+	 */
+
+	for (i=0; i<d->num_leases; i++) {
+		struct share_mode_lease *l = &d->leases[i];
+		struct share_mode_entry *e;
+		uint32_t j;
+
+		if ((l->current_state & SMB2_LEASE_READ) == 0) {
+			continue;
+		}
+		if (smb2_lease_equal(&state->client_guid,
+				     &state->lease_key,
+				     &l->client_guid,
+				     &l->lease_key)) {
+			DEBUG(10, ("Don't break our own lease\n"));
+			continue;
+		}
+
+		for (j=0; j<d->num_share_modes; j++) {
+			e = &d->share_modes[j];
+
+			if (!is_valid_share_mode_entry(e)) {
+				continue;
+			}
+			if (e->lease_idx == i) {
+				break;
+			}
+		}
+		if (j == d->num_share_modes) {
+			DEBUG(0, ("leases[%"PRIu32"] has no share mode\n",
+				  i));
+			continue;
+		}
+
+		DEBUG(10, ("Breaking lease# %"PRIu32" with share_entry# "
+			   "%"PRIu32"\n", i, j));
+
+		send_break_to_none(state->sconn->msg_ctx, e);
+	}
 
 	for(i = 0; i < d->num_share_modes; i++) {
 		struct share_mode_entry *e = &d->share_modes[i];
@@ -1032,6 +1181,12 @@ static void do_break_to_none(struct tevent_context *ctx,
 		if (!is_valid_share_mode_entry(e)) {
 			continue;
 		}
+		if (e->op_type == LEASE_OPLOCK) {
+			/*
+			 * Took care of those in the loop above
+			 */
+			continue;
+		}
 
 		/*
 		 * As there could have been multiple writes waiting at the
diff --git a/source3/smbd/smb2_break.c b/source3/smbd/smb2_break.c
index 4492456..126bf78 100644
--- a/source3/smbd/smb2_break.c
+++ b/source3/smbd/smb2_break.c
@@ -24,6 +24,10 @@
 #include "smbd/globals.h"
 #include "../libcli/smb/smb_common.h"
 #include "../lib/util/tevent_ntstatus.h"
+#include "locking/leases_db.h"
+
+static NTSTATUS smbd_smb2_request_process_lease_break(
+	struct smbd_smb2_request *req);
 
 static struct tevent_req *smbd_smb2_oplock_break_send(TALLOC_CTX *mem_ctx,
 						      struct tevent_context *ev,
@@ -45,6 +49,12 @@ NTSTATUS smbd_smb2_request_process_break(struct smbd_smb2_request *req)
 	struct tevent_req *subreq;
 
 	status = smbd_smb2_request_verify_sizes(req, 0x18);
+	if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
+		/*
+		 * Retry as a lease break
+		 */
+		return smbd_smb2_request_process_lease_break(req);
+	}
 	if (!NT_STATUS_IS_OK(status)) {
 		return smbd_smb2_request_error(req, status);
 	}
@@ -222,16 +232,213 @@ static NTSTATUS smbd_smb2_oplock_break_recv(struct tevent_req *req,
 	return NT_STATUS_OK;
 }
 
+static void smbd_smb2_request_lease_break_done(struct tevent_req *subreq);
+
+static struct tevent_req *smbd_smb2_lease_break_send(
+	TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+	struct smbd_smb2_request *smb2_req, struct smb2_lease_key in_lease_key,
+	uint32_t in_lease_state);
+static NTSTATUS smbd_smb2_lease_break_recv(struct tevent_req *req,
+					   uint32_t *out_lease_state);
+
+
+static NTSTATUS smbd_smb2_request_process_lease_break(
+	struct smbd_smb2_request *req)
+{
+	NTSTATUS status;
+	const uint8_t *inbody;
+	struct smb2_lease_key in_lease_key;
+	uint32_t in_lease_state;
+	struct tevent_req *subreq;
+
+	status = smbd_smb2_request_verify_sizes(req, 0x24);
+	if (!NT_STATUS_IS_OK(status)) {
+		return smbd_smb2_request_error(req, status);
+	}
+
+	inbody = SMBD_SMB2_IN_BODY_PTR(req);
+
+	in_lease_key.data[0] = BVAL(inbody, 8);
+	in_lease_key.data[1] = BVAL(inbody, 16);
+	in_lease_state = IVAL(inbody, 24);
+
+	subreq = smbd_smb2_lease_break_send(req, req->sconn->ev_ctx, req,
+					    in_lease_key, in_lease_state);
+	if (subreq == NULL) {
+		return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
+	}
+	tevent_req_set_callback(subreq, smbd_smb2_request_lease_break_done, req);
+
+	return smbd_smb2_request_pending_queue(req, subreq, 500);
+}
+
+static void smbd_smb2_request_lease_break_done(struct tevent_req *subreq)
+{
+	struct smbd_smb2_request *req = tevent_req_callback_data(
+		subreq, struct smbd_smb2_request);
+	const uint8_t *inbody;
+	struct smb2_lease_key in_lease_key;
+	uint32_t out_lease_state = 0;
+	DATA_BLOB outbody;
+	NTSTATUS status;
+	NTSTATUS error; /* transport error */
+
+	status = smbd_smb2_lease_break_recv(subreq, &out_lease_state);
+	TALLOC_FREE(subreq);
+	if (!NT_STATUS_IS_OK(status)) {
+		error = smbd_smb2_request_error(req, status);
+		if (!NT_STATUS_IS_OK(error)) {
+			smbd_server_connection_terminate(req->xconn,
+							 nt_errstr(error));
+			return;
+		}
+		return;
+	}
+
+	inbody = SMBD_SMB2_IN_BODY_PTR(req);
+
+	in_lease_key.data[0] = BVAL(inbody, 8);
+	in_lease_key.data[1] = BVAL(inbody, 16);
+
+	outbody = smbd_smb2_generate_outbody(req, 0x24);
+	if (outbody.data == NULL) {
+		error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
+		if (!NT_STATUS_IS_OK(error)) {
+			smbd_server_connection_terminate(req->xconn,
+							 nt_errstr(error));
+			return;
+		}
+		return;
+	}
+
+	SSVAL(outbody.data, 0x00, 0x24);	/* struct size */
+	SSVAL(outbody.data, 0x02, 0);		/* reserved */
+	SIVAL(outbody.data, 0x04, 0);		/* flags, must be 0 */
+	SBVAL(outbody.data, 0x08, in_lease_key.data[0]);
+	SBVAL(outbody.data, 0x10, in_lease_key.data[1]);
+	SIVAL(outbody.data, 0x18, out_lease_state);
+	SBVAL(outbody.data, 0x1c, 0);           /* leaseduration, must be 0 */
+
+	error = smbd_smb2_request_done(req, outbody, NULL);
+	if (!NT_STATUS_IS_OK(error)) {
+		smbd_server_connection_terminate(req->xconn,
+						 nt_errstr(error));
+		return;
+	}
+}
+
+struct smbd_smb2_lease_break_state {
+	uint32_t lease_state;
+};
+
+struct lease_lookup_state {
+	TALLOC_CTX *mem_ctx;
+	/* Return parameters. */
+	uint32_t num_file_ids;
+	struct file_id *ids;
+	NTSTATUS status;
+};
+
+static void lease_parser(
+	uint32_t num_file_ids,
+	struct file_id *ids, const char *filename, const char *stream_name,
+	void *private_data)
+{
+	struct lease_lookup_state *lls =
+		(struct lease_lookup_state *)private_data;
+
+	lls->status = NT_STATUS_OK;
+	lls->num_file_ids = num_file_ids;
+	lls->ids = talloc_memdup(lls->mem_ctx,
+				ids,
+				num_file_ids * sizeof(struct file_id));
+	if (lls->ids == NULL) {
+		lls->status = NT_STATUS_NO_MEMORY;
+	}
+}
+
+static struct tevent_req *smbd_smb2_lease_break_send(
+	TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+	struct smbd_smb2_request *smb2_req, struct smb2_lease_key in_lease_key,
+	uint32_t in_lease_state)
+{
+	struct tevent_req *req;
+	struct smbd_smb2_lease_break_state *state;
+	struct lease_lookup_state lls = {.mem_ctx = mem_ctx};
+	NTSTATUS status;
+
+	req = tevent_req_create(mem_ctx, &state,
+				struct smbd_smb2_lease_break_state);
+	if (req == NULL) {
+		return NULL;
+	}
+	state->lease_state = in_lease_state;
+
+	/* Find any file ids with this lease key. */
+	status = leases_db_parse(&smb2_req->xconn->smb2.client.guid,
+				 &in_lease_key,
+				 lease_parser,
+				 &lls);
+
+	if (!NT_STATUS_IS_OK(status)) {
+		if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+			status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+			DEBUG(10, ("No record for lease key found\n"));
+		}
+	} else if (!NT_STATUS_IS_OK(lls.status)) {
+		status = lls.status;
+	} else if (lls.num_file_ids == 0) {
+		status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+	}
+
+	if (!NT_STATUS_IS_OK(status)) {
+		tevent_req_nterror(req, status);
+		return tevent_req_post(req, ev);
+	}
+
+	status = downgrade_lease(smb2_req->xconn,
+				lls.num_file_ids,
+				lls.ids,
+				&in_lease_key,
+				in_lease_state);
+
+	if (NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_BREAK_IN_PROGRESS)) {
+		tevent_req_done(req);
+		return tevent_req_post(req, ev);
+	}
+	if (tevent_req_nterror(req, status)) {
+		DEBUG(10, ("downgrade_lease returned %s\n",
+			   nt_errstr(status)));
+		return tevent_req_post(req, ev);
+	}
+
+	tevent_req_done(req);
+	return tevent_req_post(req, ev);
+}
+
+static NTSTATUS smbd_smb2_lease_break_recv(struct tevent_req *req,
+					   uint32_t *out_lease_state)
+{
+	struct smbd_smb2_lease_break_state *state = tevent_req_data(
+		req, struct smbd_smb2_lease_break_state);
+	NTSTATUS status;
+
+	if (tevent_req_is_nterror(req, &status)) {
+		return status;
+	}
+	*out_lease_state = state->lease_state;
+	return NT_STATUS_OK;
+}
+
 /*********************************************************
  Create and send an asynchronous
  SMB2 OPLOCK_BREAK_NOTIFICATION.
 *********************************************************/
 
-void send_break_message_smb2(files_struct *fsp, int level)
+void send_break_message_smb2(files_struct *fsp,
+			     uint32_t break_from,
+			     uint32_t break_to)
 {
-	uint8_t smb2_oplock_level = (level == OPLOCKLEVEL_II) ?
-				SMB2_OPLOCK_LEVEL_II :
-				SMB2_OPLOCK_LEVEL_NONE;
 	NTSTATUS status;
 	struct smbXsrv_connection *xconn = NULL;
 	struct smbXsrv_session *session = NULL;
@@ -257,7 +464,7 @@ void send_break_message_smb2(files_struct *fsp, int level)
 			"for file %s, %s, smb2 level %u session %llu not found\n",
 			fsp_str_dbg(fsp),
 			fsp_fnum_dbg(fsp),
-			(unsigned int)smb2_oplock_level,
+			(unsigned int)break_to,
 			(unsigned long long)fsp->vuid));
 		return;
 	}
@@ -266,13 +473,35 @@ void send_break_message_smb2(files_struct *fsp, int level)
 		"for file %s, %s, smb2 level %u\n",
 		fsp_str_dbg(fsp),
 		fsp_fnum_dbg(fsp),
-		(unsigned int)smb2_oplock_level ));
+		(unsigned int)break_to ));
 
-	status = smbd_smb2_send_oplock_break(xconn,
-					     session,
-					     fsp->conn->tcon,
-					     fsp->op,
-					     smb2_oplock_level);
+	if (fsp->oplock_type == LEASE_OPLOCK) {
+		uint32_t break_flags = 0;
+		uint16_t new_epoch;
+
+		if (fsp->lease->lease.lease_state != SMB2_LEASE_NONE) {
+			break_flags = SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED;
+		}
+
+		if (fsp->lease->lease.lease_version > 1) {
+			new_epoch = fsp->lease->lease.lease_epoch;
+		} else {
+			new_epoch = 0;
+		}
+
+		status = smbd_smb2_send_lease_break(xconn, new_epoch, break_flags,
+						    &fsp->lease->lease.lease_key,
+						    break_from, break_to);
+	} else {
+		uint8_t smb2_oplock_level;
+		smb2_oplock_level = (break_to & SMB2_LEASE_READ) ?
+			SMB2_OPLOCK_LEVEL_II : SMB2_OPLOCK_LEVEL_NONE;
+		status = smbd_smb2_send_oplock_break(xconn,
+						     session,
+						     fsp->conn->tcon,
+						     fsp->op,
+						     smb2_oplock_level);
+	}
 	if (!NT_STATUS_IS_OK(status)) {
 		smbd_server_connection_terminate(xconn,
 						 nt_errstr(status));
-- 
1.9.1


From 9b16f7c19ae1a8cc361ee8c85b2d531b0f65f9e4 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 28 Oct 2014 15:31:46 -0700
Subject: [PATCH 23/28] s3:smb2_create: support leases and pass them down to
 the VFS layer.

Pair-Programmed-With: Stefan Metzmacher <metze at samba.org>

Signed-off-by: Volker Lendecke <vl at samba.org>
Signed-off-by: Stefan Metzmacher <metze at samba.org>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
 source3/smbd/smb2_create.c | 91 ++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 87 insertions(+), 4 deletions(-)

diff --git a/source3/smbd/smb2_create.c b/source3/smbd/smb2_create.c
index e96e84a..e0eef43 100644
--- a/source3/smbd/smb2_create.c
+++ b/source3/smbd/smb2_create.c
@@ -25,6 +25,7 @@
 #include "smbd/globals.h"
 #include "../libcli/smb/smb_common.h"
 #include "../librpc/gen_ndr/ndr_security.h"
+#include "../librpc/gen_ndr/ndr_smb2_lease_struct.h"
 #include "../lib/util/tevent_ntstatus.h"
 #include "messages.h"
 
@@ -40,9 +41,7 @@ int map_smb2_oplock_levels_to_samba(uint8_t in_oplock_level)
 	case SMB2_OPLOCK_LEVEL_BATCH:
 		return BATCH_OPLOCK;
 	case SMB2_OPLOCK_LEVEL_LEASE:
-		DEBUG(2,("map_smb2_oplock_levels_to_samba: "
-			"LEASE_OPLOCK_REQUESTED\n"));
-		return NO_OPLOCK;
+		return LEASE_OPLOCK;
 	default:
 		DEBUG(2,("map_smb2_oplock_levels_to_samba: "
 			"unknown level %u\n",
@@ -59,6 +58,8 @@ static uint8_t map_samba_oplock_levels_to_smb2(int oplock_type)
 		return SMB2_OPLOCK_LEVEL_EXCLUSIVE;
 	} else if (oplock_type == LEVEL_II_OPLOCK) {
 		return SMB2_OPLOCK_LEVEL_II;
+	} else if (oplock_type == LEASE_OPLOCK) {
+		return SMB2_OPLOCK_LEVEL_LEASE;
 	} else {
 		return SMB2_OPLOCK_LEVEL_NONE;
 	}
@@ -368,6 +369,11 @@ static void smbd_smb2_request_create_done(struct tevent_req *tsubreq)
 	}
 }
 
+static bool smb2_lease_key_valid(const struct smb2_lease_key *key)
+{
+	return ((key->data[0] != 0) || (key->data[1] != 0));
+}
+
 static NTSTATUS smbd_smb2_create_durable_lease_check(
 	const char *requested_filename, const struct files_struct *fsp,
 	const struct smb2_lease *lease_ptr)
@@ -467,6 +473,7 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 	struct smb2_create_blob *dh2c = NULL;
 	struct smb2_create_blob *dhnq = NULL;
 	struct smb2_create_blob *dh2q = NULL;
+	struct smb2_create_blob *rqls = NULL;
 	struct smbXsrv_open *op = NULL;
 
 	ZERO_STRUCT(out_context_blobs);
@@ -513,6 +520,10 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 				     SMB2_CREATE_TAG_DH2Q);
 	dh2c = smb2_create_blob_find(&in_context_blobs,
 				     SMB2_CREATE_TAG_DH2C);
+	if (smb2req->xconn->smb2.server.capabilities & SMB2_CAP_LEASING) {
+		rqls = smb2_create_blob_find(&in_context_blobs,
+					     SMB2_CREATE_TAG_RQLS);
+	}
 
 	if ((dhnc && dh2c) || (dhnc && dh2q) || (dh2c && dhnq) ||
 	    (dh2q && dh2c))
@@ -552,6 +563,10 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 			num_blobs_allowed = 1;
 		}
 
+		if (rqls != NULL) {
+			num_blobs_allowed += 1;
+		}
+
 		if (in_context_blobs.num_blobs != num_blobs_allowed) {
 			tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
 			return tevent_req_post(req, ev);
@@ -584,6 +599,10 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 
 		num_blobs_allowed = 1;
 
+		if (rqls != NULL) {
+			num_blobs_allowed += 1;
+		}
+
 		if (in_context_blobs.num_blobs != num_blobs_allowed) {
 			tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
 			return tevent_req_post(req, ev);
@@ -650,7 +669,9 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 		uint32_t durable_timeout_msec = 0;
 		bool do_durable_reconnect = false;
 		uint64_t persistent_id = 0;
+		struct smb2_lease lease;
 		struct smb2_lease *lease_ptr = NULL;
+		ssize_t lease_len = -1;
 
 		exta = smb2_create_blob_find(&in_context_blobs,
 					     SMB2_CREATE_TAG_EXTA);
@@ -850,6 +871,34 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 			}
 		}
 
+		if (rqls) {
+			lease_len = smb2_lease_pull(
+				rqls->data.data, rqls->data.length, &lease);
+			if (lease_len == -1) {
+				tevent_req_nterror(
+					req, NT_STATUS_INVALID_PARAMETER);
+				return tevent_req_post(req, ev);
+			}
+			lease_ptr = &lease;
+
+			if (DEBUGLEVEL >= 10) {
+				DEBUG(10, ("Got lease request size %d\n",
+					   (int)lease_len));
+				NDR_PRINT_DEBUG(smb2_lease, lease_ptr);
+			}
+
+			if (!smb2_lease_key_valid(&lease.lease_key)) {
+				lease_ptr = NULL;
+				requested_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
+			}
+
+			if ((smb2req->xconn->protocol < PROTOCOL_SMB3_00) &&
+			    (lease.lease_version != 1)) {
+				DEBUG(10, ("v2 lease key only for SMB3\n"));
+				lease_ptr = NULL;
+			}
+		}
+
 		/* these are ignored for SMB2 */
 		in_create_options &= ~(0x10);/* NTCREATEX_OPTIONS_SYNC_ALERT */
 		in_create_options &= ~(0x20);/* NTCREATEX_OPTIONS_ASYNC_ALERT */
@@ -935,6 +984,14 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 		} else {
 			struct smb_filename *smb_fname = NULL;
 
+			if (requested_oplock_level == SMB2_OPLOCK_LEVEL_LEASE) {
+				if (lease_ptr == NULL) {
+					requested_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
+				}
+			} else {
+				lease_ptr = NULL;
+			}
+
 			/*
 			 * For a DFS path the function parse_dfs_path()
 			 * will do the path processing.
@@ -1005,7 +1062,7 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 						     in_create_options,
 						     in_file_attributes,
 						     map_smb2_oplock_levels_to_samba(requested_oplock_level),
-						     NULL,
+						     lease_ptr,
 						     allocation_size,
 						     0, /* private_flags */
 						     sec_desc,
@@ -1144,6 +1201,32 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 				return tevent_req_post(req, ev);
 			}
 		}
+
+		if ((rqls != NULL) && (result->oplock_type == LEASE_OPLOCK)) {
+			uint8_t buf[52];
+
+			lease = result->lease->lease;
+
+			lease_len = sizeof(buf);
+			if (lease.lease_version == 1) {
+				lease_len = 32;
+			}
+
+			if (!smb2_lease_push(&lease, buf, lease_len)) {
+				tevent_req_nterror(
+					req, NT_STATUS_INTERNAL_ERROR);
+				return tevent_req_post(req, ev);
+			}
+
+			status = smb2_create_blob_add(
+				state, &out_context_blobs,
+				SMB2_CREATE_TAG_RQLS,
+				data_blob_const(buf, lease_len));
+			if (!NT_STATUS_IS_OK(status)) {
+				tevent_req_nterror(req, status);
+				return tevent_req_post(req, ev);
+			}
+		}
 	}
 
 	smb2req->compat_chain_fsp = smb1req->chain_fsp;
-- 
1.9.1


From 5623e0e8125be0a699d4f7e15d60b461133e6223 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Tue, 14 Oct 2014 10:34:53 -0700
Subject: [PATCH 24/28] s3:param: Add "smb2 leases" parameter. Default "false".

This is currently marked as experimental!

Signed-off-by: Jeremy Allison <jra at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 docs-xml/smbdotconf/locking/kerneloplocks.xml |  1 +
 docs-xml/smbdotconf/locking/oplocks.xml       |  1 +
 docs-xml/smbdotconf/locking/smb2leases.xml    | 28 +++++++++++++++++++++++++++
 lib/param/param_table.c                       |  9 +++++++++
 source3/param/loadparm.c                      |  1 +
 5 files changed, 40 insertions(+)
 create mode 100644 docs-xml/smbdotconf/locking/smb2leases.xml

diff --git a/docs-xml/smbdotconf/locking/kerneloplocks.xml b/docs-xml/smbdotconf/locking/kerneloplocks.xml
index 8e3bba5..d8fe223 100644
--- a/docs-xml/smbdotconf/locking/kerneloplocks.xml
+++ b/docs-xml/smbdotconf/locking/kerneloplocks.xml
@@ -25,5 +25,6 @@
 
 <related>oplocks</related>
 <related>level2 oplocks</related>
+<related>smb2 leases</related>
 <value type="default">no</value>
 </samba:parameter>
diff --git a/docs-xml/smbdotconf/locking/oplocks.xml b/docs-xml/smbdotconf/locking/oplocks.xml
index a56e921..a5e163b 100644
--- a/docs-xml/smbdotconf/locking/oplocks.xml
+++ b/docs-xml/smbdotconf/locking/oplocks.xml
@@ -25,5 +25,6 @@
 
 <related>kernel oplocks</related>
 <related>level2 oplocks</related>
+<related>smb2 leases</related>
 <value type="default">yes</value>
 </samba:parameter>
diff --git a/docs-xml/smbdotconf/locking/smb2leases.xml b/docs-xml/smbdotconf/locking/smb2leases.xml
new file mode 100644
index 0000000..0a734ec
--- /dev/null
+++ b/docs-xml/smbdotconf/locking/smb2leases.xml
@@ -0,0 +1,28 @@
+<samba:parameter name="smb2 leases"
+                 context="G"
+				 type="boolean"
+                 xmlns:samba="http://www.samba.org/samba/DTD/samba-doc">
+<description>
+	<para>
+	This boolean option tells <command moreinfo="none">smbd</command> whether to
+	globally negotiate SMB2 leases on file open requests. Leasing is an SMB2-only
+	feature which allows clients to aggressively cache files locally above and
+	beyond the caching allowed by SMB1 oplocks. This (experimental) parameter is
+	set to off by default until the SMB2 leasing code is declared fully stable.
+	</para>
+
+	<para>
+	This is only available with <smbconfoption name="oplocks">yes</smbconfoption>
+	and <smbconfoption name="kernel oplocks">no</smbconfoption>.
+	</para>
+
+	<para>
+	The Samba implementation of leases is currently marked as experimental!
+	</para>
+</description>
+
+<related>oplocks</related>
+<related>kernel oplocks</related>
+<related>level2 oplocks</related>
+<value type="default">no</value>
+</samba:parameter>
diff --git a/lib/param/param_table.c b/lib/param/param_table.c
index 5dbcda8..18b0628 100644
--- a/lib/param/param_table.c
+++ b/lib/param/param_table.c
@@ -3023,6 +3023,15 @@ struct parm_struct parm_table[] = {
 		.flags		= FLAG_ADVANCED | FLAG_SHARE | FLAG_GLOBAL,
 	},
 	{
+		.label		= "smb2 leases",
+		.type		= P_BOOL,
+		.p_class	= P_GLOBAL,
+		.offset		= GLOBAL_VAR(smb2_leases),
+		.special	= NULL,
+		.enum_list	= NULL,
+		.flags		= FLAG_ADVANCED,
+	},
+	{
 		.label		= "locking",
 		.type		= P_BOOL,
 		.p_class	= P_LOCAL,
diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c
index 8494c3f..da50e3a 100644
--- a/source3/param/loadparm.c
+++ b/source3/param/loadparm.c
@@ -855,6 +855,7 @@ static void init_globals(struct loadparm_context *lp_ctx, bool reinit_globals)
 	Globals.smb2_max_write = DEFAULT_SMB2_MAX_WRITE;
 	Globals.smb2_max_trans = DEFAULT_SMB2_MAX_TRANSACT;
 	Globals.ismb2_max_credits = DEFAULT_SMB2_MAX_CREDITS;
+	Globals.smb2_leases = false;
 
 	string_set(Globals.ctx, &Globals.ncalrpc_dir, get_dyn_NCALRPCDIR());
 
-- 
1.9.1


From c02b61500fd75915ab77c2e5b05286fd0650e806 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Tue, 28 Oct 2014 15:31:46 -0700
Subject: [PATCH 25/28] s3:smb2_negprot: announce support for SMB2.1 leases.

We only do this with "smb2 leases = yes"
and the default values for "oplocks = yes"
and "kernel oplocks = no".

Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Jeremy Allison <jra at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 source3/smbd/smb2_negprot.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/source3/smbd/smb2_negprot.c b/source3/smbd/smb2_negprot.c
index 6904972..9a1ca9c 100644
--- a/source3/smbd/smb2_negprot.c
+++ b/source3/smbd/smb2_negprot.c
@@ -230,6 +230,14 @@ NTSTATUS smbd_smb2_request_process_negprot(struct smbd_smb2_request *req)
 		capabilities |= SMB2_CAP_DFS;
 	}
 
+	if (protocol >= PROTOCOL_SMB2_10 &&
+	    lp_smb2_leases() &&
+	    lp_oplocks(GLOBAL_SECTION_SNUM) &&
+	    !lp_kernel_oplocks(GLOBAL_SECTION_SNUM))
+	{
+		capabilities |= SMB2_CAP_LEASING;
+	}
+
 	if ((protocol >= PROTOCOL_SMB2_24) &&
 	    (lp_smb_encrypt(-1) != SMB_SIGNING_OFF) &&
 	    (in_capabilities & SMB2_CAP_ENCRYPTION)) {
-- 
1.9.1


From 17afe43582545571f02fbee84e1f0efd6d9a56e2 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Tue, 14 Oct 2014 10:34:53 -0700
Subject: [PATCH 26/28] selftest:Samba3: use "smb2 leases = yes"

Signed-off-by: Jeremy Allison <jra at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 selftest/knownfail        | 13 -------------
 selftest/target/Samba3.pm |  1 +
 2 files changed, 1 insertion(+), 13 deletions(-)

diff --git a/selftest/knownfail b/selftest/knownfail
index 1c4f446..af7e7fd 100644
--- a/selftest/knownfail
+++ b/selftest/knownfail
@@ -189,25 +189,12 @@
 ^samba3.smb2.notify.valid-req
 ^samba3.smb2.notify.dir
 ^samba3.smb2.notify.rec
-^samba3.smb2.durable-open.lock-lease
 ^samba3.smb2.durable-open.delete_on_close2
-^samba3.smb2.durable-v2-open.open-lease
-^samba3.smb2.durable-v2-open.persistent-open-lease
 ^samba3.smb2.durable-v2-open.app-instance
 ^samba4.smb2.ioctl.req_resume_key\(dc\) # not supported by s4 ntvfs server
 ^samba4.smb2.ioctl.copy_chunk_\w*\(dc\)	# not supported by s4 ntvfs server
 ^samba3.smb2.dir.one
 ^samba3.smb2.dir.modify
-^samba3.smb2.lease.request
-^samba3.smb2.lease.upgrade
-^samba3.smb2.lease.break
-^samba3.smb2.lease.oplock
-^samba3.smb2.lease.multibreak
-^samba3.smb2.lease.v2_request
-^samba3.smb2.lease.v2_request_parent
-^samba3.smb2.lease.break_twice
-^samba3.smb2.lease.nobreakself
-^samba3.smb2.lease.v2_epoch1
 ^samba3.smb2.oplock.batch20
 ^samba3.smb2.oplock.stream1
 ^samba3.smb2.streams.rename
diff --git a/selftest/target/Samba3.pm b/selftest/target/Samba3.pm
index 797179f..50c2642 100755
--- a/selftest/target/Samba3.pm
+++ b/selftest/target/Samba3.pm
@@ -1057,6 +1057,7 @@ sub provision($$$$$$)
 
 	kernel oplocks = no
 	kernel change notify = no
+	smb2 leases = yes
 
 	syslog = no
 	printing = bsd
-- 
1.9.1


From 5e70c549df1436beb68b853ae8770e45472d0d08 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Sat, 8 Nov 2014 09:45:59 +0100
Subject: [PATCH 27/28] s3:smbd: document the interaction between "smb2 leases"
 and "write cache size"

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 docs-xml/smbdotconf/locking/smb2leases.xml    | 3 +++
 docs-xml/smbdotconf/tuning/writecachesize.xml | 3 +++
 source3/smbd/fileio.c                         | 5 +++++
 3 files changed, 11 insertions(+)

diff --git a/docs-xml/smbdotconf/locking/smb2leases.xml b/docs-xml/smbdotconf/locking/smb2leases.xml
index 0a734ec..9241bc8 100644
--- a/docs-xml/smbdotconf/locking/smb2leases.xml
+++ b/docs-xml/smbdotconf/locking/smb2leases.xml
@@ -16,6 +16,8 @@
 	and <smbconfoption name="kernel oplocks">no</smbconfoption>.
 	</para>
 
+	<para>Note that the write cache won't be used for file handles with a smb2 write lease.</para>
+
 	<para>
 	The Samba implementation of leases is currently marked as experimental!
 	</para>
@@ -24,5 +26,6 @@
 <related>oplocks</related>
 <related>kernel oplocks</related>
 <related>level2 oplocks</related>
+<related>write cache size</related>
 <value type="default">no</value>
 </samba:parameter>
diff --git a/docs-xml/smbdotconf/tuning/writecachesize.xml b/docs-xml/smbdotconf/tuning/writecachesize.xml
index 12965b4..d7e2cc5 100644
--- a/docs-xml/smbdotconf/tuning/writecachesize.xml
+++ b/docs-xml/smbdotconf/tuning/writecachesize.xml
@@ -21,8 +21,11 @@
 
     <para>The integer parameter specifies the size of this cache 
 		(per oplocked file) in bytes.</para>
+
+    <para>Note that the write cache won't be used for file handles with a smb2 write lease.</para>
 </description>
 
+<related>smb2 leases</related>
 <value type="default">0</value>
 <value type="example">262144<comment> for a 256k cache size per file</comment></value>
 </samba:parameter>
diff --git a/source3/smbd/fileio.c b/source3/smbd/fileio.c
index 9f3cc1a..2fe0432 100644
--- a/source3/smbd/fileio.c
+++ b/source3/smbd/fileio.c
@@ -339,6 +339,11 @@ ssize_t write_file(struct smb_request *req,
 	if (!fsp->modified &&
 	    EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type) &&
 	    (wcp == NULL)) {
+		/*
+		 * Note: no write cache with leases!
+		 * as the handles would have to share the write cache
+		 * that's possible but an improvement for another day...
+		 */
 		setup_write_cache(fsp, fsp->fsp_name->st.st_ex_size);
 		wcp = fsp->wcp;
 	}
-- 
1.9.1


From d18d0e70e15da56506b1ee4bcda87d27a8561d74 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Sat, 8 Nov 2014 09:47:01 +0100
Subject: [PATCH 28/28] docs-xml: document the interaction between "write cache
 size" and "aio read/write size"

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 docs-xml/smbdotconf/tuning/writecachesize.xml | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/docs-xml/smbdotconf/tuning/writecachesize.xml b/docs-xml/smbdotconf/tuning/writecachesize.xml
index d7e2cc5..e2fa17f 100644
--- a/docs-xml/smbdotconf/tuning/writecachesize.xml
+++ b/docs-xml/smbdotconf/tuning/writecachesize.xml
@@ -25,6 +25,8 @@
     <para>Note that the write cache won't be used for file handles with a smb2 write lease.</para>
 </description>
 
+<related>aio read size</related>
+<related>aio write size</related>
 <related>smb2 leases</related>
 <value type="default">0</value>
 <value type="example">262144<comment> for a 256k cache size per file</comment></value>
-- 
1.9.1

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 181 bytes
Desc: OpenPGP digital signature
URL: <http://lists.samba.org/pipermail/samba-technical/attachments/20141204/3a1847ec/attachment.pgp>


More information about the samba-technical mailing list