Latest leases patchset - getting there !

Stefan (metze) Metzmacher metze at samba.org
Tue Nov 11 09:35:21 MST 2014


Hi Jeremy,

here're 3 patch files.

ok12.diff.txt - this is ready to be pushed to master

leases12.diff.txt - contains the last patchset with the undo patch
squashed.

ontop12.diff.txt - contains fixes for "kernel oplocks = yes" and "level2
oplocks = no"
I'll squash them in the next round...

So please push ok12.diff.txt and make sure the result of all patches
still works for you.

metze

Am 07.11.2014 um 21:21 schrieb Jeremy Allison:
> On Fri, Nov 07, 2014 at 10:03:50AM -0800, Jeremy Allison wrote:
>> On Fri, Nov 07, 2014 at 06:08:38PM +0100, Stefan (metze) Metzmacher wrote:
>>> Hi Jeremy,
>>>
>>> here're 2 patch files.
>>>
>>> ok.diff.txt contains the changes from my
>>> https://git.samba.org/?p=metze/samba/wip.git;a=shortlog;h=refs/heads/master3-leases-ok
>>> branch, they're ready for master and should not cause much trouble when
>>> you rebase the rest.
>>
>> OK - pushed !
>>
>>> tmp.diff.txt contains the grant_fsp_oplock_type() and brl_num_read changes
>>> Just tell me if they look ok for you, don't push them yet.
>>
>> Yes, they look ok - I looked over the logic carefully,
>> plus it still passes make test :-).
>>
>>> undo.diff.txt revert some of tmp.diff.txt so that the rest will
>>> rebase correctly.
>>
>> Thanks !
>>
>>> If you're fine with tmp.diff.txt I'll prepare a complete rebased patchset.
>>
>> Sure - send it over to me once you're finished.
>>
>> I'll also rebase this afternoon on top of ok+tmp+undo
>> just to make sure all the leases code still works :-).
> 
> Ok, here is the rebased leases patchset on top
> of ok+tmp+undo (for anyone who wants to follow
> along :-).
> 
> Doing a dirdiff in source3 between the original
> leases patchset and this one only shows the
> DEBUG message difference in source3/locking/brlock.c.
> 
> Cheers,
> 
> Jeremy.
> 
-------------- next part --------------
From 576055815b09508c7bf59c5b59b3bdf776af4f28 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 23 Sep 2014 23:34:14 +0200
Subject: [PATCH 1/5] s3:smbd: break oplocks to none with FILE_OVERWRITE

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/open.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index ccea1e9..26244f6 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1431,6 +1431,7 @@ static bool delay_for_oplock(files_struct *fsp,
 
 	switch (create_disposition) {
 	case FILE_SUPERSEDE:
+	case FILE_OVERWRITE:
 	case FILE_OVERWRITE_IF:
 		break_to = NO_OPLOCK;
 		break;
-- 
1.9.1


From 6800229d6ec6ba29b3365786d66d352a454ccfd4 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 28 Oct 2014 15:27:09 -0700
Subject: [PATCH 2/5] s3:smbd: move all oplock granting code to
 grant_fsp_oplock_type()

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

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

diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index 26244f6..945850c 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1490,38 +1490,37 @@ static bool file_has_brlocks(files_struct *fsp)
 	return (brl_num_locks(br_lck) > 0);
 }
 
-static void grant_fsp_oplock_type(files_struct *fsp,
-				  struct share_mode_lock *lck,
-				  int oplock_request)
+static NTSTATUS grant_fsp_oplock_type(struct smb_request *req,
+				      struct files_struct *fsp,
+				      struct share_mode_lock *lck,
+				      int oplock_request)
 {
 	bool allow_level2 = (global_client_caps & CAP_LEVEL_II_OPLOCKS) &&
 		            lp_level2_oplocks(SNUM(fsp->conn));
 	bool got_level2_oplock, got_a_none_oplock;
 	uint32_t i;
+	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;
-		DEBUG(10,("grant_fsp_oplock_type: oplock type 0x%x on file %s\n",
-			fsp->oplock_type, fsp_str_dbg(fsp)));
-		return;
+		goto type_selected;
 	}
 
 	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;
-	}
-
-	if (is_stat_open(fsp->access_mask)) {
-		/* Leave the value already set. */
-		DEBUG(10,("grant_fsp_oplock_type: oplock type 0x%x on file %s\n",
-			fsp->oplock_type, fsp_str_dbg(fsp)));
-		return;
+		goto type_selected;
 	}
 
 	got_level2_oplock = false;
@@ -1555,6 +1554,23 @@ static void grant_fsp_oplock_type(files_struct *fsp,
 	 */
 	if (fsp->oplock_type == LEVEL_II_OPLOCK && !allow_level2) {
 		fsp->oplock_type = NO_OPLOCK;
+		goto type_selected;
+	}
+
+ type_selected:
+	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);
+	if (!ok) {
+		return NT_STATUS_NO_MEMORY;
 	}
 
 	if (fsp->oplock_type == LEVEL_II_OPLOCK && !got_level2_oplock) {
@@ -1572,6 +1588,8 @@ static void grant_fsp_oplock_type(files_struct *fsp,
 
 	DEBUG(10,("grant_fsp_oplock_type: oplock type 0x%x on file %s\n",
 		  fsp->oplock_type, fsp_str_dbg(fsp)));
+
+	return NT_STATUS_OK;
 }
 
 static bool request_timed_out(struct timeval request_time,
@@ -2739,8 +2757,6 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
 		}
 	}
 
-	grant_fsp_oplock_type(fsp, lck, oplock_request);
-
 	/*
 	 * We have the share entry *locked*.....
 	 */
@@ -2800,9 +2816,18 @@ 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.
+		 *
+		 * Note that we check for stat open on the *open_access_mask*,
+		 * i.e. the access mask we actually used to do the open,
+		 * not the one the client asked for (which is in
+		 * fsp->access_mask). This is due to the fact that
+		 * FILE_OVERWRITE and FILE_OVERWRITE_IF add in O_TRUNC,
+		 * which adds FILE_WRITE_DATA to open_access_mask.
+		 */
 		if (is_stat_open(open_access_mask)) {
-			fsp->oplock_type = NO_OPLOCK;
+			oplock_request = NO_OPLOCK;
 		}
 	}
 
@@ -2824,21 +2849,11 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
 	 * Setup the oplock info in both the shared memory and
 	 * file structs.
 	 */
-
-	status = set_file_oplock(fsp);
+	status = grant_fsp_oplock_type(req, fsp, lck, oplock_request);
 	if (!NT_STATUS_IS_OK(status)) {
-		/*
-		 * Could not get the kernel oplock
-		 */
-		fsp->oplock_type = NO_OPLOCK;
-	}
-
-	if (!set_share_mode(lck, fsp, get_current_uid(conn),
-			    req ? req->mid : 0,
-			    fsp->oplock_type)) {
 		TALLOC_FREE(lck);
 		fd_close(fsp);
-		return NT_STATUS_NO_MEMORY;
+		return status;
 	}
 
 	/* Handle strange delete on close create semantics. */
-- 
1.9.1


From e43b5f28a7f7aae3fe8e0d9eb0d9bddcb471c62c Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Wed, 22 Oct 2014 17:53:01 -0700
Subject: [PATCH 3/5] s3:smbd: Don't set fsp->oplock_type before we've granted
 any oplocks.

It's not needed, and may lead to unexpected side effects.

grant_fsp_oplock_type() is the only place to touch this.

Signed-off-by: Jeremy Allison <jra at samba.org>
Reviewed-by: Stefan Metzmacher <metze at samba.org>
---
 source3/smbd/open.c | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index 945850c..28ab434 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -2423,9 +2423,6 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
 					      * the open is done. */
 	fsp->posix_open = posix_open;
 
-	/* Ensure no SAMBA_PRIVATE bits can be set. */
-	fsp->oplock_type = (oplock_request & ~SAMBA_PRIVATE_OPLOCK_MASK);
-
 	if (timeval_is_zero(&request_time)) {
 		request_time = fsp->open_time;
 	}
-- 
1.9.1


From b0dc1c83249542069966dec92d3cd87a3412d330 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 28 Oct 2014 15:27:09 -0700
Subject: [PATCH 4/5] s3:locking: convert brl_have_read field to brl_num_read.

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/locking/brlock.c | 123 +++++++++++++++++++++--------------------------
 source3/locking/proto.h  |   6 +--
 source3/smbd/open.c      |  15 ++----
 source3/smbd/oplock.c    | 115 +++++++++++++++++++++++++++-----------------
 source3/smbd/proto.h     |   2 +
 5 files changed, 135 insertions(+), 126 deletions(-)

diff --git a/source3/locking/brlock.c b/source3/locking/brlock.c
index 0bed9f9..6c73c72 100644
--- a/source3/locking/brlock.c
+++ b/source3/locking/brlock.c
@@ -47,7 +47,7 @@ struct byte_range_lock {
 	struct files_struct *fsp;
 	unsigned int num_locks;
 	bool modified;
-	bool have_read_oplocks;
+	uint32_t num_read_oplocks;
 	struct lock_struct *lock_data;
 	struct db_record *record;
 };
@@ -82,18 +82,18 @@ struct files_struct *brl_fsp(struct byte_range_lock *brl)
 	return brl->fsp;
 }
 
-bool brl_have_read_oplocks(const struct byte_range_lock *brl)
+uint32_t brl_num_read_oplocks(const struct byte_range_lock *brl)
 {
-	return brl->have_read_oplocks;
+	return brl->num_read_oplocks;
 }
 
-void brl_set_have_read_oplocks(struct byte_range_lock *brl,
-			       bool have_read_oplocks)
+void brl_set_num_read_oplocks(struct byte_range_lock *brl,
+			      uint32_t num_read_oplocks)
 {
-	DEBUG(10, ("Setting have_read_oplocks to %s\n",
-		   have_read_oplocks ? "true" : "false"));
+	DEBUG(10, ("Setting num_read_oplocks to %"PRIu32"\n",
+		   num_read_oplocks));
 	SMB_ASSERT(brl->record != NULL); /* otherwise we're readonly */
-	brl->have_read_oplocks = have_read_oplocks;
+	brl->num_read_oplocks = num_read_oplocks;
 	brl->modified = true;
 }
 
@@ -1850,7 +1850,6 @@ int brl_forall(void (*fn)(struct file_id id, struct server_id pid,
 
 static void byte_range_lock_flush(struct byte_range_lock *br_lck)
 {
-	size_t data_len;
 	unsigned i;
 	struct lock_struct *locks = br_lck->lock_data;
 
@@ -1874,15 +1873,7 @@ static void byte_range_lock_flush(struct byte_range_lock *br_lck)
 		}
 	}
 
-	data_len = br_lck->num_locks * sizeof(struct lock_struct);
-
-	if (br_lck->have_read_oplocks) {
-		data_len += 1;
-	}
-
-	DEBUG(10, ("data_len=%d\n", (int)data_len));
-
-	if (data_len == 0) {
+	if ((br_lck->num_locks == 0) && (br_lck->num_read_oplocks == 0)) {
 		/* No locks - delete this entry. */
 		NTSTATUS status = dbwrap_record_delete(br_lck->record);
 		if (!NT_STATUS_IS_OK(status)) {
@@ -1891,19 +1882,20 @@ static void byte_range_lock_flush(struct byte_range_lock *br_lck)
 			smb_panic("Could not delete byte range lock entry");
 		}
 	} else {
+		size_t lock_len, data_len;
 		TDB_DATA data;
 		NTSTATUS status;
 
+		lock_len = br_lck->num_locks * sizeof(struct lock_struct);
+		data_len = lock_len + sizeof(br_lck->num_read_oplocks);
+
 		data.dsize = data_len;
 		data.dptr = talloc_array(talloc_tos(), uint8_t, data_len);
 		SMB_ASSERT(data.dptr != NULL);
 
-		memcpy(data.dptr, br_lck->lock_data,
-		       br_lck->num_locks * sizeof(struct lock_struct));
-
-		if (br_lck->have_read_oplocks) {
-			data.dptr[data_len-1] = 1;
-		}
+		memcpy(data.dptr, br_lck->lock_data, lock_len);
+		memcpy(data.dptr + lock_len, &br_lck->num_read_oplocks,
+		       sizeof(br_lck->num_read_oplocks));
 
 		status = dbwrap_record_store(br_lck->record, data, TDB_REPLACE);
 		TALLOC_FREE(data.dptr);
@@ -1926,6 +1918,32 @@ static int byte_range_lock_destructor(struct byte_range_lock *br_lck)
 	return 0;
 }
 
+static bool brl_parse_data(struct byte_range_lock *br_lck, TDB_DATA data)
+{
+	size_t data_len;
+
+	if (data.dsize == 0) {
+		return true;
+	}
+	if (data.dsize % sizeof(struct lock_struct) !=
+	    sizeof(br_lck->num_read_oplocks)) {
+		DEBUG(1, ("Invalid data size: %u\n", (unsigned)data.dsize));
+		return false;
+	}
+
+	br_lck->num_locks = data.dsize / sizeof(struct lock_struct);
+	data_len = br_lck->num_locks * sizeof(struct lock_struct);
+
+	br_lck->lock_data = talloc_memdup(br_lck, data.dptr, data_len);
+	if (br_lck->lock_data == NULL) {
+		DEBUG(1, ("talloc_memdup failed\n"));
+		return false;
+	}
+	memcpy(&br_lck->num_read_oplocks, data.dptr + data_len,
+	       sizeof(br_lck->num_read_oplocks));
+	return true;
+}
+
 /*******************************************************************
  Fetch a set of byte range lock data from the database.
  Leave the record locked.
@@ -1935,16 +1953,14 @@ static int byte_range_lock_destructor(struct byte_range_lock *br_lck)
 struct byte_range_lock *brl_get_locks(TALLOC_CTX *mem_ctx, files_struct *fsp)
 {
 	TDB_DATA key, data;
-	struct byte_range_lock *br_lck = talloc(mem_ctx, struct byte_range_lock);
+	struct byte_range_lock *br_lck;
 
+	br_lck = talloc_zero(mem_ctx, struct byte_range_lock);
 	if (br_lck == NULL) {
 		return NULL;
 	}
 
 	br_lck->fsp = fsp;
-	br_lck->num_locks = 0;
-	br_lck->have_read_oplocks = false;
-	br_lck->modified = False;
 
 	key.dptr = (uint8 *)&fsp->file_id;
 	key.dsize = sizeof(struct file_id);
@@ -1959,30 +1975,12 @@ struct byte_range_lock *brl_get_locks(TALLOC_CTX *mem_ctx, files_struct *fsp)
 
 	data = dbwrap_record_get_value(br_lck->record);
 
-	br_lck->lock_data = NULL;
-
-	talloc_set_destructor(br_lck, byte_range_lock_destructor);
-
-	br_lck->num_locks = data.dsize / sizeof(struct lock_struct);
-
-	if (br_lck->num_locks != 0) {
-		br_lck->lock_data = talloc_array(
-			br_lck, struct lock_struct, br_lck->num_locks);
-		if (br_lck->lock_data == NULL) {
-			DEBUG(0, ("malloc failed\n"));
-			TALLOC_FREE(br_lck);
-			return NULL;
-		}
-
-		memcpy(br_lck->lock_data, data.dptr,
-		       talloc_get_size(br_lck->lock_data));
+	if (!brl_parse_data(br_lck, data)) {
+		TALLOC_FREE(br_lck);
+		return NULL;
 	}
 
-	DEBUG(10, ("data.dsize=%d\n", (int)data.dsize));
-
-	if ((data.dsize % sizeof(struct lock_struct)) == 1) {
-		br_lck->have_read_oplocks = (data.dptr[data.dsize-1] == 1);
-	}
+	talloc_set_destructor(br_lck, byte_range_lock_destructor);
 
 	if (DEBUGLEVEL >= 10) {
 		unsigned int i;
@@ -2008,28 +2006,19 @@ static void brl_get_locks_readonly_parser(TDB_DATA key, TDB_DATA data,
 {
 	struct brl_get_locks_readonly_state *state =
 		(struct brl_get_locks_readonly_state *)private_data;
-	struct byte_range_lock *br_lock;
+	struct byte_range_lock *br_lck;
 
-	br_lock = talloc_pooled_object(
+	br_lck = talloc_pooled_object(
 		state->mem_ctx, struct byte_range_lock, 1, data.dsize);
-	if (br_lock == NULL) {
+	if (br_lck == NULL) {
 		*state->br_lock = NULL;
 		return;
 	}
-	br_lock->lock_data = (struct lock_struct *)talloc_memdup(
-		br_lock, data.dptr, data.dsize);
-	br_lock->num_locks = data.dsize / sizeof(struct lock_struct);
-
-	if ((data.dsize % sizeof(struct lock_struct)) == 1) {
-		br_lock->have_read_oplocks = (data.dptr[data.dsize-1] == 1);
-	} else {
-		br_lock->have_read_oplocks = false;
+	if (!brl_parse_data(br_lck, data)) {
+		*state->br_lock = NULL;
+		return;
 	}
-
-	DEBUG(10, ("Got %d bytes, have_read_oplocks: %s\n", (int)data.dsize,
-		   br_lock->have_read_oplocks ? "true" : "false"));
-
-	*state->br_lock = br_lock;
+	*state->br_lock = br_lck;
 }
 
 struct byte_range_lock *brl_get_locks_readonly(files_struct *fsp)
@@ -2072,7 +2061,7 @@ struct byte_range_lock *brl_get_locks_readonly(files_struct *fsp)
 			return NULL;
 		}
 
-		br_lock->have_read_oplocks = false;
+		br_lock->num_read_oplocks = 0;
 		br_lock->num_locks = 0;
 		br_lock->lock_data = NULL;
 
diff --git a/source3/locking/proto.h b/source3/locking/proto.h
index 44f3ba1..8eccff8 100644
--- a/source3/locking/proto.h
+++ b/source3/locking/proto.h
@@ -30,9 +30,9 @@ void brl_shutdown(void);
 
 unsigned int brl_num_locks(const struct byte_range_lock *brl);
 struct files_struct *brl_fsp(struct byte_range_lock *brl);
-bool brl_have_read_oplocks(const struct byte_range_lock *brl);
-void brl_set_have_read_oplocks(struct byte_range_lock *brl,
-			       bool have_read_oplocks);
+uint32_t brl_num_read_oplocks(const struct byte_range_lock *brl);
+void brl_set_num_read_oplocks(struct byte_range_lock *brl,
+			      uint32_t num_read_oplocks);
 
 NTSTATUS brl_lock_windows_default(struct byte_range_lock *br_lck,
 		struct lock_struct *plock,
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index 28ab434..1952823 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1573,17 +1573,10 @@ static NTSTATUS grant_fsp_oplock_type(struct smb_request *req,
 		return NT_STATUS_NO_MEMORY;
 	}
 
-	if (fsp->oplock_type == LEVEL_II_OPLOCK && !got_level2_oplock) {
-		/*
-		 * We're the first level2 oplock. Indicate that in brlock.tdb.
-		 */
-		struct byte_range_lock *brl;
-
-		brl = brl_get_locks(talloc_tos(), fsp);
-		if (brl != NULL) {
-			brl_set_have_read_oplocks(brl, true);
-			TALLOC_FREE(brl);
-		}
+	ok = update_num_read_oplocks(fsp, lck);
+	if (!ok) {
+		del_share_mode(lck, fsp);
+		return NT_STATUS_INTERNAL_ERROR;
 	}
 
 	DEBUG(10,("grant_fsp_oplock_type: oplock type 0x%x on file %s\n",
diff --git a/source3/smbd/oplock.c b/source3/smbd/oplock.c
index 7935092..aa99f68 100644
--- a/source3/smbd/oplock.c
+++ b/source3/smbd/oplock.c
@@ -148,6 +148,53 @@ static void downgrade_file_oplock(files_struct *fsp)
 	TALLOC_FREE(fsp->oplock_timeout);
 }
 
+bool update_num_read_oplocks(files_struct *fsp, struct share_mode_lock *lck)
+{
+	struct share_mode_data *d = lck->data;
+	struct byte_range_lock *br_lck;
+	uint32_t num_read_oplocks = 0;
+	uint32_t i;
+
+	if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) {
+		/*
+		 * If we're the only one, we don't need a brlock entry
+		 */
+		SMB_ASSERT(d->num_share_modes == 1);
+		SMB_ASSERT(EXCLUSIVE_OPLOCK_TYPE(d->share_modes[0].op_type));
+		return true;
+	}
+
+	for (i=0; i<d->num_share_modes; i++) {
+		struct share_mode_entry *e = &d->share_modes[i];
+
+		if (EXCLUSIVE_OPLOCK_TYPE(e->op_type)) {
+			num_read_oplocks += 1;
+			continue;
+		}
+
+		if (LEVEL_II_OPLOCK_TYPE(e->op_type)) {
+			num_read_oplocks += 1;
+			continue;
+		}
+	}
+
+	br_lck = brl_get_locks_readonly(fsp);
+	if (br_lck == NULL) {
+		return false;
+	}
+	if (brl_num_read_oplocks(br_lck) == num_read_oplocks) {
+		return true;
+	}
+
+	br_lck = brl_get_locks(talloc_tos(), fsp);
+	if (br_lck == NULL) {
+		return false;
+	}
+	brl_set_num_read_oplocks(br_lck, num_read_oplocks);
+	TALLOC_FREE(br_lck);
+	return true;
+}
+
 /****************************************************************************
  Remove a file oplock. Copes with level II and exclusive.
  Locks then unlocks the share mode lock. Client can decide to go directly
@@ -170,44 +217,6 @@ bool remove_oplock(files_struct *fsp)
 		return False;
 	}
 
-	if (fsp->oplock_type == LEVEL_II_OPLOCK) {
-
-		/*
-		 * If we're the only LEVEL_II holder, we have to remove the
-		 * have_read_oplocks from the brlock entry
-		 */
-
-		struct share_mode_data *data = lck->data;
-		uint32_t i, num_level2;
-
-		num_level2 = 0;
-		for (i=0; i<data->num_share_modes; i++) {
-			if (data->share_modes[i].op_type == LEVEL_II_OPLOCK) {
-				num_level2 += 1;
-			}
-			if (num_level2 > 1) {
-				/*
-				 * No need to count them all...
-				 */
-				break;
-			}
-		}
-
-		if (num_level2 == 1) {
-			/*
-			 * That's only us. We are dropping that level2 oplock,
-			 * so remove the brlock flag.
-			 */
-			struct byte_range_lock *brl;
-
-			brl = brl_get_locks(talloc_tos(), fsp);
-			if (brl) {
-				brl_set_have_read_oplocks(brl, false);
-				TALLOC_FREE(brl);
-			}
-		}
-	}
-
 	ret = remove_share_oplock(lck, fsp);
 	if (!ret) {
 		DEBUG(0,("remove_oplock: failed to remove share oplock for "
@@ -216,6 +225,15 @@ bool remove_oplock(files_struct *fsp)
 			 file_id_string_tos(&fsp->file_id)));
 	}
 	release_file_oplock(fsp);
+
+	ret = update_num_read_oplocks(fsp, lck);
+	if (!ret) {
+		DEBUG(0, ("%s: update_num_read_oplocks failed for "
+			 "file %s, %s, %s\n",
+			  __func__, fsp_str_dbg(fsp), fsp_fnum_dbg(fsp),
+			 file_id_string_tos(&fsp->file_id)));
+	}
+
 	TALLOC_FREE(lck);
 	return ret;
 }
@@ -227,7 +245,6 @@ bool downgrade_oplock(files_struct *fsp)
 {
 	bool ret;
 	struct share_mode_lock *lck;
-	struct byte_range_lock *brl;
 
 	DEBUG(10, ("downgrade_oplock called for %s\n",
 		   fsp_str_dbg(fsp)));
@@ -245,13 +262,14 @@ bool downgrade_oplock(files_struct *fsp)
 			 fsp_str_dbg(fsp), fsp_fnum_dbg(fsp),
 			 file_id_string_tos(&fsp->file_id)));
 	}
-
 	downgrade_file_oplock(fsp);
 
-	brl = brl_get_locks(talloc_tos(), fsp);
-	if (brl != NULL) {
-		brl_set_have_read_oplocks(brl, true);
-		TALLOC_FREE(brl);
+	ret = update_num_read_oplocks(fsp, lck);
+	if (!ret) {
+		DEBUG(0, ("%s: update_num_read_oplocks failed for "
+			 "file %s, %s, %s\n",
+			  __func__, fsp_str_dbg(fsp), fsp_fnum_dbg(fsp),
+			 file_id_string_tos(&fsp->file_id)));
 	}
 
 	TALLOC_FREE(lck);
@@ -596,6 +614,7 @@ static void contend_level2_oplocks_begin_default(files_struct *fsp,
 	struct tevent_immediate *im;
 	struct break_to_none_state *state;
 	struct byte_range_lock *brl;
+	uint32_t num_read_oplocks = 0;
 
 	/*
 	 * If this file is level II oplocked then we need
@@ -613,7 +632,13 @@ static void contend_level2_oplocks_begin_default(files_struct *fsp,
 	}
 
 	brl = brl_get_locks_readonly(fsp);
-	if ((brl != NULL) && !brl_have_read_oplocks(brl)) {
+	if (brl != NULL) {
+		num_read_oplocks = brl_num_read_oplocks(brl);
+	}
+
+	DEBUG(10, ("num_read_oplocks = %"PRIu32"\n", num_read_oplocks));
+
+	if (num_read_oplocks == 0) {
 		DEBUG(10, ("No read oplocks around\n"));
 		return;
 	}
diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
index 68c2da2..1080895 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -647,6 +647,8 @@ NTSTATUS get_relative_fid_filename(connection_struct *conn,
 
 /* The following definitions come from smbd/oplock.c  */
 
+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);
 NTSTATUS set_file_oplock(files_struct *fsp);
 bool remove_oplock(files_struct *fsp);
-- 
1.9.1


From 0f0e3c937ef530f3f81fa79ba6bc53eac530a652 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 23 Sep 2014 18:49:46 +0200
Subject: [PATCH 5/5] s3:smb2_break: First test for
 NT_STATUS_INVALID_OPLOCK_PROTOCOL, then for in_oplock_level being reasonable

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_break.c | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/source3/smbd/smb2_break.c b/source3/smbd/smb2_break.c
index 5c079ec..4492456 100644
--- a/source3/smbd/smb2_break.c
+++ b/source3/smbd/smb2_break.c
@@ -52,11 +52,6 @@ NTSTATUS smbd_smb2_request_process_break(struct smbd_smb2_request *req)
 
 	in_oplock_level		= CVAL(inbody, 0x02);
 
-	if (in_oplock_level != SMB2_OPLOCK_LEVEL_NONE &&
-			in_oplock_level != SMB2_OPLOCK_LEVEL_II) {
-		return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
-	}
-
 	/* 0x03 1 bytes reserved */
 	/* 0x04 4 bytes reserved */
 	in_file_id_persistent		= BVAL(inbody, 0x08);
@@ -67,6 +62,17 @@ NTSTATUS smbd_smb2_request_process_break(struct smbd_smb2_request *req)
 		return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
 	}
 
+	/* Are we awaiting a break message ? */
+	if (in_fsp->oplock_timeout == NULL) {
+		return smbd_smb2_request_error(
+			req, NT_STATUS_INVALID_OPLOCK_PROTOCOL);
+	}
+
+	if (in_oplock_level != SMB2_OPLOCK_LEVEL_NONE &&
+	    in_oplock_level != SMB2_OPLOCK_LEVEL_II) {
+		return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
+	}
+
 	subreq = smbd_smb2_oplock_break_send(req, req->sconn->ev_ctx,
 					     req, in_fsp, in_oplock_level);
 	if (subreq == NULL) {
@@ -177,12 +183,6 @@ static struct tevent_req *smbd_smb2_oplock_break_send(TALLOC_CTX *mem_ctx,
 		fsp_str_dbg(fsp),
 		fsp_fnum_dbg(fsp)));
 
-	/* Are we awaiting a break message ? */
-	if (fsp->oplock_timeout == NULL) {
-		tevent_req_nterror(req, NT_STATUS_INVALID_OPLOCK_PROTOCOL);
-		return tevent_req_post(req, ev);
-	}
-
 	if ((fsp->sent_oplock_break == BREAK_TO_NONE_SENT) ||
 			(break_to_none)) {
 		result = remove_oplock(fsp);
-- 
1.9.1

-------------- next part --------------
From 2e4cd4777cf2af03ff8c981810afe556a374d255 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 01/16] s3: smbd: Implementation of SMB2.1 leases.

Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
 source3/include/smb.h             |   1 +
 source3/librpc/idl/open_files.idl |  30 +++
 source3/librpc/idl/wscript_build  |   1 +
 source3/librpc/wscript_build      |   7 +-
 source3/locking/locking.c         | 141 +++++++++-
 source3/locking/proto.h           |  11 +-
 source3/smbd/durable.c            |  28 +-
 source3/smbd/files.c              |  34 +++
 source3/smbd/globals.h            |  10 +-
 source3/smbd/open.c               | 543 ++++++++++++++++++++++++++++++--------
 source3/smbd/oplock.c             | 291 +++++++++++++++-----
 source3/smbd/proto.h              |  12 +
 source3/smbd/server.c             |   5 +
 source3/smbd/smb2_break.c         | 194 +++++++++++++-
 source3/smbd/smb2_create.c        | 151 ++++++++++-
 source3/smbd/smb2_server.c        |  25 ++
 source3/utils/status.c            |   2 +
 source3/wscript_build             |   6 +
 18 files changed, 1292 insertions(+), 200 deletions(-)

diff --git a/source3/include/smb.h b/source3/include/smb.h
index aab4ff5..7bace88 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..48e6bbe 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)
@@ -14,6 +16,7 @@ interface open_files
 		server_id	pid;
 		hyper		op_mid;
 		uint16		op_type;
+		uint32		lease_idx;
 		uint32		access_mask;
 		uint32		share_access;
 		uint32		private_options;
@@ -31,6 +34,31 @@ interface open_files
 		[skip] boolean8	stale;
 	} share_mode_entry;
 
+	typedef [public,bitmap8bit] bitmap {
+		SHARE_MODE_NO_CACHING = 0x00,
+		SHARE_MODE_READ_CACHING = 0x01,
+		SHARE_MODE_HANDLE_CACHING = 0x02,
+		SHARE_MODE_WRITE_CACHING = 0x04
+	} share_mode_caching;
+
+	typedef [public,flag(NDR_PAHEX)] struct {
+		GUID			client_guid;
+		smb2_lease_key		lease_key;
+		share_mode_caching      current_state;
+		/*
+		 * breaking_to_state indicates to which level
+		 * the current state is broken when a conflicting
+		 * request is processed. The calculation is as follows:
+		 *
+		 *   breaking_to_state = current_state;
+		 *   breaking_to_state &= ~(remove_state)
+		 *   breaking_to_state &= allowed_shared_state
+		 */
+		share_mode_caching      breaking_to_state;
+		boolean8                breaking;
+		uint16			epoch;
+	} share_mode_oplock;
+
 	typedef [public] struct {
 		uint32		name_hash;
 		security_token *delete_nt_token;
@@ -43,6 +71,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_oplock leases[];
 		uint32 num_delete_tokens;
 		[size_is(num_delete_tokens)] delete_token delete_tokens[];
 		timespec old_write_time;
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..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',
@@ -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/locking.c b/source3/locking/locking.c
index a320068..170fa1d 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
@@ -608,6 +609,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");
@@ -694,7 +696,8 @@ 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)
+		    uid_t uid, uint64_t mid, uint16 op_type,
+		    uint32_t lease_idx)
 {
 	struct share_mode_data *d = lck->data;
 	struct share_mode_entry *tmp, *e;
@@ -716,6 +719,7 @@ 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->time.tv_sec = fsp->open_time.tv_sec;
 	e->time.tv_usec = fsp->open_time.tv_usec;
 	e->id = fsp->file_id;
@@ -816,16 +820,70 @@ 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;
+	uint16_t op_type;
+	uint32_t lease_idx;
+	uint32_t i;
 
 	e = find_share_mode_entry(lck, fsp);
 	if (e == NULL) {
 		return False;
 	}
 
+	op_type = e->op_type;
 	e->op_type = NO_OPLOCK;
-	lck->data->modified = True;
-	return True;
+
+	d->modified = True;
+
+	if (op_type != LEASE_OPLOCK) {
+		return true;
+	}
+
+	/*
+	 * 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].lease_idx == lease_idx) {
+			break;
+		}
+	}
+	if (i < d->num_share_modes) {
+		/*
+		 * Found another one
+		 */
+		return true;
+	}
+
+	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;
+		}
+	}
+
+	{
+		NTSTATUS status;
+
+		status = leases_db_del(
+			&fsp->conn->sconn->client->connections->smb2.client.guid,
+			&fsp->lease->lease.lease_key);
+
+		DEBUG(10, ("%s: leases_db_del returned %s\n", __func__,
+			   nt_errstr(status)));
+	}
+
+	return true;
 }
 
 /*******************************************************************
@@ -846,6 +904,83 @@ bool downgrade_share_oplock(struct share_mode_lock *lck, files_struct *fsp)
 	return True;
 }
 
+/*
+ * "key" has been broken, it is referenced somewhere in "lck". If "fsp"
+ * applies to it, reset fsp->sent_oplock_break.
+ */
+
+bool fsp_lease_broken(struct share_mode_lock *lck,
+		      struct file_id lck_id,
+		      const struct smb2_lease_key *key,
+		      files_struct *fsp, uint32_t new_lease_state)
+{
+	struct share_mode_data *d = lck->data;
+	struct share_mode_entry *e;
+
+	if (!file_id_equal(&fsp->file_id, &lck_id)) {
+		return false;
+	}
+	if (fsp->oplock_type != LEASE_OPLOCK) {
+		return false;
+	}
+
+	e = find_share_mode_entry(lck, fsp);
+	if (e == NULL) {
+		DEBUG(1, ("downgrade_lease_fsps: Could not find share mode "
+			  "entry\n"));
+		return false;
+	}
+
+	if (!smb2_lease_key_equal(key, &d->leases[e->lease_idx].lease_key)) {
+		return false;
+	}
+	fsp->sent_oplock_break = NO_BREAK_SENT;
+	TALLOC_FREE(fsp->oplock_timeout);
+	fsp->lease->lease.lease_state = new_lease_state;
+	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_data *d = lck->data;
+	struct share_mode_oplock *l;
+	uint32_t i;
+
+	for (i=0; i<d->num_leases; i++) {
+		if (smb2_lease_key_equal(key, &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];
+
+	/*
+	 * Can't upgrade anything: l->current_state must be a strict bitwise
+	 * superset of new_lease_state
+	 */
+
+	if ((new_lease_state & l->current_state) != new_lease_state) {
+		DEBUG(10, ("Attempt to upgrade from %d to %d\n",
+			   (int)l->current_state, (int)new_lease_state));
+		return NT_STATUS_REQUEST_NOT_ACCEPTED;
+	}
+
+	l->current_state = new_lease_state;
+	l->breaking_to_state = 0;
+	l->breaking = 0;
+
+	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 8eccff8..94e9b8f 100644
--- a/source3/locking/proto.h
+++ b/source3/locking/proto.h
@@ -167,13 +167,22 @@ void get_file_infos(struct file_id id,
 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);
+		    uid_t uid, uint64_t mid, uint16 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,
 				  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);
+bool fsp_lease_broken(struct share_mode_lock *lck,
+		      struct file_id lck_id,
+		      const struct smb2_lease_key *key,
+		      files_struct *fsp, uint32_t new_lease_state);
+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);
 bool get_delete_on_close_token(struct share_mode_lock *lck,
 				uint32_t name_hash,
 				const struct security_token **pp_nt_tok,
diff --git a/source3/smbd/durable.c b/source3/smbd/durable.c
index 9489cf1..660865d 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;
 	}
 
@@ -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_oplock *o = &lck->data->leases[e->lease_idx];
+		struct smb2_lease_key key;
+
+		key.data[0] = o->lease_key.data[0];
+		key.data[1] = o->lease_key.data[1];
+
+		fsp->lease = find_fsp_lease(fsp, &key);
+
+		if (fsp->lease != NULL) {
+			fsp->lease->ref_count += 1;
+		} else {
+			fsp->lease = talloc_zero(fsp->conn->sconn,
+						 struct fsp_lease);
+			if (fsp->lease == NULL) {
+				TALLOC_FREE(lck);
+				fsp_free(fsp);
+				return NT_STATUS_NO_MEMORY;
+			}
+			fsp->lease->ref_count = 1;
+			fsp->lease->lease.lease_key = key;
+			fsp->lease->lease.lease_state = o->current_state;
+			fsp->lease->lease.lease_epoch = o->epoch;
+		}
+	}
+
 	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 a9e8357..13d1138 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.
 ****************************************************************************/
@@ -472,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
@@ -740,3 +766,11 @@ NTSTATUS fsp_set_smb_fname(struct files_struct *fsp,
 			smb_fname_str_dbg(fsp->fsp_name),
 			&fsp->name_hash);
 }
+
+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/globals.h b/source3/smbd/globals.h
index 36e7f0f..f297ff7 100644
--- a/source3/smbd/globals.h
+++ b/source3/smbd/globals.h
@@ -250,6 +250,14 @@ 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,
+				    struct smbXsrv_session *session,
+				    struct smbXsrv_tcon *tcon,
+				    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,
@@ -298,7 +306,7 @@ 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_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 1952823..7ac8fd8 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -35,6 +35,12 @@
 #include "serverid.h"
 #include "messages.h"
 #include "source3/lib/dbwrap/dbwrap_watch.h"
+#include "locking/leases_db.h"
+
+static bool is_same_lease(const struct share_mode_data *d,
+			  const struct share_mode_entry *e,
+			  const struct smb2_lease *lease);
+
 
 extern const struct generic_mapping file_generic_mapping;
 
@@ -1257,6 +1263,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,
@@ -1376,31 +1386,28 @@ 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)
 {
 	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;
+	bool have_broken = false;
+	bool will_overwrite;
 
-	if ((oplock_request & INTERNAL_OPEN_ONLY) || is_stat_open(fsp->access_mask)) {
+	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) {
 		/*
@@ -1408,24 +1415,31 @@ static bool delay_for_oplock(files_struct *fsp,
 		 */
 		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;
+
+	if (have_sharing_violation) {
+		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 (!(e_lease_type & SMB2_LEASE_HANDLE)) {
+				continue;
+			}
+			if (is_same_lease(d, e, lease)) {
+				continue;
+			}
+			if (share_mode_stale_pid(d, i)) {
+				continue;
+			}
+			send_break_message(fsp->conn->sconn->msg_ctx, e,
+					   e_lease_type & ~SMB2_LEASE_HANDLE);
+			have_broken = true;
+		}
 	}
 
-	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 (have_broken) {
+		return true;
+	}
+	if (have_sharing_violation) {
 		return false;
 	}
 
@@ -1433,50 +1447,67 @@ 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];
+		uint32_t e_lease_type = get_lease_type(d, e);
+
+		DEBUG(10, ("entry %u: e_lease_type %u, will_overwrite: %u\n",
+			   (unsigned)i, (unsigned)e_lease_type,
+			   (unsigned)will_overwrite));
+
+		if (e_lease_type & SMB2_LEASE_WRITE) {
+			uint32_t break_to;
+
+			if (share_mode_stale_pid(d, i)) {
+				return false;
+			}
+			if ((e->op_type == LEASE_OPLOCK) &&
+			    (lease != NULL) &&
+			    smb2_lease_key_equal(
+				    &lease->lease_key,
+				    &d->leases[e->lease_idx].lease_key)) {
+				return false;
+			}
+
+			break_to = e_lease_type & ~SMB2_LEASE_WRITE;
+			if (will_overwrite) {
+				/*
+				 * There's no H only lease that we could break
+				 * to
+				 */
+				break_to = SMB2_LEASE_NONE;
+			}
+
+			DEBUG(10, ("breaking SMB2_LEASE_WRITE to %d\n",
+				   (int)break_to));
+			send_break_message(fsp->conn->sconn->msg_ctx, e,
+					   break_to);
+			return true;
 		}
-		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 (will_overwrite && (e_lease_type & SMB2_LEASE_READ)) {
+			if (share_mode_stale_pid(d, i)) {
+				continue;
+			}
+			DEBUG(10, ("breaking SMB2_LEASE_READ\n"));
+			send_break_message(fsp->conn->sconn->msg_ctx, e,
+					   SMB2_LEASE_NONE);
+			/*
+			 * This is an async break. No need to wait for a
+			 * response.
+			 */
+			continue;
 		}
-		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;
 	}
 
-	send_break_message(fsp->conn->sconn->msg_ctx, entry, break_to);
-	return true;
+	return have_broken;
 }
 
 static bool file_has_brlocks(files_struct *fsp)
@@ -1490,74 +1521,302 @@ static bool file_has_brlocks(files_struct *fsp)
 	return (brl_num_locks(br_lck) > 0);
 }
 
-static NTSTATUS grant_fsp_oplock_type(struct smb_request *req,
-				      struct files_struct *fsp,
-				      struct share_mode_lock *lck,
-				      int oplock_request)
+static int find_share_mode_oplock(struct share_mode_data *d,
+				  const struct GUID *client_guid,
+				  const struct smb2_lease_key *key)
 {
-	bool allow_level2 = (global_client_caps & CAP_LEVEL_II_OPLOCKS) &&
-		            lp_level2_oplocks(SNUM(fsp->conn));
-	bool got_level2_oplock, got_a_none_oplock;
 	uint32_t i;
-	bool ok;
+
+	for (i=0; i<d->num_leases; i++) {
+		struct share_mode_oplock *l = &d->leases[i];
+		if (GUID_equal(client_guid, &l->client_guid) &&
+		    smb2_lease_key_equal(key, &l->lease_key)) {
+			return i;
+		}
+	}
+	return -1;
+}
+
+static bool is_same_lease(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_key_equal(&d->leases[e->lease_idx].lease_key,
+				    &lease->lease_key);
+}
+
+struct fsp_lease *find_fsp_lease(files_struct *new_fsp,
+				 const struct smb2_lease_key *key)
+{
+	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)) {
+			return fsp->lease;
+		}
+	}
+
+	return NULL;
+}
+
+static NTSTATUS grant_fsp_lease(files_struct *fsp, struct share_mode_data *d,
+				const struct smb2_lease *lease,
+				uint32_t *p_lease_idx,
+				uint32_t granted)
+{
+	const struct GUID *client_guid;
+	struct share_mode_oplock *o;
+	struct share_mode_oplock *tmp;
 	NTSTATUS status;
+	int idx;
+
+
+	/*
+	 * TODO: in future we can have multiple connections...
+	 */
+	client_guid = &fsp->conn->sconn->client->connections->smb2.client.guid;
+
+	idx = find_share_mode_oplock(d, client_guid, &lease->lease_key);
+
+	if (idx != -1) {
+
+		bool do_upgrade;
+		uint32_t existing, requested;
+
+		fsp->lease = find_fsp_lease(fsp, &lease->lease_key);
+		if (fsp->lease == NULL) {
+			DEBUG(1, ("Did not find existing lease for file %s\n",
+				  fsp_str_dbg(fsp)));
+			return NT_STATUS_INTERNAL_ERROR;
+		}
+		fsp->lease->ref_count += 1;
+
+		*p_lease_idx = idx;
+		o = &d->leases[idx];
+
+		/*
+		 * Upgrade only if the requested lease is a strict upgrade.
+		 */
+		existing = o->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 other leases don't prevent what was asked
+		 * for.
+		 */
+		do_upgrade &= (granted == requested);
+
+		DEBUG(10, ("existing=%"PRIu32", requested=%"PRIu32", "
+			   "granted=%"PRIu32", do_upgrade=%d\n",
+			   existing, requested, granted, (int)do_upgrade));
+
+		if (do_upgrade) {
+			o->current_state = granted;
+		}
+		fsp->lease->lease.lease_state = o->current_state;
+		return NT_STATUS_OK;
+	}
+
+	/*
+	 * Create new lease
+	 */
+
+	tmp = talloc_realloc(d, d->leases, struct share_mode_oplock,
+			     d->num_leases+1);
+	if (tmp == NULL) {
+		/*
+		 * See [MS-SMB2]
+		 */
+		return NT_STATUS_INSUFFICIENT_RESOURCES;
+	}
+	d->leases = tmp;
+
+	fsp->lease = talloc(fsp->conn->sconn, struct fsp_lease);
+	if (fsp->lease == NULL) {
+		return NT_STATUS_INSUFFICIENT_RESOURCES;
+	}
+	fsp->lease->ref_count = 1;
+	fsp->lease->lease = *lease;
+	fsp->lease->lease.lease_state = granted;
+	fsp->lease->lease.lease_epoch += 1;
+
+	*p_lease_idx = d->num_leases;
 
-	/* 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);
+	d->leases[d->num_leases] = (struct share_mode_oplock) {
+		.client_guid = *client_guid,
+		.lease_key = lease->lease_key,
+		.epoch = lease->lease_epoch,
+		.current_state = granted,
+	};
 
-	if (fsp->oplock_type == NO_OPLOCK) {
-		goto type_selected;
+	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 NTSTATUS grant_fsp_oplock_type(struct smb_request *req, files_struct *fsp,
+				      struct share_mode_lock *lck,
+				      int oplock_request,
+				      uint32_t create_disposition,
+				      struct smb2_lease *lease)
+{
+	struct share_mode_data *d = lck->data;
+	bool got_handle_lease, got_oplock;
+	uint32_t i;
+	uint32_t granted;
+	uint32_t lease_idx = UINT32_MAX;
+	NTSTATUS status;
+	bool ret;
+
 	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) {
+		granted = lease->lease_state;
+
+		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;
+	got_handle_lease = false;
+	got_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(d, e, lease) &&
+		    !share_mode_stale_pid(d, i)) {
+			/*
+			 * Can grant only one writer
+			 */
+			granted &= ~SMB2_LEASE_WRITE;
+		}
+
+		if ((e_lease_type & SMB2_LEASE_HANDLE) && !got_handle_lease &&
+		    !share_mode_stale_pid(d, i)) {
+			got_handle_lease = true;
 		}
-		if (op_type == NO_OPLOCK) {
-			got_a_none_oplock = 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 (oplock_request == LEASE_OPLOCK) {
 
-	if (got_level2_oplock || got_a_none_oplock) {
-		if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) {
+		fsp->oplock_type = LEASE_OPLOCK;
+
+		if (got_oplock) {
+			granted &= SMB2_LEASE_READ;
+		}
+
+		status = grant_fsp_lease(fsp, lck->data, lease, &lease_idx,
+					 granted);
+		if (!NT_STATUS_IS_OK(status)) {
+			return status;
+
+		}
+		lease->lease_state = d->leases[lease_idx].current_state;
+		DEBUG(10, ("lease_state=%d\n", lease->lease_state));
+	} else {
+		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;
 		}
-	}
+		if (fsp->oplock_type == LEVEL_II_OPLOCK) {
+			bool allow_level2 =
+				(global_client_caps & CAP_LEVEL_II_OPLOCKS) &&
+				lp_level2_oplocks(SNUM(fsp->conn));
 
-	/*
-	 * 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 (!allow_level2) {
+				fsp->oplock_type = NO_OPLOCK;
+			}
+		}
+		if (got_handle_lease) {
+			fsp->oplock_type = NO_OPLOCK;
+		}
 	}
 
- type_selected:
+	DEBUG(10,("grant_fsp_oplock_type: oplock type 0x%x on file %s\n",
+		  fsp->oplock_type, fsp_str_dbg(fsp)));
+
 	status = set_file_oplock(fsp);
 	if (!NT_STATUS_IS_OK(status)) {
 		/*
@@ -1566,22 +1825,16 @@ static NTSTATUS grant_fsp_oplock_type(struct smb_request *req,
 		fsp->oplock_type = NO_OPLOCK;
 	}
 
-	ok = set_share_mode(lck, fsp, get_current_uid(fsp->conn),
+	if (!set_share_mode(lck, fsp, get_current_uid(fsp->conn),
 			    req ? req->mid : 0,
-			    fsp->oplock_type);
-	if (!ok) {
+			    fsp->oplock_type, lease_idx)) {
 		return NT_STATUS_NO_MEMORY;
 	}
-
-	ok = update_num_read_oplocks(fsp, lck);
-	if (!ok) {
+	ret = update_num_read_oplocks(fsp, lck);
+	if (!ret) {
 		del_share_mode(lck, fsp);
 		return NT_STATUS_INTERNAL_ERROR;
 	}
-
-	DEBUG(10,("grant_fsp_oplock_type: oplock type 0x%x on file %s\n",
-		  fsp->oplock_type, fsp_str_dbg(fsp)));
-
 	return NT_STATUS_OK;
 }
 
@@ -2474,8 +2727,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)) {
+			schedule_defer_open(lck, fsp->file_id, request_time,
+					    req);
 			TALLOC_FREE(lck);
 			DEBUG(10, ("Sent oplock break request to kernel "
 				   "oplock holder\n"));
@@ -2596,7 +2851,7 @@ 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)) {
 		schedule_defer_open(lck, fsp->file_id, request_time, req);
@@ -2807,7 +3062,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,
@@ -2817,7 +3072,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;
+			}
 		}
 	}
 
@@ -2839,7 +3098,9 @@ 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,
+				       create_disposition, lease);
 	if (!NT_STATUS_IS_OK(status)) {
 		TALLOC_FREE(lck);
 		fd_close(fsp);
@@ -3337,7 +3598,7 @@ static NTSTATUS open_directory(connection_struct *conn,
 	}
 
 	if (!set_share_mode(lck, fsp, get_current_uid(conn),
-			    req ? req->mid : 0, NO_OPLOCK)) {
+			    req ? req->mid : 0, NO_OPLOCK, UINT32_MAX)) {
 		TALLOC_FREE(lck);
 		fd_close(fsp);
 		file_free(req, fsp);
@@ -3822,6 +4083,48 @@ 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 {
+	const struct smb_filename *fname;
+	bool match;
+};
+
+static void lease_fname_match_parser(
+	struct file_id id, const char *filename, const char *stream_name,
+	void *private_data)
+{
+	struct lease_fname_match_state *state =
+		(struct lease_fname_match_state *)private_data;
+
+	state->match =
+		strequal(filename, state->fname->base_name) &&
+		strequal(stream_name, state->fname->stream_name);
+}
+
+static bool lease_fname_match(struct smbd_server_connection *sconn,
+			      struct smb2_lease_key *lease_key,
+			      const struct smb_filename *fname)
+{
+	struct lease_fname_match_state state =
+		{ .fname = fname, .match = true };
+	NTSTATUS status;
+
+	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 true;
+	}
+	return state.match;
+}
+
+/*
  * Wrapper around open_file_ntcreate and open_directory
  */
 
@@ -3877,6 +4180,12 @@ static NTSTATUS create_file_unixpath(connection_struct *conn,
 		oplock_request |= INTERNAL_OPEN_ONLY;
 	}
 
+	if ((lease != NULL) &&
+	    !lease_fname_match(req->sconn, &lease->lease_key, smb_fname)) {
+		status = NT_STATUS_INVALID_PARAMETER;
+		goto fail;
+	}
+
 	if ((conn->fs_capabilities & FILE_NAMED_STREAMS)
 	    && (access_mask & DELETE_ACCESS)
 	    && !is_ntfs_stream_smb_fname(smb_fname)) {
diff --git a/source3/smbd/oplock.c b/source3/smbd/oplock.c
index aa99f68..0a7ac17 100644
--- a/source3/smbd/oplock.c
+++ b/source3/smbd/oplock.c
@@ -116,8 +116,6 @@ static void release_file_oplock(files_struct *fsp)
 
 	flush_write_cache(fsp, SAMBA_OPLOCK_RELEASE_FLUSH);
 	delete_write_cache(fsp);
-
-	TALLOC_FREE(fsp->oplock_timeout);
 }
 
 /****************************************************************************
@@ -144,8 +142,36 @@ static void downgrade_file_oplock(files_struct *fsp)
 
 	flush_write_cache(fsp, SAMBA_OPLOCK_RELEASE_FLUSH);
 	delete_write_cache(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)
@@ -155,26 +181,12 @@ bool update_num_read_oplocks(files_struct *fsp, struct share_mode_lock *lck)
 	uint32_t num_read_oplocks = 0;
 	uint32_t i;
 
-	if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) {
-		/*
-		 * If we're the only one, we don't need a brlock entry
-		 */
-		SMB_ASSERT(d->num_share_modes == 1);
-		SMB_ASSERT(EXCLUSIVE_OPLOCK_TYPE(d->share_modes[0].op_type));
-		return true;
-	}
-
 	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;
 		}
 	}
 
@@ -225,6 +237,7 @@ bool remove_oplock(files_struct *fsp)
 			 file_id_string_tos(&fsp->file_id)));
 	}
 	release_file_oplock(fsp);
+	TALLOC_FREE(fsp->oplock_timeout);
 
 	ret = update_num_read_oplocks(fsp, lck);
 	if (!ret) {
@@ -262,13 +275,15 @@ bool downgrade_oplock(files_struct *fsp)
 			 fsp_str_dbg(fsp), fsp_fnum_dbg(fsp),
 			 file_id_string_tos(&fsp->file_id)));
 	}
+
 	downgrade_file_oplock(fsp);
+	TALLOC_FREE(fsp->oplock_timeout);
 
 	ret = update_num_read_oplocks(fsp, lck);
 	if (!ret) {
-		DEBUG(0, ("%s: update_num_read_oplocks failed for "
-			 "file %s, %s, %s\n",
-			  __func__, fsp_str_dbg(fsp), fsp_fnum_dbg(fsp),
+		DEBUG(0,("%s: failed to downgrade share oplock "
+			 "for file %s, %s, file_id %s\n",
+			 __func__, fsp_str_dbg(fsp), fsp_fnum_dbg(fsp),
 			 file_id_string_tos(&fsp->file_id)));
 	}
 
@@ -276,6 +291,64 @@ bool downgrade_oplock(files_struct *fsp)
 	return ret;
 }
 
+struct downgrade_lease_fsps_state {
+	struct file_id id;
+	struct share_mode_lock *lck;
+	const struct smb2_lease_key *key;
+	uint32_t new_lease_state;
+	unsigned count;
+};
+
+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;
+	bool downgraded;
+
+	downgraded = fsp_lease_broken(state->lck, state->id, state->key, fsp,
+				      state->new_lease_state);
+	state->count += downgraded ? 1 : 0;
+	return NULL;
+}
+
+NTSTATUS downgrade_lease(struct smbd_server_connection *sconn,
+			 const struct file_id id,
+			 const struct smb2_lease_key *key,
+			 uint32_t lease_state)
+{
+	struct share_mode_lock *lck;
+	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);
+
+	/*
+	 * This sucks. We have to reset fsp->sent_oplock_break on all fsps
+	 * that reference this lease.
+	 */
+	{
+		struct downgrade_lease_fsps_state state = {
+			.id = id, .lck = lck, .key = key,
+			.count = 0, .new_lease_state = lease_state
+		};
+
+		files_forall(sconn, downgrade_lease_fsps, &state);
+
+		/* Paranoia */
+		SMB_ASSERT(state.count > 0);
+	}
+
+	TALLOC_FREE(lck);
+	return status;
+}
+
 /****************************************************************************
  Set up an oplock break message.
 ****************************************************************************/
@@ -447,7 +520,6 @@ 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,
@@ -488,23 +560,42 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx,
 		/*
 		 * Nothing to do anymore
 		 */
+		DEBUG(10, ("fsp->sent_oplock_break = %d\n",
+			   fsp->sent_oplock_break));
 		return;
 	}
 
-	if (break_to == fsp->oplock_type) {
-		DEBUG(3, ("Already downgraded oplock on %s: %s\n",
-			  file_id_string_tos(&fsp->file_id),
-			  fsp_str_dbg(fsp)));
-		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 ((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 (!lp_level2_oplocks(SNUM(fsp->conn))) {
+		DEBUG(10, ("no level2 oplocks by config\n"));
+		break_to &= ~SMB2_LEASE_READ;
+	}
+
+	DEBUG(10, ("msg.op_type=%u, break_to=%u\n",
+		   (unsigned)msg.op_type, (unsigned)break_to));
+
+	if (fsp->oplock_type == NO_OPLOCK) {
+		DEBUG(3, ("Already downgraded oplock to none on %s: %s\n",
+			  file_id_string_tos(&fsp->file_id),
+			  fsp_str_dbg(fsp)));
+		return;
+	}
+	if ((break_to & SMB2_LEASE_READ) &&
+	    (fsp->oplock_type == LEVEL_II_OPLOCK)) {
+		DEBUG(3, ("Already downgraded oplock to level2 on %s: %s\n",
+			  file_id_string_tos(&fsp->file_id),
+			  fsp_str_dbg(fsp)));
+		return;
 	}
 
 	/* Need to wait before sending a break
@@ -514,21 +605,33 @@ 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_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 ((fsp_lease_type(fsp) == SMB2_LEASE_READ) &&
+	    (break_to == SMB2_LEASE_NONE)) {
 		/*
 		 * This is an async break without a reply and thus no timeout
 		 */
-		remove_oplock(fsp);
+		if (fsp->oplock_type == LEASE_OPLOCK) {
+			/*
+			 * We must leave the lease around, it might be
+			 * upgraded later
+			 */
+			downgrade_lease(fsp->conn->sconn, fsp->file_id,
+					&fsp->lease->lease.lease_key,
+					break_to);
+		} else {
+			remove_oplock(fsp);
+		}
 		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);
 }
 
@@ -596,6 +699,7 @@ 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;
 };
 static void do_break_to_none(struct tevent_context *ctx,
 			     struct tevent_immediate *im,
@@ -614,7 +718,7 @@ static void contend_level2_oplocks_begin_default(files_struct *fsp,
 	struct tevent_immediate *im;
 	struct break_to_none_state *state;
 	struct byte_range_lock *brl;
-	uint32_t num_read_oplocks = 0;
+	uint32_t num_read_oplocks;
 
 	/*
 	 * If this file is level II oplocked then we need
@@ -631,6 +735,8 @@ static void contend_level2_oplocks_begin_default(files_struct *fsp,
 		return;
 	}
 
+	num_read_oplocks = 0;
+
 	brl = brl_get_locks_readonly(fsp);
 	if (brl != NULL) {
 		num_read_oplocks = brl_num_read_oplocks(brl);
@@ -642,6 +748,10 @@ static void contend_level2_oplocks_begin_default(files_struct *fsp,
 		DEBUG(10, ("No read oplocks around\n"));
 		return;
 	}
+	if ((num_read_oplocks == 1) && (fsp->oplock_type == LEASE_OPLOCK)) {
+		DEBUG(10, ("We're the only reader, don't break\n"));
+		return;
+	}
 
 	/*
 	 * When we get here we might have a brlock entry locked. Also
@@ -650,7 +760,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;
@@ -658,6 +768,13 @@ 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->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"));
@@ -667,14 +784,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 +813,61 @@ 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 ));
+	/*
+	 * 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 < 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_leases; i++) {
+		struct share_mode_oplock *l = &d->leases[i];
+		struct share_mode_entry *e;
+		uint32_t j;
 
-		if (!is_valid_share_mode_entry(share_entry)) {
+		if ((l->current_state & SMB2_LEASE_READ) == 0) {
+			continue;
+		}
+		if ((l->lease_key.data[0] == state->lease_key.data[0]) &&
+		    (l->lease_key.data[1] == state->lease_key.data[1])) {
+			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];
+
+		if (!is_valid_share_mode_entry(e)) {
+			continue;
+		}
+		if (e->op_type == LEASE_OPLOCK) {
+			/*
+			 * Took care of those in the loop above
+			 */
 			continue;
 		}
 
@@ -705,15 +882,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 +898,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
diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
index 1080895..613af8a 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);
@@ -387,6 +390,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);
+uint32_t fsp_lease_type(struct files_struct *fsp);
 
 /* The following definitions come from smbd/ipc.c  */
 
@@ -609,6 +613,8 @@ NTSTATUS change_dir_owner_to_parent(connection_struct *conn,
 				    const char *inherit_from_dir,
 				    const char *fname,
 				    SMB_STRUCT_STAT *psbuf);
+struct fsp_lease *find_fsp_lease(files_struct *new_fsp,
+				 const struct smb2_lease_key *key);
 bool is_stat_open(uint32 access_mask);
 struct deferred_open_record;
 bool is_deferred_open_async(const struct deferred_open_record *rec);
@@ -647,12 +653,18 @@ 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);
 NTSTATUS set_file_oplock(files_struct *fsp);
 bool remove_oplock(files_struct *fsp);
 bool downgrade_oplock(files_struct *fsp);
+NTSTATUS downgrade_lease(struct smbd_server_connection *sconn,
+			 const struct file_id id,
+			 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,
diff --git a/source3/smbd/server.c b/source3/smbd/server.c
index 0d649e1..60394dd 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;
@@ -1450,6 +1451,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/smbd/smb2_break.c b/source3/smbd/smb2_break.c
index 4492456..cb57504 100644
--- a/source3/smbd/smb2_break.c
+++ b/source3/smbd/smb2_break.c
@@ -25,6 +25,9 @@
 #include "../libcli/smb/smb_common.h"
 #include "../lib/util/tevent_ntstatus.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,
 						      struct smbd_smb2_request *smb2req,
@@ -45,6 +48,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 +231,163 @@ 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;
+};
+
+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 files_struct *fsp;
+	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;
+
+	fsp = file_find_one_fsp_from_lease_key(smb2_req->sconn, &in_lease_key);
+	if (fsp == NULL) {
+		DEBUG(10, ("No fsp for lease key found\n"));
+		tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+		return tevent_req_post(req, ev);
+	}
+
+	status = downgrade_lease(smb2_req->sconn, fsp->file_id, &in_lease_key,
+				 in_lease_state);
+	if (tevent_req_nterror(req, status)) {
+		DEBUG(10, ("downgrade_lease returned %s\n",
+			   nt_errstr(status)));
+		return tevent_req_post(req, ev);
+	}
+	TALLOC_FREE(fsp->oplock_timeout);
+
+	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_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 +413,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 +422,29 @@ 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 ));
+
+	if (fsp->oplock_type == LEASE_OPLOCK) {
+		bool no_ack;
 
-	status = smbd_smb2_send_oplock_break(xconn,
-					     session,
-					     fsp->conn->tcon,
-					     fsp->op,
-					     smb2_oplock_level);
+		no_ack = ((fsp->lease->lease.lease_state == SMB2_LEASE_READ) &&
+			  (break_to == SMB2_LEASE_NONE));
+
+		status = smbd_smb2_send_lease_break(
+			xconn, session, fsp->conn->tcon, 0,
+			no_ack ? 0 : SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED,
+			&fsp->lease->lease.lease_key,
+			fsp->lease->lease.lease_state, 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));
diff --git a/source3/smbd/smb2_create.c b/source3/smbd/smb2_create.c
index 48bc486..67d4fc0 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;
 	}
@@ -374,6 +375,62 @@ 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)
+{
+	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;
@@ -507,6 +564,11 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 			num_blobs_allowed = 1;
 		}
 
+		if (smb2_create_blob_find(&in_context_blobs,
+					  SMB2_CREATE_TAG_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);
@@ -539,6 +601,11 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 
 		num_blobs_allowed = 1;
 
+		if (smb2_create_blob_find(&in_context_blobs,
+					  SMB2_CREATE_TAG_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);
@@ -600,11 +667,15 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 		struct smb2_create_blob *qfid = NULL;
 		struct GUID _create_guid = GUID_zero();
 		struct GUID *create_guid = NULL;
+		struct smb2_create_blob *rqls = NULL;
 		bool update_open = false;
 		bool durable_requested = false;
 		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;
+		ssize_t lease_len = -1;
 
 		exta = smb2_create_blob_find(&in_context_blobs,
 					     SMB2_CREATE_TAG_EXTA);
@@ -618,6 +689,8 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 					     SMB2_CREATE_TAG_TWRP);
 		qfid = smb2_create_blob_find(&in_context_blobs,
 					     SMB2_CREATE_TAG_QFID);
+		rqls = smb2_create_blob_find(&in_context_blobs,
+					     SMB2_CREATE_TAG_RQLS);
 
 		fname = talloc_strdup(state, in_name);
 		if (tevent_req_nomem(fname, req)) {
@@ -804,6 +877,39 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 			}
 		}
 
+		lease_ptr = NULL;
+
+		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;
+			}
+
+			/* TODO client->connections ... */
+			if ((smb2req->sconn->client->connections->protocol <
+			     PROTOCOL_SMB3_00)
+			    && (lease_len == 52)) {
+				DEBUG(10, ("v2 lease key only for SMB3\n"));
+				lease_ptr = NULL;
+				requested_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
+			}
+		}
+
 		/* these are ignored for SMB2 */
 		in_create_options &= ~(0x10);/* NTCREATEX_OPTIONS_SYNC_ALERT */
 		in_create_options &= ~(0x20);/* NTCREATEX_OPTIONS_ASYNC_ALERT */
@@ -863,6 +969,18 @@ 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;
 
@@ -948,7 +1066,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,
@@ -1003,7 +1121,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,
@@ -1086,6 +1204,29 @@ 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.lease_flags &=
+				SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET;
+
+			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;
diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c
index 689bfd7..36ac0d7 100644
--- a/source3/smbd/smb2_server.c
+++ b/source3/smbd/smb2_server.c
@@ -2888,6 +2888,31 @@ 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,
+				    struct smbXsrv_session *session,
+				    struct smbXsrv_tcon *tcon,
+				    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, session, tcon, body, sizeof(body));
+}
+
 static bool is_smb2_recvfile_write(struct smbd_smb2_request_read_state *state)
 {
 	NTSTATUS status;
diff --git a/source3/utils/status.c b/source3/utils/status.c
index 978d3c5..f46f336 100644
--- a/source3/utils/status.c
+++ b/source3/utils/status.c
@@ -177,6 +177,8 @@ 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) {
+			d_printf("LEASE           ");
 		} else {
 			d_printf("NONE            ");
 		}
diff --git a/source3/wscript_build b/source3/wscript_build
index 836d7b3..7a0fd7f 100755
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -619,6 +619,7 @@ bld.SAMBA3_LIBRARY('smbd_base',
                    LIBAFS
                    RPC_SERVICE
                    NDR_SMBXSRV
+                   LEASES_DB
                    LIBASYS
                    sysquotas
                    ccan-hash
@@ -637,9 +638,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 f3a903fb04cf9cc64064d1d7a028147542e59d50 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 23 Sep 2014 02:28:38 +0200
Subject: [PATCH 02/16] s3: leases - epoch handling

Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
 source3/smbd/open.c        |  6 +++---
 source3/smbd/oplock.c      | 23 +++++++++++++++++++++++
 source3/smbd/proto.h       |  3 +++
 source3/smbd/smb2_break.c  |  9 ++++++++-
 source3/smbd/smb2_create.c |  6 ++++++
 5 files changed, 43 insertions(+), 4 deletions(-)

diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index 7ac8fd8..c47e913 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1521,7 +1521,7 @@ static bool file_has_brlocks(files_struct *fsp)
 	return (brl_num_locks(br_lck) > 0);
 }
 
-static int find_share_mode_oplock(struct share_mode_data *d,
+int find_share_mode_oplock(struct share_mode_data *d,
 				  const struct GUID *client_guid,
 				  const struct smb2_lease_key *key)
 {
@@ -1670,8 +1670,8 @@ static NTSTATUS grant_fsp_lease(files_struct *fsp, struct share_mode_data *d,
 
 	d->leases[d->num_leases] = (struct share_mode_oplock) {
 		.client_guid = *client_guid,
-		.lease_key = lease->lease_key,
-		.epoch = lease->lease_epoch,
+		.lease_key = fsp->lease->lease.lease_key,
+		.epoch = fsp->lease->lease.lease_epoch,
 		.current_state = granted,
 	};
 
diff --git a/source3/smbd/oplock.c b/source3/smbd/oplock.c
index 0a7ac17..83dec36 100644
--- a/source3/smbd/oplock.c
+++ b/source3/smbd/oplock.c
@@ -598,6 +598,29 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx,
 		return;
 	}
 
+	if (fsp->oplock_type == LEASE_OPLOCK) {
+		/* Need to increment the epoch */
+		struct share_mode_lock *lck;
+		int idx;
+
+		lck = get_existing_share_mode_lock(
+			talloc_tos(), fsp->file_id);
+
+		idx = find_share_mode_oplock(
+			lck->data,
+			&fsp->conn->sconn->client->connections->
+			   smb2.client.guid,
+			&fsp->lease->lease.lease_key);
+		if (idx != -1) {
+			struct share_mode_oplock *o;
+			o = &lck->data->leases[idx];
+			o->epoch += 1;
+			fsp->lease->lease.lease_epoch = o->epoch;
+		}
+
+		TALLOC_FREE(lck);
+	}
+
 	/* Need to wait before sending a break
 	   message if we sent ourselves this message. */
 	if (serverid_equal(&self, &src)) {
diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
index 613af8a..08ad413 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -627,6 +627,9 @@ 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_oplock(struct share_mode_data *d,
+			   const struct GUID *client_guid,
+			   const struct smb2_lease_key *key);
 NTSTATUS create_file_default(connection_struct *conn,
 			     struct smb_request *req,
 			     uint16_t root_dir_fid,
diff --git a/source3/smbd/smb2_break.c b/source3/smbd/smb2_break.c
index cb57504..abe6709 100644
--- a/source3/smbd/smb2_break.c
+++ b/source3/smbd/smb2_break.c
@@ -426,12 +426,19 @@ void send_break_message_smb2(files_struct *fsp, uint32_t break_to)
 
 	if (fsp->oplock_type == LEASE_OPLOCK) {
 		bool no_ack;
+		uint16_t new_epoch;
 
 		no_ack = ((fsp->lease->lease.lease_state == SMB2_LEASE_READ) &&
 			  (break_to == SMB2_LEASE_NONE));
 
+		if (xconn->protocol >= PROTOCOL_SMB3_00) {
+			new_epoch = fsp->lease->lease.lease_epoch;
+		} else {
+			new_epoch = 0;
+		}
+
 		status = smbd_smb2_send_lease_break(
-			xconn, session, fsp->conn->tcon, 0,
+			xconn, session, fsp->conn->tcon, new_epoch,
 			no_ack ? 0 : SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED,
 			&fsp->lease->lease.lease_key,
 			fsp->lease->lease.lease_state, break_to);
diff --git a/source3/smbd/smb2_create.c b/source3/smbd/smb2_create.c
index 67d4fc0..abcfa1a 100644
--- a/source3/smbd/smb2_create.c
+++ b/source3/smbd/smb2_create.c
@@ -900,6 +900,12 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 				requested_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
 			}
 
+			if (smb2req->sconn->client->connections->protocol <
+			    PROTOCOL_SMB2_10) {
+				lease_ptr = NULL;
+				requested_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
+			}
+
 			/* TODO client->connections ... */
 			if ((smb2req->sconn->client->connections->protocol <
 			     PROTOCOL_SMB3_00)
-- 
1.9.1


From ac90f0493583bdd9c56b124a53a1660b6bae3edf Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 23 Sep 2014 22:56:41 +0200
Subject: [PATCH 03/16] s3: leases - Only increment epoch for V2 leases

Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
 source3/smbd/open.c        | 4 +++-
 source3/smbd/oplock.c      | 3 ++-
 source3/smbd/smb2_create.c | 4 ++--
 3 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index c47e913..6aa63a4 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1664,7 +1664,9 @@ static NTSTATUS grant_fsp_lease(files_struct *fsp, struct share_mode_data *d,
 	fsp->lease->ref_count = 1;
 	fsp->lease->lease = *lease;
 	fsp->lease->lease.lease_state = granted;
-	fsp->lease->lease.lease_epoch += 1;
+	if (fsp->lease->lease.lease_version > 1) {
+		fsp->lease->lease.lease_epoch += 1;
+	}
 
 	*p_lease_idx = d->num_leases;
 
diff --git a/source3/smbd/oplock.c b/source3/smbd/oplock.c
index 83dec36..e97ade6 100644
--- a/source3/smbd/oplock.c
+++ b/source3/smbd/oplock.c
@@ -598,7 +598,8 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx,
 		return;
 	}
 
-	if (fsp->oplock_type == LEASE_OPLOCK) {
+	if ((fsp->oplock_type == LEASE_OPLOCK) &&
+	    (fsp->lease->lease.lease_version != 1)) {
 		/* Need to increment the epoch */
 		struct share_mode_lock *lck;
 		int idx;
diff --git a/source3/smbd/smb2_create.c b/source3/smbd/smb2_create.c
index abcfa1a..38ef04c 100644
--- a/source3/smbd/smb2_create.c
+++ b/source3/smbd/smb2_create.c
@@ -908,8 +908,8 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 
 			/* TODO client->connections ... */
 			if ((smb2req->sconn->client->connections->protocol <
-			     PROTOCOL_SMB3_00)
-			    && (lease_len == 52)) {
+			     PROTOCOL_SMB3_00) &&
+			    (lease.lease_version != 1)) {
 				DEBUG(10, ("v2 lease key only for SMB3\n"));
 				lease_ptr = NULL;
 				requested_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
-- 
1.9.1


From f22d4519cd39f3b432d6d3fa0695f73852554e5e Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Fri, 10 Oct 2014 16:36:54 -0700
Subject: [PATCH 04/16] s3: smbd: leases - expand the leases db to hold a list
 of file_ids (dev,ino) with this lease.

Will enable us to solve the dynamic share path problem.

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 source3/librpc/idl/leases_db.idl |  24 +++
 source3/locking/leases_db.c      | 368 +++++++++++++++++++++++++++++++++++++++
 source3/locking/leases_db.h      |  46 +++++
 3 files changed, 438 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..28ec479
--- /dev/null
+++ b/source3/librpc/idl/leases_db.idl
@@ -0,0 +1,24 @@
+#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/locking/leases_db.c b/source3/locking/leases_db.c
new file mode 100644
index 0000000..29b8e35
--- /dev/null
+++ b/source3/locking/leases_db.c
@@ -0,0 +1,368 @@
+/*
+   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 {
+		new_value = (struct leases_db_value) {
+			.num_file_ids = 1,
+			.ids = 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;
+	}
+
+	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) {
+		status = dbwrap_record_delete(rec);
+	} else {
+		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;
+		}
+
+		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;
+	}
+
+	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_ */
-- 
1.9.1


From c36985a806f36be7658c574ee384ca7fb23c479d 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 05/16] s3: smbd: Leases - ensure all share mode removal
 functions go through a common lease refcount manager.

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 source3/locking/locking.c | 137 ++++++++++++++++++++++++++++------------------
 1 file changed, 83 insertions(+), 54 deletions(-)

diff --git a/source3/locking/locking.c b/source3/locking/locking.c
index 170fa1d..8132c71 100644
--- a/source3/locking/locking.c
+++ b/source3/locking/locking.c
@@ -618,6 +618,85 @@ 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;
+		}
+	}
+
+	{
+		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.
@@ -675,6 +754,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;
 }
@@ -773,6 +854,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;
@@ -822,67 +904,14 @@ bool remove_share_oplock(struct share_mode_lock *lck, files_struct *fsp)
 {
 	struct share_mode_data *d = lck->data;
 	struct share_mode_entry *e;
-	uint16_t op_type;
-	uint32_t lease_idx;
-	uint32_t i;
 
 	e = find_share_mode_entry(lck, fsp);
 	if (e == NULL) {
 		return False;
 	}
 
-	op_type = e->op_type;
-	e->op_type = NO_OPLOCK;
-
+	remove_share_mode_lease(d, e);
 	d->modified = True;
-
-	if (op_type != LEASE_OPLOCK) {
-		return true;
-	}
-
-	/*
-	 * 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].lease_idx == lease_idx) {
-			break;
-		}
-	}
-	if (i < d->num_share_modes) {
-		/*
-		 * Found another one
-		 */
-		return true;
-	}
-
-	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;
-		}
-	}
-
-	{
-		NTSTATUS status;
-
-		status = leases_db_del(
-			&fsp->conn->sconn->client->connections->smb2.client.guid,
-			&fsp->lease->lease.lease_key);
-
-		DEBUG(10, ("%s: leases_db_del returned %s\n", __func__,
-			   nt_errstr(status)));
-	}
-
 	return true;
 }
 
-- 
1.9.1


From e1c53fdb1e8137519821537e74e2d7e2431b1176 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Fri, 10 Oct 2014 17:05:46 -0700
Subject: [PATCH 06/16] s3: smbd: leases - Fix the dynamic share file case
 [homes].

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 source3/smbd/open.c | 151 ++++++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 135 insertions(+), 16 deletions(-)

diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index 6aa63a4..5d9256e 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -4091,39 +4091,148 @@ static NTSTATUS inherit_new_acl(files_struct *fsp)
  */
 
 struct lease_fname_match_state {
+	/* Input parameters. */
 	const struct smb_filename *fname;
-	bool match;
+	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(
-	struct file_id id, const char *filename, const char *stream_name,
+	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;
 
-	state->match =
-		strequal(filename, state->fname->base_name) &&
-		strequal(stream_name, state->fname->stream_name);
+	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) {
+		/* New file. */
+		state->match_status = NT_STATUS_OK;
+		return;
+	}
+
+	if (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. 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 bool lease_fname_match(struct smbd_server_connection *sconn,
-			      struct smb2_lease_key *lease_key,
-			      const struct smb_filename *fname)
+static NTSTATUS lease_match(connection_struct *conn,
+			    struct smb_request *req,
+			    struct smb2_lease_key *lease_key,
+			    const struct smb_filename *fname)
 {
-	struct lease_fname_match_state state =
-		{ .fname = fname, .match = true };
+	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);
+	}
+
 	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 true;
+		return NT_STATUS_OK;
 	}
-	return state.match;
+	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);
+
+			if (e_lease_type == SMB2_LEASE_NONE) {
+				continue;
+			}
+			if (share_mode_stale_pid(d, j)) {
+				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;
 }
 
 /*
@@ -4182,10 +4291,20 @@ static NTSTATUS create_file_unixpath(connection_struct *conn,
 		oplock_request |= INTERNAL_OPEN_ONLY;
 	}
 
-	if ((lease != NULL) &&
-	    !lease_fname_match(req->sconn, &lease->lease_key, smb_fname)) {
-		status = NT_STATUS_INVALID_PARAMETER;
-		goto fail;
+	if (lease != NULL) {
+		status = lease_match(conn,
+				req,
+				&lease->lease_key,
+				smb_fname);
+		if (NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
+			/* Dynamic share file. No leases and update epoch... */
+			lease->lease_state = SMB2_LEASE_NONE;
+			if (lease->lease_version > 1) {
+				lease->lease_epoch += 1;
+			}
+		} else if (!NT_STATUS_IS_OK(status)) {
+			goto fail;
+		}
 	}
 
 	if ((conn->fs_capabilities & FILE_NAMED_STREAMS)
-- 
1.9.1


From 2041becaf6aaab3cde2e6c3e426c92a22e81e081 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Tue, 14 Oct 2014 10:24:31 -0700
Subject: [PATCH 07/16] s3: smbd: leases - If find_fsp_lease() doesn't find a
 lease this isn't an error.

As the lease exists in the oplock db, then it just must be leased in
another smbd (multi-connection case).

This unifies code that previously existed only inside the durable
reconnect case.

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 source3/smbd/durable.c | 21 +++++----------------
 source3/smbd/open.c    | 24 ++++++++++++++++--------
 source3/smbd/proto.h   |  4 +++-
 3 files changed, 24 insertions(+), 25 deletions(-)

diff --git a/source3/smbd/durable.c b/source3/smbd/durable.c
index 660865d..73bcd58 100644
--- a/source3/smbd/durable.c
+++ b/source3/smbd/durable.c
@@ -731,22 +731,11 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
 		key.data[0] = o->lease_key.data[0];
 		key.data[1] = o->lease_key.data[1];
 
-		fsp->lease = find_fsp_lease(fsp, &key);
-
-		if (fsp->lease != NULL) {
-			fsp->lease->ref_count += 1;
-		} else {
-			fsp->lease = talloc_zero(fsp->conn->sconn,
-						 struct fsp_lease);
-			if (fsp->lease == NULL) {
-				TALLOC_FREE(lck);
-				fsp_free(fsp);
-				return NT_STATUS_NO_MEMORY;
-			}
-			fsp->lease->ref_count = 1;
-			fsp->lease->lease.lease_key = key;
-			fsp->lease->lease.lease_state = o->current_state;
-			fsp->lease->lease.lease_epoch = o->epoch;
+		fsp->lease = find_fsp_lease(fsp, &key, o);
+		if (fsp->lease == NULL) {
+			TALLOC_FREE(lck);
+			fsp_free(fsp);
+			return NT_STATUS_NO_MEMORY;
 		}
 	}
 
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index 5d9256e..df48a10 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1552,7 +1552,8 @@ static bool is_same_lease(const struct share_mode_data *d,
 }
 
 struct fsp_lease *find_fsp_lease(files_struct *new_fsp,
-				 const struct smb2_lease_key *key)
+				 const struct smb2_lease_key *key,
+				 const struct share_mode_oplock *o)
 {
 	files_struct *fsp;
 
@@ -1572,11 +1573,21 @@ struct fsp_lease *find_fsp_lease(files_struct *new_fsp,
 			continue;
 		}
 		if (smb2_lease_key_equal(&fsp->lease->lease.lease_key, key)) {
+			fsp->lease->ref_count += 1;
 			return fsp->lease;
 		}
 	}
 
-	return NULL;
+	/* 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->lease.lease_key = *key;
+	new_fsp->lease->lease.lease_state = o->current_state;
+	new_fsp->lease->lease.lease_epoch = o->epoch;
+	return new_fsp->lease;
 }
 
 static NTSTATUS grant_fsp_lease(files_struct *fsp, struct share_mode_data *d,
@@ -1585,7 +1596,6 @@ static NTSTATUS grant_fsp_lease(files_struct *fsp, struct share_mode_data *d,
 				uint32_t granted)
 {
 	const struct GUID *client_guid;
-	struct share_mode_oplock *o;
 	struct share_mode_oplock *tmp;
 	NTSTATUS status;
 	int idx;
@@ -1599,20 +1609,18 @@ static NTSTATUS grant_fsp_lease(files_struct *fsp, struct share_mode_data *d,
 	idx = find_share_mode_oplock(d, client_guid, &lease->lease_key);
 
 	if (idx != -1) {
-
+		struct share_mode_oplock *o = &d->leases[idx];
 		bool do_upgrade;
 		uint32_t existing, requested;
 
-		fsp->lease = find_fsp_lease(fsp, &lease->lease_key);
+		fsp->lease = find_fsp_lease(fsp, &lease->lease_key, o);
 		if (fsp->lease == NULL) {
 			DEBUG(1, ("Did not find existing lease for file %s\n",
 				  fsp_str_dbg(fsp)));
-			return NT_STATUS_INTERNAL_ERROR;
+			return NT_STATUS_NO_MEMORY;
 		}
-		fsp->lease->ref_count += 1;
 
 		*p_lease_idx = idx;
-		o = &d->leases[idx];
 
 		/*
 		 * Upgrade only if the requested lease is a strict upgrade.
diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
index 08ad413..22b1a82 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -613,8 +613,10 @@ NTSTATUS change_dir_owner_to_parent(connection_struct *conn,
 				    const char *inherit_from_dir,
 				    const char *fname,
 				    SMB_STRUCT_STAT *psbuf);
+struct share_mode_oplock;
 struct fsp_lease *find_fsp_lease(files_struct *new_fsp,
-				 const struct smb2_lease_key *key);
+				 const struct smb2_lease_key *key,
+				 const struct share_mode_oplock *o);
 bool is_stat_open(uint32 access_mask);
 struct deferred_open_record;
 bool is_deferred_open_async(const struct deferred_open_record *rec);
-- 
1.9.1


From dd001563b1b03d8fa05fd0d45c034984841e6c48 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 08/16] s3: leases: Add fsp_client_guid() utility function to
 return the connected client guid.

Signed-off-by: Jeremy Allison <jra 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 13d1138..6a987c4 100644
--- a/source3/smbd/files.c
+++ b/source3/smbd/files.c
@@ -774,3 +774,8 @@ uint32_t fsp_lease_type(struct files_struct *fsp)
 	}
 	return map_oplock_to_lease_type(fsp->oplock_type);
 }
+
+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 22b1a82..ecf89a7 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -391,6 +391,7 @@ NTSTATUS file_name_hash(connection_struct *conn,
 NTSTATUS fsp_set_smb_fname(struct files_struct *fsp,
 			   const struct smb_filename *smb_fname_in);
 uint32_t fsp_lease_type(struct files_struct *fsp);
+const struct GUID *fsp_client_guid(const files_struct *fsp);
 
 /* The following definitions come from smbd/ipc.c  */
 
-- 
1.9.1


From d38c1bab1815bb2a4220d582f761628da5503d26 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Tue, 4 Nov 2014 21:47:14 -0800
Subject: [PATCH 09/16] s3: leases: Ensure we check client_guid as well as
 lease key to ensure lease identity.

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 source3/locking/locking.c | 10 ++++++++--
 source3/smbd/open.c       | 39 ++++++++++++++++++++-------------------
 2 files changed, 28 insertions(+), 21 deletions(-)

diff --git a/source3/locking/locking.c b/source3/locking/locking.c
index 8132c71..006d6d3 100644
--- a/source3/locking/locking.c
+++ b/source3/locking/locking.c
@@ -960,7 +960,10 @@ bool fsp_lease_broken(struct share_mode_lock *lck,
 		return false;
 	}
 
-	if (!smb2_lease_key_equal(key, &d->leases[e->lease_idx].lease_key)) {
+	if (!smb2_lease_equal(&fsp->conn->sconn->client->connections->smb2.client.guid,
+			      key,
+			      &d->leases[e->lease_idx].client_guid,
+			      &d->leases[e->lease_idx].lease_key)) {
 		return false;
 	}
 	fsp->sent_oplock_break = NO_BREAK_SENT;
@@ -979,7 +982,10 @@ NTSTATUS downgrade_share_lease(struct smbd_server_connection *sconn,
 	uint32_t i;
 
 	for (i=0; i<d->num_leases; i++) {
-		if (smb2_lease_key_equal(key, &d->leases[i].lease_key)) {
+		if (smb2_lease_equal(&sconn->client->connections->smb2.client.guid,
+				     key,
+				     &d->leases[i].client_guid,
+				     &d->leases[i].lease_key)) {
 			break;
 		}
 	}
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index df48a10..e353ec1 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -37,11 +37,11 @@
 #include "source3/lib/dbwrap/dbwrap_watch.h"
 #include "locking/leases_db.h"
 
-static bool is_same_lease(const struct share_mode_data *d,
+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);
 
-
 extern const struct generic_mapping file_generic_mapping;
 
 struct deferred_open_record {
@@ -1424,7 +1424,7 @@ static bool delay_for_oplock(files_struct *fsp,
 			if (!(e_lease_type & SMB2_LEASE_HANDLE)) {
 				continue;
 			}
-			if (is_same_lease(d, e, lease)) {
+			if (is_same_lease(fsp, d, e, lease)) {
 				continue;
 			}
 			if (share_mode_stale_pid(d, i)) {
@@ -1470,9 +1470,10 @@ static bool delay_for_oplock(files_struct *fsp,
 			}
 			if ((e->op_type == LEASE_OPLOCK) &&
 			    (lease != NULL) &&
-			    smb2_lease_key_equal(
-				    &lease->lease_key,
-				    &d->leases[e->lease_idx].lease_key)) {
+			    smb2_lease_equal(fsp_client_guid(fsp),
+					     &lease->lease_key,
+					     &d->leases[e->lease_idx].client_guid,
+					     &d->leases[e->lease_idx].lease_key)) {
 				return false;
 			}
 
@@ -1529,15 +1530,18 @@ int find_share_mode_oplock(struct share_mode_data *d,
 
 	for (i=0; i<d->num_leases; i++) {
 		struct share_mode_oplock *l = &d->leases[i];
-		if (GUID_equal(client_guid, &l->client_guid) &&
-		    smb2_lease_key_equal(key, &l->lease_key)) {
+		if (smb2_lease_equal(client_guid,
+				     key,
+				     &l->client_guid,
+				     &l->lease_key)) {
 			return i;
 		}
 	}
 	return -1;
 }
 
-static bool is_same_lease(const struct share_mode_data *d,
+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)
 {
@@ -1547,8 +1551,11 @@ static bool is_same_lease(const struct share_mode_data *d,
 	if (lease == NULL) {
 		return false;
 	}
-	return smb2_lease_key_equal(&d->leases[e->lease_idx].lease_key,
-				    &lease->lease_key);
+
+	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);
 }
 
 struct fsp_lease *find_fsp_lease(files_struct *new_fsp,
@@ -1595,17 +1602,11 @@ static NTSTATUS grant_fsp_lease(files_struct *fsp, struct share_mode_data *d,
 				uint32_t *p_lease_idx,
 				uint32_t granted)
 {
-	const struct GUID *client_guid;
+	const struct GUID *client_guid = fsp_client_guid(fsp);
 	struct share_mode_oplock *tmp;
 	NTSTATUS status;
 	int idx;
 
-
-	/*
-	 * TODO: in future we can have multiple connections...
-	 */
-	client_guid = &fsp->conn->sconn->client->connections->smb2.client.guid;
-
 	idx = find_share_mode_oplock(d, client_guid, &lease->lease_key);
 
 	if (idx != -1) {
@@ -1759,7 +1760,7 @@ static NTSTATUS grant_fsp_oplock_type(struct smb_request *req, files_struct *fsp
 		e_lease_type = get_lease_type(d, e);
 
 		if ((granted & SMB2_LEASE_WRITE) &&
-		    !is_same_lease(d, e, lease) &&
+		    !is_same_lease(fsp, d, e, lease) &&
 		    !share_mode_stale_pid(d, i)) {
 			/*
 			 * Can grant only one writer
-- 
1.9.1


From 5a4726c5a4ead1bb3231f673f7ecc975c7cd9c29 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Tue, 4 Nov 2014 21:47:51 -0800
Subject: [PATCH 10/16] s3: leases: Ensure the client guids match when doing a
 durable reconnect.

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 source3/smbd/durable.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/source3/smbd/durable.c b/source3/smbd/durable.c
index 73bcd58..6e668ce 100644
--- a/source3/smbd/durable.c
+++ b/source3/smbd/durable.c
@@ -737,6 +737,17 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
 			fsp_free(fsp);
 			return NT_STATUS_NO_MEMORY;
 		}
+
+		/*
+		 * Ensure the existing client guid matches the
+		 * stored one in the share_mode_oplock.
+		 */
+		if (!GUID_equal(fsp_client_guid(fsp),
+				&o->client_guid)) {
+			TALLOC_FREE(lck);
+			fsp_free(fsp);
+			return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+		}
 	}
 
 	fsp->initial_allocation_size = cookie.initial_allocation_size;
-- 
1.9.1


From a93e2365fa96adba87adf2d4c854ad0d39c5ddb7 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 11/16] NEEDED? s4:torture:smb2: ignore the allocation size in
 the create return against samba3

Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
 source4/torture/smb2/durable_open.c | 5 ++++-
 source4/torture/smb2/lease.c        | 5 ++++-
 2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/source4/torture/smb2/durable_open.c b/source4/torture/smb2/durable_open.c
index c3d63d1..7e1e6a2 100644
--- a/source4/torture/smb2/durable_open.c
+++ b/source4/torture/smb2/durable_open.c
@@ -25,6 +25,7 @@
 #include "libcli/smb2/smb2_calls.h"
 #include "../libcli/smb/smbXcli_base.h"
 #include "torture/torture.h"
+#include "torture/util.h"
 #include "torture/smb2/proto.h"
 #include "../libcli/smb/smbXcli_base.h"
 
@@ -53,7 +54,9 @@
 #define CHECK_CREATED(__io, __created, __attribute)			\
 	do {								\
 		CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
-		CHECK_VAL((__io)->out.alloc_size, 0);			\
+		if (!TARGET_IS_SAMBA3(tctx)) { \
+			CHECK_VAL((__io)->out.alloc_size, 0);		\
+		} \
 		CHECK_VAL((__io)->out.size, 0);				\
 		CHECK_VAL((__io)->out.file_attr, (__attribute));	\
 		CHECK_VAL((__io)->out.reserved2, 0);			\
diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c
index 4326958..7ea2eab 100644
--- a/source4/torture/smb2/lease.c
+++ b/source4/torture/smb2/lease.c
@@ -25,6 +25,7 @@
 #include "libcli/smb2/smb2_calls.h"
 #include "torture/torture.h"
 #include "torture/smb2/proto.h"
+#include "torture/util.h"
 #include "libcli/smb/smbXcli_base.h"
 
 #define CHECK_VAL(v, correct) do { \
@@ -45,7 +46,9 @@
 #define CHECK_CREATED(__io, __created, __attribute)			\
 	do {								\
 		CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
-		CHECK_VAL((__io)->out.alloc_size, 0);			\
+		if (!TARGET_IS_SAMBA3(tctx)) { \
+			CHECK_VAL((__io)->out.alloc_size, 0);		\
+		} \
 		CHECK_VAL((__io)->out.size, 0);				\
 		CHECK_VAL((__io)->out.file_attr, (__attribute));	\
 		CHECK_VAL((__io)->out.reserved2, 0);			\
-- 
1.9.1


From 533f5033d494ffa091b9a7f8b6a14e4a04253cc9 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 12/16] s3:param: Add "smb2 leases" parameter. Default "false".

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 docs-xml/smbdotconf/locking/kerneloplocks.xml |  1 +
 docs-xml/smbdotconf/locking/oplocks.xml       |  1 +
 docs-xml/smbdotconf/locking/smb2leases.xml    | 24 ++++++++++++++++++++++++
 lib/param/param_table.c                       |  9 +++++++++
 source3/param/loadparm.c                      |  1 +
 5 files changed, 36 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..e7b1fb4
--- /dev/null
+++ b/docs-xml/smbdotconf/locking/smb2leases.xml
@@ -0,0 +1,24 @@
+<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>
+</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 7a13e6f..f46dd99 100644
--- a/lib/param/param_table.c
+++ b/lib/param/param_table.c
@@ -3010,6 +3010,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 d6ba8fb..4a85045 100644
--- a/source3/param/loadparm.c
+++ b/source3/param/loadparm.c
@@ -854,6 +854,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 ac44bd0e85a36778ee5bd1a71b5426ad34578c99 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 13/16] 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 7c87f55b83e784d46f138aa41d67225c5b315481 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 14/16] 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        | 14 +-------------
 selftest/target/Samba3.pm |  1 +
 2 files changed, 2 insertions(+), 13 deletions(-)

diff --git a/selftest/knownfail b/selftest/knownfail
index 1c4f446..66b4084 100644
--- a/selftest/knownfail
+++ b/selftest/knownfail
@@ -189,25 +189,13 @@
 ^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.lease.v2_request\(.*\)$
 ^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 48b8164..ae0a3a6 100755
--- a/selftest/target/Samba3.pm
+++ b/selftest/target/Samba3.pm
@@ -1051,6 +1051,7 @@ sub provision($$$$$$)
 
 	kernel oplocks = no
 	kernel change notify = no
+	smb2 leases = yes
 
 	syslog = no
 	printing = bsd
-- 
1.9.1


From ec35d449002792dce8b89676ec8870c762e6d6c0 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 15/16] 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 e7b1fb4..5b6fc31 100644
--- a/docs-xml/smbdotconf/locking/smb2leases.xml
+++ b/docs-xml/smbdotconf/locking/smb2leases.xml
@@ -15,10 +15,13 @@
 	This is only available with <smbconfoption name="oplocks">yes</smbconfoption>
 	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>
 </description>
 
 <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 37c3f66..6ba7285 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 c8ca2664dc56d74e52397bfa6b02f28c8fd30a67 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 16/16] 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 --------------
From 5eacb9604c0658852bbbc271c2c954b75f2e8b15 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 30 Oct 2014 10:10:07 +0100
Subject: [PATCH 1/3] SQ grant_fsp_oplock_type: minimize the diff

---
 source3/smbd/open.c | 34 ++++++++++++++++------------------
 1 file changed, 16 insertions(+), 18 deletions(-)

diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index e353ec1..aa250f2 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1700,22 +1700,22 @@ static NTSTATUS grant_fsp_lease(files_struct *fsp, struct share_mode_data *d,
 	d->modified = true;
 
 	return NT_STATUS_OK;
-
 }
 
-static NTSTATUS grant_fsp_oplock_type(struct smb_request *req, files_struct *fsp,
+static NTSTATUS grant_fsp_oplock_type(struct smb_request *req,
+				      struct files_struct *fsp,
 				      struct share_mode_lock *lck,
 				      int oplock_request,
-				      uint32_t create_disposition,
 				      struct smb2_lease *lease)
 {
 	struct share_mode_data *d = lck->data;
-	bool got_handle_lease, got_oplock;
+	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;
-	bool ret;
 
 	if (oplock_request & INTERNAL_OPEN_ONLY) {
 		/* No oplocks on internal open. */
@@ -1750,9 +1750,6 @@ static NTSTATUS grant_fsp_oplock_type(struct smb_request *req, files_struct *fsp
 		granted &= ~SMB2_LEASE_READ;
 	}
 
-	got_handle_lease = false;
-	got_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;
@@ -1825,9 +1822,6 @@ static NTSTATUS grant_fsp_oplock_type(struct smb_request *req, files_struct *fsp
 		}
 	}
 
-	DEBUG(10,("grant_fsp_oplock_type: oplock type 0x%x on file %s\n",
-		  fsp->oplock_type, fsp_str_dbg(fsp)));
-
 	status = set_file_oplock(fsp);
 	if (!NT_STATUS_IS_OK(status)) {
 		/*
@@ -1836,16 +1830,22 @@ static NTSTATUS grant_fsp_oplock_type(struct smb_request *req, files_struct *fsp
 		fsp->oplock_type = NO_OPLOCK;
 	}
 
-	if (!set_share_mode(lck, fsp, get_current_uid(fsp->conn),
+	ok = set_share_mode(lck, fsp, get_current_uid(fsp->conn),
 			    req ? req->mid : 0,
-			    fsp->oplock_type, lease_idx)) {
+			    fsp->oplock_type, lease_idx);
+	if (!ok) {
 		return NT_STATUS_NO_MEMORY;
 	}
-	ret = update_num_read_oplocks(fsp, lck);
-	if (!ret) {
+
+	ok = update_num_read_oplocks(fsp, lck);
+	if (!ok) {
 		del_share_mode(lck, fsp);
 		return NT_STATUS_INTERNAL_ERROR;
 	}
+
+	DEBUG(10,("grant_fsp_oplock_type: oplock type 0x%x on file %s\n",
+		  fsp->oplock_type, fsp_str_dbg(fsp)));
+
 	return NT_STATUS_OK;
 }
 
@@ -3109,9 +3109,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,
-				       create_disposition, lease);
+	status = grant_fsp_oplock_type(req, fsp, lck, oplock_request, lease);
 	if (!NT_STATUS_IS_OK(status)) {
 		TALLOC_FREE(lck);
 		fd_close(fsp);
-- 
1.9.1


From 1dfd83f7d5ac12834ebcd23e9f685fa9f006a6a1 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Tue, 11 Nov 2014 10:56:52 +0100
Subject: [PATCH 2/3] SQ fix grant_fsp_oplock_type() for leases  allow_level2
 ... simplify

---
 source3/smbd/open.c | 31 ++++++++++++++++---------------
 1 file changed, 16 insertions(+), 15 deletions(-)

diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index aa250f2..af99f36 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1776,14 +1776,23 @@ static NTSTATUS grant_fsp_oplock_type(struct smb_request *req,
 		}
 	}
 
-	if (oplock_request == LEASE_OPLOCK) {
+	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));
 
-		fsp->oplock_type = LEASE_OPLOCK;
+		if (!allow_level2) {
+			granted = SMB2_LEASE_NONE;
+		}
+	}
 
+	if (oplock_request == LEASE_OPLOCK) {
 		if (got_oplock) {
-			granted &= SMB2_LEASE_READ;
+			granted &= ~SMB2_LEASE_HANDLE;
 		}
 
+		fsp->oplock_type = LEASE_OPLOCK;
+
 		status = grant_fsp_lease(fsp, lck->data, lease, &lease_idx,
 					 granted);
 		if (!NT_STATUS_IS_OK(status)) {
@@ -1793,6 +1802,10 @@ static NTSTATUS grant_fsp_oplock_type(struct smb_request *req,
 		lease->lease_state = d->leases[lease_idx].current_state;
 		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;
@@ -1808,18 +1821,6 @@ static NTSTATUS grant_fsp_oplock_type(struct smb_request *req,
 			fsp->oplock_type = NO_OPLOCK;
 			break;
 		}
-		if (fsp->oplock_type == LEVEL_II_OPLOCK) {
-			bool allow_level2 =
-				(global_client_caps & CAP_LEVEL_II_OPLOCKS) &&
-				lp_level2_oplocks(SNUM(fsp->conn));
-
-			if (!allow_level2) {
-				fsp->oplock_type = NO_OPLOCK;
-			}
-		}
-		if (got_handle_lease) {
-			fsp->oplock_type = NO_OPLOCK;
-		}
 	}
 
 	status = set_file_oplock(fsp);
-- 
1.9.1


From 71a36122fb64811b064b58621d81ad80010fb42f Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Tue, 11 Nov 2014 10:57:32 +0100
Subject: [PATCH 3/3] SQ no leases for kernel oplocks

---
 source3/smbd/open.c | 18 +++++++++++-------
 1 file changed, 11 insertions(+), 7 deletions(-)

diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index af99f36..838e550 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1727,6 +1727,10 @@ static NTSTATUS grant_fsp_oplock_type(struct smb_request *req,
 	if (oplock_request == LEASE_OPLOCK) {
 		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;
@@ -1821,14 +1825,14 @@ static NTSTATUS grant_fsp_oplock_type(struct smb_request *req,
 			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;
+		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),
-- 
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/20141111/03371bcf/attachment.pgp>


More information about the samba-technical mailing list