Current SMB2 leases patchset (WIP).

Jeremy Allison jra at samba.org
Thu Oct 30 13:10:13 MDT 2014


On Thu, Oct 30, 2014 at 04:33:49PM +0100, Stefan (metze) Metzmacher wrote:
> Hi Jeremy,
> 
> one more...
> 
> > here's a first independent patchset set that can go to master now.
> > Mostly the strict rename patches.
> > 
> > The rest of the patches is in the following branch:
> > https://git.samba.org/?p=metze/samba/wip.git;a=shortlog;h=refs/heads/master3-leases
> > 
> > I'll post more review details on the rest once I looked at it more closely.
> > 
> > metze

OK, I've pushed the previous patchset + this one
to master.

Attached is the rebase of my leases-wip patches
remaining on top, just so I can keep working on
it and ensure the logic hasn't changed :-).

I'll accept more fixes to push as you get done
with them !

Thanks for doing this metze. I appreciate the
work you're putting into it (and I noticed and
adopted a fix you already did in your wip tree,
removing the superfluous del_share_mode()
call if grant_fsp_oplock_type() fails in open.c).
That kind of thing is why I appreciate your
careful review.

I know I'm grumpy about *how* you're doing it,
but I think that's more the difference in yours
and my work-styles with git than anything else.

Michael tried to tell me my use of git was just
"wrong", but I prefer "different" :-). After all,
there's no one way to use git :-) :-) :-).

Cheers,

	Jeremy.
-------------- next part --------------
From 1f2382ca34a0505e62db86bd59936871149c40d7 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Mon, 22 Sep 2014 21:21:36 +0200
Subject: [PATCH 01/15] s3: leases: mask off
 SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET.

Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
 libcli/smb/smb2_lease.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcli/smb/smb2_lease.c b/libcli/smb/smb2_lease.c
index f97f096..6fc26f2 100644
--- a/libcli/smb/smb2_lease.c
+++ b/libcli/smb/smb2_lease.c
@@ -47,6 +47,7 @@ ssize_t smb2_lease_pull(const uint8_t *buf, size_t len,
 	switch (version) {
 	case 1:
 		ZERO_STRUCT(lease->parent_lease_key);
+		lease->lease_flags &= ~SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET;
 		lease->lease_epoch = 0;
 		break;
 	case 2:
-- 
2.1.0.rc2.206.gedb03e5


From a9463988e6e027ddf7d51bb1ae5eadac808c59f2 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Tue, 28 Oct 2014 15:27:09 -0700
Subject: [PATCH 02/15] s3: leases - convert have_read field to num_read.

Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
 source3/locking/brlock.c | 123 +++++++++++++++++++++--------------------------
 1 file changed, 56 insertions(+), 67 deletions(-)

diff --git a/source3/locking/brlock.c b/source3/locking/brlock.c
index 1c4c4d0..265540b 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 have_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;
 }
 
@@ -1841,7 +1841,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;
 
@@ -1865,15 +1864,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)) {
@@ -1882,19 +1873,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);
@@ -1917,6 +1909,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.
@@ -1926,16 +1944,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);
@@ -1950,30 +1966,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;
@@ -1999,28 +1997,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)
@@ -2064,7 +2053,7 @@ struct byte_range_lock *brl_get_locks_readonly(files_struct *fsp)
 		}
 
 		br_lock->have_read_oplocks = false;
-		br_lock->num_locks = 0;
+		br_lock->num_read_oplocks = 0;
 		br_lock->lock_data = NULL;
 
 	} else if (!NT_STATUS_IS_OK(status)) {
-- 
2.1.0.rc2.206.gedb03e5


From 8c6ae906b5582ed70199bc0235f910602dfcb906 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 03/15] s3: smbd: Implementation of SMB2.1 leases.

Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
 libcli/smb/smb2_lease.c             |   6 +
 libcli/smb/smb2_lease.h             |   2 +
 selftest/knownfail                  |  12 -
 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             |  17 +-
 source3/smbd/durable.c              |  28 +-
 source3/smbd/files.c                |  34 +++
 source3/smbd/globals.h              |  10 +-
 source3/smbd/open.c                 | 576 +++++++++++++++++++++++++++---------
 source3/smbd/oplock.c               | 364 +++++++++++++++++------
 source3/smbd/proto.h                |  14 +
 source3/smbd/server.c               |   5 +
 source3/smbd/smb2_break.c           | 194 +++++++++++-
 source3/smbd/smb2_create.c          | 151 +++++++++-
 source3/smbd/smb2_negprot.c         |   4 +
 source3/smbd/smb2_server.c          |  25 ++
 source3/utils/status.c              |   2 +
 source3/wscript_build               |   6 +
 source4/torture/smb2/durable_open.c |   5 +-
 source4/torture/smb2/lease.c        |   5 +-
 24 files changed, 1381 insertions(+), 259 deletions(-)

diff --git a/libcli/smb/smb2_lease.c b/libcli/smb/smb2_lease.c
index 6fc26f2..70dd3d4 100644
--- a/libcli/smb/smb2_lease.c
+++ b/libcli/smb/smb2_lease.c
@@ -86,3 +86,9 @@ bool smb2_lease_push(const struct smb2_lease *lease, uint8_t *buf, size_t len)
 
 	return true;
 }
+
+bool smb2_lease_key_equal(const struct smb2_lease_key *k1,
+			  const struct smb2_lease_key *k2)
+{
+	return ((k1->data[0] == k2->data[0]) && (k1->data[1] == k2->data[1]));
+}
diff --git a/libcli/smb/smb2_lease.h b/libcli/smb/smb2_lease.h
index ba8178d..9db239d 100644
--- a/libcli/smb/smb2_lease.h
+++ b/libcli/smb/smb2_lease.h
@@ -32,5 +32,7 @@
 ssize_t smb2_lease_pull(const uint8_t *buf, size_t len,
 			struct smb2_lease *lease);
 bool smb2_lease_push(const struct smb2_lease *lease, uint8_t *buf, size_t len);
+bool smb2_lease_key_equal(const struct smb2_lease_key *k1,
+			  const struct smb2_lease_key *k2);
 
 #endif /* _LIBCLI_SMB_SMB2_LEASE_H_ */
diff --git a/selftest/knownfail b/selftest/knownfail
index 3d73495..5a9a865 100644
--- a/selftest/knownfail
+++ b/selftest/knownfail
@@ -187,25 +187,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.oplock.batch20
 ^samba3.smb2.oplock.stream1
 ^samba3.smb2.streams.rename
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..64bd043 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_INVALID_PARAMETER;
+	}
+
+	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 44f3ba1..94e9b8f 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,
@@ -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..39c537c 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 smbd_server_connection *sconn,
+				    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 ccea1e9..c9196d2 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,74 +1415,98 @@ 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;
 	}
 
 	switch (create_disposition) {
 	case FILE_SUPERSEDE:
 	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)
@@ -1489,88 +1520,330 @@ 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 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;
+	uint16_t i;
 
-	/* 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);
+	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;
+
+	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,
+	};
+
+	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;
+		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)));
-		return;
+	}
+
+	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;
+		granted &= ~SMB2_LEASE_READ;
 	}
 
-	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;
-	}
+	got_handle_lease = false;
+	got_oplock = false;
 
-	got_level2_oplock = false;
-	got_a_none_oplock = false;
+	for (i=0; i<d->num_share_modes; i++) {
+		struct share_mode_entry *e = &d->share_modes[i];
+		uint32_t e_lease_type;
 
-	for (i=0; i<lck->data->num_share_modes; i++) {
-		int op_type = lck->data->share_modes[i].op_type;
+		e_lease_type = get_lease_type(d, e);
 
-		if (LEVEL_II_OPLOCK_TYPE(op_type)) {
-			got_level2_oplock = true;
-		}
-		if (op_type == NO_OPLOCK) {
-			got_a_none_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;
 		}
-	}
-
-	/*
-	 * Match what was requested (fsp->oplock_type) with
- 	 * what was found in the existing share modes.
- 	 */
 
-	if (got_level2_oplock || got_a_none_oplock) {
-		if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) {
-			fsp->oplock_type = LEVEL_II_OPLOCK;
+		if ((e_lease_type & SMB2_LEASE_HANDLE) && !got_handle_lease &&
+		    !share_mode_stale_pid(d, i)) {
+			got_handle_lease = true;
 		}
-	}
 
-	/*
-	 * 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;
+		if ((e->op_type != LEASE_OPLOCK) && !got_oplock &&
+		    !share_mode_stale_pid(d, i)) {
+			got_oplock = true;
+		}
 	}
 
-	if (fsp->oplock_type == LEVEL_II_OPLOCK && !got_level2_oplock) {
+	if (is_stat_open(fsp->access_mask) &&
+	    ((create_disposition == FILE_OPEN) ||
+	     (create_disposition == FILE_OPEN_IF))) {
 		/*
-		 * We're the first level2 oplock. Indicate that in brlock.tdb.
+		 * No-overwrite stat open doesn't get oplocks
 		 */
-		struct byte_range_lock *brl;
+		granted = SMB2_LEASE_NONE;
+	}
+
+	if (oplock_request == LEASE_OPLOCK) {
+
+		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));
 
-		brl = brl_get_locks(talloc_tos(), fsp);
-		if (brl != NULL) {
-			brl_set_have_read_oplocks(brl, true);
-			TALLOC_FREE(brl);
+			if (!allow_level2) {
+				fsp->oplock_type = NO_OPLOCK;
+			}
+		}
+		if (got_handle_lease) {
+			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)));
+
+	status = set_file_oplock(fsp);
+	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(fsp->conn),
+			    req ? req->mid : 0,
+			    fsp->oplock_type, lease_idx)) {
+		return NT_STATUS_NO_MEMORY;
+	}
+	ret = update_num_read_oplocks(fsp, lck);
+	if (!ret) {
+		del_share_mode(lck, fsp);
+		return NT_STATUS_INTERNAL_ERROR;
+	}
+	return NT_STATUS_OK;
 }
 
 static bool request_timed_out(struct timeval request_time,
@@ -2465,8 +2738,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"));
@@ -2587,7 +2862,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);
@@ -2738,8 +3013,6 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
 		}
 	}
 
-	grant_fsp_oplock_type(fsp, lck, oplock_request);
-
 	/*
 	 * We have the share entry *locked*.....
 	 */
@@ -2798,13 +3071,6 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
 		fsp->access_mask = access_mask | FILE_READ_ATTRIBUTES;
 	}
 
-	if (file_existed) {
-		/* stat opens on existing files don't get oplocks. */
-		if (is_stat_open(open_access_mask)) {
-			fsp->oplock_type = NO_OPLOCK;
-		}
-	}
-
 	if (new_file_created) {
 		info = FILE_WAS_CREATED;
 	} else {
@@ -2824,20 +3090,12 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
 	 * file structs.
 	 */
 
-	status = set_file_oplock(fsp);
+	status = grant_fsp_oplock_type(req, fsp, lck, oplock_request,
+				       create_disposition, lease);
 	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. */
@@ -3331,7 +3589,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);
@@ -3816,6 +4074,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
  */
 
@@ -3871,6 +4171,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 17cb22e..c55fb3b 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);
 }
 
 /****************************************************************************
@@ -141,8 +139,69 @@ static void downgrade_file_oplock(files_struct *fsp)
 	sconn->oplocks.exclusive_open--;
 	sconn->oplocks.level_II_open++;
 	fsp->sent_oplock_break = NO_BREAK_SENT;
+}
 
-	TALLOC_FREE(fsp->oplock_timeout);
+uint32_t map_oplock_to_lease_type(uint16_t op_type)
+{
+	uint32_t ret;
+
+	switch(op_type) {
+	case BATCH_OPLOCK:
+	case BATCH_OPLOCK|EXCLUSIVE_OPLOCK:
+		ret = SMB2_LEASE_READ|SMB2_LEASE_WRITE|SMB2_LEASE_HANDLE;
+		break;
+	case EXCLUSIVE_OPLOCK:
+		ret = SMB2_LEASE_READ|SMB2_LEASE_WRITE;
+		break;
+	case LEVEL_II_OPLOCK:
+		ret = SMB2_LEASE_READ;
+		break;
+	default:
+		ret = SMB2_LEASE_NONE;
+		break;
+	}
+	return ret;
+}
+
+uint32_t get_lease_type(struct share_mode_data *d, struct share_mode_entry *e)
+{
+	if (e->op_type == LEASE_OPLOCK) {
+		return d->leases[e->lease_idx].current_state;
+	}
+	return map_oplock_to_lease_type(e->op_type);
+}
+
+bool update_num_read_oplocks(files_struct *fsp, struct share_mode_lock *lck)
+{
+	struct share_mode_data *d = lck->data;
+	struct byte_range_lock *br_lck;
+	uint32_t num_read_oplocks = 0;
+	uint32_t i;
+
+	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_READ) {
+			num_read_oplocks += 1;
+		}
+	}
+
+	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;
 }
 
 /****************************************************************************
@@ -167,44 +226,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 "
@@ -213,6 +234,16 @@ 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) {
+		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;
 }
@@ -224,7 +255,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)));
@@ -244,17 +274,78 @@ bool downgrade_oplock(files_struct *fsp)
 	}
 
 	downgrade_file_oplock(fsp);
+	TALLOC_FREE(fsp->oplock_timeout);
 
-	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: 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)));
 	}
 
 	TALLOC_FREE(lck);
 	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.
 ****************************************************************************/
@@ -426,7 +517,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,
@@ -467,23 +557,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 (!lp_level2_oplocks(SNUM(fsp->conn))) {
+		DEBUG(10, ("no level2 oplocks by config\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;
+	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
@@ -493,21 +602,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);
 }
 
@@ -575,6 +696,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,
@@ -593,6 +715,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;
 
 	/*
 	 * If this file is level II oplocked then we need
@@ -609,11 +732,23 @@ static void contend_level2_oplocks_begin_default(files_struct *fsp,
 		return;
 	}
 
+	num_read_oplocks = 0;
+
 	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;
 	}
+	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
@@ -622,7 +757,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;
@@ -630,6 +765,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"));
@@ -639,14 +781,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) {
@@ -654,15 +810,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;
 		}
 
@@ -677,15 +879,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 ));
@@ -693,13 +895,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 68c2da2..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,10 +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 5c079ec..a11cb5f 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_negprot.c b/source3/smbd/smb2_negprot.c
index 6904972..a27ab0c 100644
--- a/source3/smbd/smb2_negprot.c
+++ b/source3/smbd/smb2_negprot.c
@@ -230,6 +230,10 @@ NTSTATUS smbd_smb2_request_process_negprot(struct smbd_smb2_request *req)
 		capabilities |= SMB2_CAP_DFS;
 	}
 
+	if (protocol >= PROTOCOL_SMB2_10) {
+		capabilities |= SMB2_CAP_LEASING;
+	}
+
 	if ((protocol >= PROTOCOL_SMB2_24) &&
 	    (lp_smb_encrypt(-1) != SMB_SIGNING_OFF) &&
 	    (in_capabilities & SMB2_CAP_ENCRYPTION)) {
diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c
index 689bfd7..cff10d8 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 smbd_server_connection *sconn,
+				    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(sconn, 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 b589813..fba1738 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 54ba3a7..4328500 100755
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -618,6 +618,7 @@ bld.SAMBA3_LIBRARY('smbd_base',
                    LIBAFS
                    RPC_SERVICE
                    NDR_SMBXSRV
+                   LEASES_DB
                    LIBASYS
                    sysquotas
                    ccan-hash
@@ -635,9 +636,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',
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);			\
-- 
2.1.0.rc2.206.gedb03e5


From 423b00289a1cbb1c7e04cc4dad381ba379f18b99 Mon Sep 17 00:00:00 2001
From: Michael Adam <obnox at samba.org>
Date: Sun, 21 Sep 2014 09:32:25 +0200
Subject: [PATCH 04/15] smbd: use smbXsrv_connection as argument to
 smbd_smb2_send_lease_break()

Signed-off-by: Michael Adam <obnox at samba.org>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
 source3/smbd/globals.h     | 2 +-
 source3/smbd/smb2_server.c | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/source3/smbd/globals.h b/source3/smbd/globals.h
index 39c537c..f297ff7 100644
--- a/source3/smbd/globals.h
+++ b/source3/smbd/globals.h
@@ -250,7 +250,7 @@ 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 smbd_server_connection *sconn,
+NTSTATUS smbd_smb2_send_lease_break(struct smbXsrv_connection *xconn,
 				    struct smbXsrv_session *session,
 				    struct smbXsrv_tcon *tcon,
 				    uint16_t new_epoch,
diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c
index cff10d8..36ac0d7 100644
--- a/source3/smbd/smb2_server.c
+++ b/source3/smbd/smb2_server.c
@@ -2888,7 +2888,7 @@ 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 smbd_server_connection *sconn,
+NTSTATUS smbd_smb2_send_lease_break(struct smbXsrv_connection *xconn,
 				    struct smbXsrv_session *session,
 				    struct smbXsrv_tcon *tcon,
 				    uint16_t new_epoch,
@@ -2910,7 +2910,7 @@ NTSTATUS smbd_smb2_send_lease_break(struct smbd_server_connection *sconn,
 	SIVAL(body, 0x24, 0);		/* AccessMaskHint, MUST be 0 */
 	SIVAL(body, 0x28, 0);		/* ShareMaskHint, MUST be 0 */
 
-	return smbd_smb2_send_break(sconn, session, tcon, body, sizeof(body));
+	return smbd_smb2_send_break(xconn, session, tcon, body, sizeof(body));
 }
 
 static bool is_smb2_recvfile_write(struct smbd_smb2_request_read_state *state)
-- 
2.1.0.rc2.206.gedb03e5


From 5dca15378f565b490eee9ece0badbd24c941cd2a Mon Sep 17 00:00:00 2001
From: Michael Adam <obnox at samba.org>
Date: Sun, 21 Sep 2014 09:46:30 +0200
Subject: [PATCH 05/15] selftest: samba3.smb2.leases.v2_request_parent succeeds

Signed-off-by: Michael Adam <obnox at samba.org>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
 selftest/knownfail | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/selftest/knownfail b/selftest/knownfail
index 5a9a865..d4cae25 100644
--- a/selftest/knownfail
+++ b/selftest/knownfail
@@ -193,7 +193,7 @@
 ^samba4.smb2.ioctl.copy_chunk_\w*\(dc\)	# not supported by s4 ntvfs server
 ^samba3.smb2.dir.one
 ^samba3.smb2.dir.modify
-^samba3.smb2.lease.v2_request
+^samba3.smb2.lease.v2_request\(.*\)$
 ^samba3.smb2.oplock.batch20
 ^samba3.smb2.oplock.stream1
 ^samba3.smb2.streams.rename
-- 
2.1.0.rc2.206.gedb03e5


From 852386f73f5d37e2b25fa01ab24c32eae9ec730b 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 06/15] 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 c9196d2..3cc89f0 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1520,7 +1520,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)
 {
@@ -1669,8 +1669,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 c55fb3b..f80a3999 100644
--- a/source3/smbd/oplock.c
+++ b/source3/smbd/oplock.c
@@ -595,6 +595,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 a11cb5f..178dba9 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)
-- 
2.1.0.rc2.206.gedb03e5


From 17f309ddddf383a7b3ad6cee035bf3b450239c6b 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 07/15] 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>
---
 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 178dba9..abe6709 100644
--- a/source3/smbd/smb2_break.c
+++ b/source3/smbd/smb2_break.c
@@ -61,11 +61,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);
@@ -76,6 +71,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) {
@@ -186,12 +192,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);
-- 
2.1.0.rc2.206.gedb03e5


From c26846dabc28ae2d79788b46295780732b926095 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 08/15] 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>
---
 libcli/smb/smb2_lease.c          | 1 +
 librpc/idl/smb2_lease_struct.idl | 1 +
 source3/smbd/open.c              | 4 +++-
 source3/smbd/oplock.c            | 3 ++-
 source3/smbd/smb2_create.c       | 4 ++--
 5 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/libcli/smb/smb2_lease.c b/libcli/smb/smb2_lease.c
index 70dd3d4..41eafc9 100644
--- a/libcli/smb/smb2_lease.c
+++ b/libcli/smb/smb2_lease.c
@@ -43,6 +43,7 @@ ssize_t smb2_lease_pull(const uint8_t *buf, size_t len,
 	lease->lease_state = IVAL(buf, 16);
 	lease->lease_flags = IVAL(buf, 20);
 	lease->lease_duration = BVAL(buf, 24);
+	lease->lease_version = version;
 
 	switch (version) {
 	case 1:
diff --git a/librpc/idl/smb2_lease_struct.idl b/librpc/idl/smb2_lease_struct.idl
index be80d14..5ccd8a3 100644
--- a/librpc/idl/smb2_lease_struct.idl
+++ b/librpc/idl/smb2_lease_struct.idl
@@ -28,6 +28,7 @@ interface smb2_lease_struct
 		uint32 lease_flags;
 		hyper lease_duration;	/* should be 0 */
 		smb2_lease_key parent_lease_key;
+		uint16 lease_version;
 		uint16 lease_epoch;
 	} smb2_lease;
 };
\ No newline at end of file
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index 3cc89f0..2a4440a 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1663,7 +1663,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 f80a3999..d42058c 100644
--- a/source3/smbd/oplock.c
+++ b/source3/smbd/oplock.c
@@ -595,7 +595,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;
-- 
2.1.0.rc2.206.gedb03e5


From 21df42e811b2a994143f71cfd90605073d46069d 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 09/15] s3: leases - break to none with FILE_OVERWRITE

Signed-off-by: Volker Lendecke <vl at samba.org>
Reviewed-by: Jeremy Allison <jra 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 2a4440a..8f20d99 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1446,6 +1446,7 @@ static bool delay_for_oplock(files_struct *fsp,
 	switch (create_disposition) {
 	case FILE_SUPERSEDE:
 	case FILE_OVERWRITE_IF:
+	case FILE_OVERWRITE:
 		will_overwrite = true;
 		break;
 	default:
-- 
2.1.0.rc2.206.gedb03e5


From 040b56fc4e9fdd51386f25ba980e298168101882 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 10/15] 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 | 136 ++++++++++++++++++++++++++++------------------
 1 file changed, 82 insertions(+), 54 deletions(-)

diff --git a/source3/locking/locking.c b/source3/locking/locking.c
index 64bd043..7ccd8d4 100644
--- a/source3/locking/locking.c
+++ b/source3/locking/locking.c
@@ -618,6 +618,84 @@ 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);
+
+		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 +753,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 +853,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 +903,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;
 }
 
-- 
2.1.0.rc2.206.gedb03e5


From 97ab50f2e4d161af9dc3e36a466374cc94bac01d 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 11/15] 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 +++++
 source3/locking/locking.c        |   3 +-
 4 files changed, 440 insertions(+), 1 deletion(-)
 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_ */
diff --git a/source3/locking/locking.c b/source3/locking/locking.c
index 7ccd8d4..1c6707e 100644
--- a/source3/locking/locking.c
+++ b/source3/locking/locking.c
@@ -688,7 +688,8 @@ static void remove_share_mode_lease(struct share_mode_data *d,
 		NTSTATUS status;
 
 		status = leases_db_del(&client_guid,
-					&lease_key);
+					&lease_key,
+					&e->id);
 
 		DEBUG(10, ("%s: leases_db_del returned %s\n", __func__,
 			   nt_errstr(status)));
-- 
2.1.0.rc2.206.gedb03e5


From 42b15779c1b0f73665637c25fa9b7ea9c03cb1ec 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 12/15] 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 8f20d99..976dd16 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -4083,39 +4083,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;
 }
 
 /*
@@ -4174,10 +4283,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)
-- 
2.1.0.rc2.206.gedb03e5


From 129ee00ac920481013e85a68be53a7f11e37ae9d 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 13/15] 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 976dd16..8217a9a 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);
-- 
2.1.0.rc2.206.gedb03e5


From 6286187ffea14db4df3647a49e0c92fa0c4156f8 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 14/15] s3: leases: Don't set fsp->oplock_type before we've
 granted any oplocks.

It's not needed, and when we have leases it causes a crash in the call
stack when truncate is requested as follows:

open_file_ntcreate()->vfs_set_filelen()->smbd_contend_level2_oplocks_begin()->
	contend_level2_oplocks_begin_default().

Inside which we check if fsp->oplock_type == LEASE_TYPE.

In this case we have not yet added the entry in the share
mode database so fsp->lease == NULL.

Signed-off-by: Jeremy Allison <jra 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 8217a9a..754de89 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -2688,9 +2688,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;
 	}
-- 
2.1.0.rc2.206.gedb03e5


From dd46c3da906cd168af2e242277dfbc85d715695f 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 15/15] s3: smbd: Add "smb2 leases" parameter. Default "false".

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 docs-xml/smbdotconf/locking/smb2leases.xml | 19 +++++++++++++++++++
 lib/param/param_table.c                    |  9 +++++++++
 selftest/target/Samba3.pm                  |  1 +
 source3/param/loadparm.c                   |  1 +
 source3/smbd/smb2_negprot.c                |  2 +-
 5 files changed, 31 insertions(+), 1 deletion(-)
 create mode 100644 docs-xml/smbdotconf/locking/smb2leases.xml

diff --git a/docs-xml/smbdotconf/locking/smb2leases.xml b/docs-xml/smbdotconf/locking/smb2leases.xml
new file mode 100644
index 0000000..2927e56
--- /dev/null
+++ b/docs-xml/smbdotconf/locking/smb2leases.xml
@@ -0,0 +1,19 @@
+<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>
+</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..b6ff571 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 | FLAG_SHARE,
+	},
+	{
 		.label		= "locking",
 		.type		= P_BOOL,
 		.p_class	= P_LOCAL,
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
diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c
index d2afac7..a0f3eef 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());
 
diff --git a/source3/smbd/smb2_negprot.c b/source3/smbd/smb2_negprot.c
index a27ab0c..e88165b 100644
--- a/source3/smbd/smb2_negprot.c
+++ b/source3/smbd/smb2_negprot.c
@@ -230,7 +230,7 @@ NTSTATUS smbd_smb2_request_process_negprot(struct smbd_smb2_request *req)
 		capabilities |= SMB2_CAP_DFS;
 	}
 
-	if (protocol >= PROTOCOL_SMB2_10) {
+	if (protocol >= PROTOCOL_SMB2_10 && lp_smb2_leases()) {
 		capabilities |= SMB2_CAP_LEASING;
 	}
 
-- 
2.1.0.rc2.206.gedb03e5



More information about the samba-technical mailing list