Latest leases patchset - getting there !

Jeremy Allison jra at samba.org
Fri Nov 14 21:18:49 MST 2014


On Fri, Nov 14, 2014 at 04:55:00PM -0800, Jeremy Allison wrote:
> On Fri, Nov 14, 2014 at 09:26:17AM +0100, Stefan (metze) Metzmacher wrote:
> > > Here's the full fix.:-)
> > > 
> > > Only the timeout handling needs some test and fixes.
> > 
> > Sorry, here's the complete set on top of leases18.diff.txt
> 
> Ah, we spoke a little too soon. This patchset
> passes the smb2.lease tests, but causes the
> samba3.raw.oplock tests over SMB1 to fail.
> 
> I'll take a look at that test asap, and see
> if I can figure out a way to keep both tests
> working.

Found it I think. Patch attached. Problem was
we were always removing both SMB2_LEASE_HANDLE and SMB2_LEASE_WRITE
if there was an existing on-lease sharemode.

We should only remove SMB2_LEASE_HANDLE by
default for non-lease entries, not both.
If we remove both we get extra oplock
breaks we shouldn't.

Now passes raw.oplock, smb2.oplock (at
least as much as we always have, and
probably more :-) and smb2.lease.

I'll now run a full make test.

For anyone following along - patches attached are:

leases18.diff.txt	- Initial patchset.
todo20-2.diff.txt	- extra patches to pass smb2.lease
ontop.jra.txt		- Fixes for lease timeout + raw.oplock test (described above)

Last test to do is to check leases+Samba dynamic
share paths. I think we already work here we just
need a regression test to ensure we keep working.

Cheers,

Jeremy.
-------------- next part --------------
From f8f1bcace12a012a3a9974fdb66b0aaaf24c3e99 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Tue, 28 Oct 2014 15:31:46 -0700
Subject: [PATCH 01/46] s3: smbd: Implementation of SMB2.1 leases.

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

diff --git a/source3/include/smb.h b/source3/include/smb.h
index aab4ff5..7bace88 100644
--- a/source3/include/smb.h
+++ b/source3/include/smb.h
@@ -577,6 +577,7 @@ enum remote_arch_types {RA_UNKNOWN, RA_WFWG, RA_OS2, RA_WIN95, RA_WINNT,
 #define EXCLUSIVE_OPLOCK 		OPLOCK_EXCLUSIVE
 #define BATCH_OPLOCK 			OPLOCK_BATCH
 #define LEVEL_II_OPLOCK 		OPLOCK_LEVEL_II
+#define LEASE_OPLOCK			0x100
 
 /* The following are Samba-private. */
 #define INTERNAL_OPEN_ONLY 		0x8
diff --git a/source3/librpc/idl/open_files.idl b/source3/librpc/idl/open_files.idl
index 4278301..48e6bbe 100644
--- a/source3/librpc/idl/open_files.idl
+++ b/source3/librpc/idl/open_files.idl
@@ -3,6 +3,8 @@
 import "server_id.idl";
 import "security.idl";
 import "file_id.idl";
+import "smb2_lease_struct.idl";
+import "misc.idl";
 
 [
 	pointer_default(unique)
@@ -14,6 +16,7 @@ interface open_files
 		server_id	pid;
 		hyper		op_mid;
 		uint16		op_type;
+		uint32		lease_idx;
 		uint32		access_mask;
 		uint32		share_access;
 		uint32		private_options;
@@ -31,6 +34,31 @@ interface open_files
 		[skip] boolean8	stale;
 	} share_mode_entry;
 
+	typedef [public,bitmap8bit] bitmap {
+		SHARE_MODE_NO_CACHING = 0x00,
+		SHARE_MODE_READ_CACHING = 0x01,
+		SHARE_MODE_HANDLE_CACHING = 0x02,
+		SHARE_MODE_WRITE_CACHING = 0x04
+	} share_mode_caching;
+
+	typedef [public,flag(NDR_PAHEX)] struct {
+		GUID			client_guid;
+		smb2_lease_key		lease_key;
+		share_mode_caching      current_state;
+		/*
+		 * breaking_to_state indicates to which level
+		 * the current state is broken when a conflicting
+		 * request is processed. The calculation is as follows:
+		 *
+		 *   breaking_to_state = current_state;
+		 *   breaking_to_state &= ~(remove_state)
+		 *   breaking_to_state &= allowed_shared_state
+		 */
+		share_mode_caching      breaking_to_state;
+		boolean8                breaking;
+		uint16			epoch;
+	} share_mode_oplock;
+
 	typedef [public] struct {
 		uint32		name_hash;
 		security_token *delete_nt_token;
@@ -43,6 +71,8 @@ interface open_files
 		[string,charset(UTF8)] char *stream_name;
 		uint32 num_share_modes;
 		[size_is(num_share_modes)] share_mode_entry share_modes[];
+		uint32 num_leases;
+		[size_is(num_leases)] share_mode_oplock leases[];
 		uint32 num_delete_tokens;
 		[size_is(num_delete_tokens)] delete_token delete_tokens[];
 		timespec old_write_time;
diff --git a/source3/librpc/idl/wscript_build b/source3/librpc/idl/wscript_build
index c38fe7b..f9b1bd7 100644
--- a/source3/librpc/idl/wscript_build
+++ b/source3/librpc/idl/wscript_build
@@ -8,6 +8,7 @@ bld.SAMBA_PIDL_LIST('PIDL',
                     '''messaging.idl libnetapi.idl open_files.idl
                        perfcount.idl secrets.idl libnet_join.idl
                        smbXsrv.idl
+                       leases_db.idl
                     ''',
                     options='--includedir=%s --header --ndr-parser' % topinclude,
                     output_dir='../gen_ndr')
diff --git a/source3/librpc/wscript_build b/source3/librpc/wscript_build
index 77ae048..5c83cf2 100644
--- a/source3/librpc/wscript_build
+++ b/source3/librpc/wscript_build
@@ -17,7 +17,7 @@ bld.SAMBA3_SUBSYSTEM('NDR_MESSAGING',
 
 bld.SAMBA3_SUBSYSTEM('NDR_OPEN_FILES',
 	source='gen_ndr/ndr_open_files.c',
-	public_deps='ndr NDR_SERVER_ID NDR_FILE_ID NDR_SECURITY'
+	public_deps='ndr NDR_SERVER_ID NDR_FILE_ID NDR_SECURITY NDR_SMB2_LEASE_STRUCT'
 	)
 
 bld.SAMBA3_SUBSYSTEM('NDR_SMBXSRV',
@@ -25,6 +25,11 @@ bld.SAMBA3_SUBSYSTEM('NDR_SMBXSRV',
 	public_deps='ndr NDR_SERVER_ID NDR_SECURITY NDR_AUTH'
 	)
 
+bld.SAMBA3_SUBSYSTEM('NDR_LEASES_DB',
+	source='gen_ndr/ndr_leases_db.c',
+	public_deps='ndr'
+	)
+
 bld.SAMBA3_SUBSYSTEM('NDR_SECRETS',
 	source='gen_ndr/ndr_secrets.c',
 	public_deps='ndr'
diff --git a/source3/locking/locking.c b/source3/locking/locking.c
index a320068..170fa1d 100644
--- a/source3/locking/locking.c
+++ b/source3/locking/locking.c
@@ -46,6 +46,7 @@
 #include "messages.h"
 #include "util_tdb.h"
 #include "../librpc/gen_ndr/ndr_open_files.h"
+#include "locking/leases_db.h"
 
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_LOCKING
@@ -608,6 +609,7 @@ bool is_valid_share_mode_entry(const struct share_mode_entry *e)
 	num_props += ((e->op_type == NO_OPLOCK) ? 1 : 0);
 	num_props += (EXCLUSIVE_OPLOCK_TYPE(e->op_type) ? 1 : 0);
 	num_props += (LEVEL_II_OPLOCK_TYPE(e->op_type) ? 1 : 0);
+	num_props += (e->op_type == LEASE_OPLOCK);
 
 	if ((num_props > 1) && serverid_exists(&e->pid)) {
 		smb_panic("Invalid share mode entry");
@@ -694,7 +696,8 @@ void remove_stale_share_mode_entries(struct share_mode_data *d)
 }
 
 bool set_share_mode(struct share_mode_lock *lck, files_struct *fsp,
-		    uid_t uid, uint64_t mid, uint16 op_type)
+		    uid_t uid, uint64_t mid, uint16 op_type,
+		    uint32_t lease_idx)
 {
 	struct share_mode_data *d = lck->data;
 	struct share_mode_entry *tmp, *e;
@@ -716,6 +719,7 @@ bool set_share_mode(struct share_mode_lock *lck, files_struct *fsp,
 	e->access_mask = fsp->access_mask;
 	e->op_mid = mid;
 	e->op_type = op_type;
+	e->lease_idx = lease_idx;
 	e->time.tv_sec = fsp->open_time.tv_sec;
 	e->time.tv_usec = fsp->open_time.tv_usec;
 	e->id = fsp->file_id;
@@ -816,16 +820,70 @@ bool mark_share_mode_disconnected(struct share_mode_lock *lck,
 
 bool remove_share_oplock(struct share_mode_lock *lck, files_struct *fsp)
 {
+	struct share_mode_data *d = lck->data;
 	struct share_mode_entry *e;
+	uint16_t op_type;
+	uint32_t lease_idx;
+	uint32_t i;
 
 	e = find_share_mode_entry(lck, fsp);
 	if (e == NULL) {
 		return False;
 	}
 
+	op_type = e->op_type;
 	e->op_type = NO_OPLOCK;
-	lck->data->modified = True;
-	return True;
+
+	d->modified = True;
+
+	if (op_type != LEASE_OPLOCK) {
+		return true;
+	}
+
+	/*
+	 * This used to reference a lease. If there's no other one referencing
+	 * it, remove it.
+	 */
+
+	lease_idx = e->lease_idx;
+	e->lease_idx = UINT32_MAX;
+
+	for (i=0; i<d->num_share_modes; i++) {
+		if (d->share_modes[i].lease_idx == lease_idx) {
+			break;
+		}
+	}
+	if (i < d->num_share_modes) {
+		/*
+		 * Found another one
+		 */
+		return true;
+	}
+
+	d->num_leases -= 1;
+	d->leases[lease_idx] = d->leases[d->num_leases];
+
+	/*
+	 * We changed the lease array. Fix all references to it.
+	 */
+	for (i=0; i<d->num_share_modes; i++) {
+		if (d->share_modes[i].lease_idx == d->num_leases) {
+			d->share_modes[i].lease_idx = lease_idx;
+		}
+	}
+
+	{
+		NTSTATUS status;
+
+		status = leases_db_del(
+			&fsp->conn->sconn->client->connections->smb2.client.guid,
+			&fsp->lease->lease.lease_key);
+
+		DEBUG(10, ("%s: leases_db_del returned %s\n", __func__,
+			   nt_errstr(status)));
+	}
+
+	return true;
 }
 
 /*******************************************************************
@@ -846,6 +904,83 @@ bool downgrade_share_oplock(struct share_mode_lock *lck, files_struct *fsp)
 	return True;
 }
 
+/*
+ * "key" has been broken, it is referenced somewhere in "lck". If "fsp"
+ * applies to it, reset fsp->sent_oplock_break.
+ */
+
+bool fsp_lease_broken(struct share_mode_lock *lck,
+		      struct file_id lck_id,
+		      const struct smb2_lease_key *key,
+		      files_struct *fsp, uint32_t new_lease_state)
+{
+	struct share_mode_data *d = lck->data;
+	struct share_mode_entry *e;
+
+	if (!file_id_equal(&fsp->file_id, &lck_id)) {
+		return false;
+	}
+	if (fsp->oplock_type != LEASE_OPLOCK) {
+		return false;
+	}
+
+	e = find_share_mode_entry(lck, fsp);
+	if (e == NULL) {
+		DEBUG(1, ("downgrade_lease_fsps: Could not find share mode "
+			  "entry\n"));
+		return false;
+	}
+
+	if (!smb2_lease_key_equal(key, &d->leases[e->lease_idx].lease_key)) {
+		return false;
+	}
+	fsp->sent_oplock_break = NO_BREAK_SENT;
+	TALLOC_FREE(fsp->oplock_timeout);
+	fsp->lease->lease.lease_state = new_lease_state;
+	return true;
+}
+
+NTSTATUS downgrade_share_lease(struct smbd_server_connection *sconn,
+			       struct share_mode_lock *lck,
+			       const struct smb2_lease_key *key,
+			       uint32_t new_lease_state)
+{
+	struct share_mode_data *d = lck->data;
+	struct share_mode_oplock *l;
+	uint32_t i;
+
+	for (i=0; i<d->num_leases; i++) {
+		if (smb2_lease_key_equal(key, &d->leases[i].lease_key)) {
+			break;
+		}
+	}
+	if (i == d->num_leases) {
+		DEBUG(10, ("lease not found\n"));
+		return NT_STATUS_INVALID_PARAMETER;
+	}
+
+	l = &d->leases[i];
+
+	/*
+	 * Can't upgrade anything: l->current_state must be a strict bitwise
+	 * superset of new_lease_state
+	 */
+
+	if ((new_lease_state & l->current_state) != new_lease_state) {
+		DEBUG(10, ("Attempt to upgrade from %d to %d\n",
+			   (int)l->current_state, (int)new_lease_state));
+		return NT_STATUS_REQUEST_NOT_ACCEPTED;
+	}
+
+	l->current_state = new_lease_state;
+	l->breaking_to_state = 0;
+	l->breaking = 0;
+
+	d->modified = true;
+
+	return NT_STATUS_OK;
+}
+
 /****************************************************************************
  Adds a delete on close token.
 ****************************************************************************/
diff --git a/source3/locking/proto.h b/source3/locking/proto.h
index 8eccff8..94e9b8f 100644
--- a/source3/locking/proto.h
+++ b/source3/locking/proto.h
@@ -167,13 +167,22 @@ void get_file_infos(struct file_id id,
 bool is_valid_share_mode_entry(const struct share_mode_entry *e);
 bool share_mode_stale_pid(struct share_mode_data *d, uint32_t idx);
 bool set_share_mode(struct share_mode_lock *lck, files_struct *fsp,
-		    uid_t uid, uint64_t mid, uint16 op_type);
+		    uid_t uid, uint64_t mid, uint16 op_type,
+		    uint32_t lease_idx);
 void remove_stale_share_mode_entries(struct share_mode_data *d);
 bool del_share_mode(struct share_mode_lock *lck, files_struct *fsp);
 bool mark_share_mode_disconnected(struct share_mode_lock *lck,
 				  struct files_struct *fsp);
 bool remove_share_oplock(struct share_mode_lock *lck, files_struct *fsp);
 bool downgrade_share_oplock(struct share_mode_lock *lck, files_struct *fsp);
+bool fsp_lease_broken(struct share_mode_lock *lck,
+		      struct file_id lck_id,
+		      const struct smb2_lease_key *key,
+		      files_struct *fsp, uint32_t new_lease_state);
+NTSTATUS downgrade_share_lease(struct smbd_server_connection *sconn,
+			       struct share_mode_lock *lck,
+			       const struct smb2_lease_key *key,
+			       uint32_t new_lease_state);
 bool get_delete_on_close_token(struct share_mode_lock *lck,
 				uint32_t name_hash,
 				const struct security_token **pp_nt_tok,
diff --git a/source3/smbd/durable.c b/source3/smbd/durable.c
index 9489cf1..660865d 100644
--- a/source3/smbd/durable.c
+++ b/source3/smbd/durable.c
@@ -168,7 +168,7 @@ NTSTATUS vfs_default_durable_disconnect(struct files_struct *fsp,
 		return NT_STATUS_INVALID_PARAMETER;
 	}
 
-	if (!BATCH_OPLOCK_TYPE(fsp->oplock_type)) {
+	if ((fsp_lease_type(fsp) & SMB2_LEASE_HANDLE) == 0) {
 		return NT_STATUS_NOT_SUPPORTED;
 	}
 
@@ -724,6 +724,32 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
 	fsp->aio_write_behind = false;
 	fsp->oplock_type = e->op_type;
 
+	if (fsp->oplock_type == LEASE_OPLOCK) {
+		struct share_mode_oplock *o = &lck->data->leases[e->lease_idx];
+		struct smb2_lease_key key;
+
+		key.data[0] = o->lease_key.data[0];
+		key.data[1] = o->lease_key.data[1];
+
+		fsp->lease = find_fsp_lease(fsp, &key);
+
+		if (fsp->lease != NULL) {
+			fsp->lease->ref_count += 1;
+		} else {
+			fsp->lease = talloc_zero(fsp->conn->sconn,
+						 struct fsp_lease);
+			if (fsp->lease == NULL) {
+				TALLOC_FREE(lck);
+				fsp_free(fsp);
+				return NT_STATUS_NO_MEMORY;
+			}
+			fsp->lease->ref_count = 1;
+			fsp->lease->lease.lease_key = key;
+			fsp->lease->lease.lease_state = o->current_state;
+			fsp->lease->lease.lease_epoch = o->epoch;
+		}
+	}
+
 	fsp->initial_allocation_size = cookie.initial_allocation_size;
 	fsp->fh->position_information = cookie.position_information;
 	fsp->update_write_time_triggered = cookie.update_write_time_triggered;
diff --git a/source3/smbd/files.c b/source3/smbd/files.c
index a9e8357..13d1138 100644
--- a/source3/smbd/files.c
+++ b/source3/smbd/files.c
@@ -387,6 +387,24 @@ files_struct *file_find_di_next(files_struct *start_fsp)
 	return NULL;
 }
 
+struct files_struct *file_find_one_fsp_from_lease_key(
+	struct smbd_server_connection *sconn,
+	const struct smb2_lease_key *lease_key)
+{
+	struct files_struct *fsp;
+
+	for (fsp = sconn->files; fsp; fsp=fsp->next) {
+		if ((fsp->lease != NULL) &&
+		    (fsp->lease->lease.lease_key.data[0] ==
+		     lease_key->data[0]) &&
+		    (fsp->lease->lease.lease_key.data[1] ==
+		     lease_key->data[1])) {
+			return fsp;
+		}
+	}
+	return NULL;
+}
+
 /****************************************************************************
  Find any fsp open with a pathname below that of an already open path.
 ****************************************************************************/
@@ -472,6 +490,14 @@ void fsp_free(files_struct *fsp)
 		fsp->fh->ref_count--;
 	}
 
+	if (fsp->lease != NULL) {
+		if (fsp->lease->ref_count == 1) {
+			TALLOC_FREE(fsp->lease);
+		} else {
+			fsp->lease->ref_count--;
+		}
+	}
+
 	fsp->conn->num_files_open--;
 
 	/* this is paranoia, just in case someone tries to reuse the
@@ -740,3 +766,11 @@ NTSTATUS fsp_set_smb_fname(struct files_struct *fsp,
 			smb_fname_str_dbg(fsp->fsp_name),
 			&fsp->name_hash);
 }
+
+uint32_t fsp_lease_type(struct files_struct *fsp)
+{
+	if (fsp->oplock_type == LEASE_OPLOCK) {
+		return fsp->lease->lease.lease_state;
+	}
+	return map_oplock_to_lease_type(fsp->oplock_type);
+}
diff --git a/source3/smbd/globals.h b/source3/smbd/globals.h
index 36e7f0f..f297ff7 100644
--- a/source3/smbd/globals.h
+++ b/source3/smbd/globals.h
@@ -250,6 +250,14 @@ NTSTATUS smbd_smb2_send_oplock_break(struct smbXsrv_connection *xconn,
 				     struct smbXsrv_tcon *tcon,
 				     struct smbXsrv_open *op,
 				     uint8_t oplock_level);
+NTSTATUS smbd_smb2_send_lease_break(struct smbXsrv_connection *xconn,
+				    struct smbXsrv_session *session,
+				    struct smbXsrv_tcon *tcon,
+				    uint16_t new_epoch,
+				    uint32_t lease_flags,
+				    struct smb2_lease_key *lease_key,
+				    uint32_t current_lease_state,
+				    uint32_t new_lease_state);
 
 NTSTATUS smbd_smb2_request_pending_queue(struct smbd_smb2_request *req,
 					 struct tevent_req *subreq,
@@ -298,7 +306,7 @@ void smbd_smb2_request_dispatch_immediate(struct tevent_context *ctx,
 struct deferred_open_record;
 
 /* SMB1 -> SMB2 glue. */
-void send_break_message_smb2(files_struct *fsp, int level);
+void send_break_message_smb2(files_struct *fsp, uint32_t break_to);
 struct blocking_lock_record *get_pending_smb2req_blr(struct smbd_smb2_request *smb2req);
 bool push_blocking_lock_request_smb2( struct byte_range_lock *br_lck,
 				struct smb_request *req,
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index 1952823..7ac8fd8 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -35,6 +35,12 @@
 #include "serverid.h"
 #include "messages.h"
 #include "source3/lib/dbwrap/dbwrap_watch.h"
+#include "locking/leases_db.h"
+
+static bool is_same_lease(const struct share_mode_data *d,
+			  const struct share_mode_entry *e,
+			  const struct smb2_lease *lease);
+
 
 extern const struct generic_mapping file_generic_mapping;
 
@@ -1257,6 +1263,10 @@ static NTSTATUS send_break_message(struct messaging_context *msg_ctx,
 	share_mode_entry_to_message(msg, exclusive);
 
 	/* Overload entry->op_type */
+	/*
+	 * This is a cut from uint32 to uint16, but so far only the lower 3
+	 * bits (LEASE_WRITE/HANDLE/READ are used anyway.
+	 */
 	SSVAL(msg,OP_BREAK_MSG_OP_TYPE_OFFSET, break_to);
 
 	status = messaging_send_buf(msg_ctx, exclusive->pid,
@@ -1376,31 +1386,28 @@ static bool validate_oplock_types(struct share_mode_lock *lck)
 
 static bool delay_for_oplock(files_struct *fsp,
 			     int oplock_request,
+			     const struct smb2_lease *lease,
 			     struct share_mode_lock *lck,
 			     bool have_sharing_violation,
 			     uint32_t create_disposition)
 {
 	struct share_mode_data *d = lck->data;
-	struct share_mode_entry *entry;
 	uint32_t num_non_stat_opens = 0;
 	uint32_t i;
-	uint16_t break_to;
+	bool have_broken = false;
+	bool will_overwrite;
 
-	if ((oplock_request & INTERNAL_OPEN_ONLY) || is_stat_open(fsp->access_mask)) {
+	if ((oplock_request & INTERNAL_OPEN_ONLY) ||
+	    is_stat_open(fsp->access_mask)) {
 		return false;
 	}
+
 	for (i=0; i<d->num_share_modes; i++) {
 		struct share_mode_entry *e = &d->share_modes[i];
 		if (e->op_type == NO_OPLOCK && is_stat_open(e->access_mask)) {
 			continue;
 		}
 		num_non_stat_opens += 1;
-
-		/*
-		 * We found the a non-stat open, which in the exclusive/batch
-		 * case will be inspected further down.
-		 */
-		entry = e;
 	}
 	if (num_non_stat_opens == 0) {
 		/*
@@ -1408,24 +1415,31 @@ static bool delay_for_oplock(files_struct *fsp,
 		 */
 		return false;
 	}
-	if (num_non_stat_opens != 1) {
-		/*
-		 * More than one open around. There can't be any exclusive or
-		 * batch left, this is all level2.
-		 */
-		return false;
+
+	if (have_sharing_violation) {
+		for (i=0; i<d->num_share_modes; i++) {
+			struct share_mode_entry *e = &d->share_modes[i];
+			uint32_t e_lease_type = get_lease_type(d, e);
+
+			if (!(e_lease_type & SMB2_LEASE_HANDLE)) {
+				continue;
+			}
+			if (is_same_lease(d, e, lease)) {
+				continue;
+			}
+			if (share_mode_stale_pid(d, i)) {
+				continue;
+			}
+			send_break_message(fsp->conn->sconn->msg_ctx, e,
+					   e_lease_type & ~SMB2_LEASE_HANDLE);
+			have_broken = true;
+		}
 	}
 
-	if (server_id_is_disconnected(&entry->pid)) {
-		/*
-		 * TODO: clean up.
-		 * This could be achieved by sending a break message
-		 * to ourselves. Special considerations for files
-		 * with delete_on_close flag set!
-		 *
-		 * For now we keep it simple and do not
-		 * allow delete on close for durable handles.
-		 */
+	if (have_broken) {
+		return true;
+	}
+	if (have_sharing_violation) {
 		return false;
 	}
 
@@ -1433,50 +1447,67 @@ static bool delay_for_oplock(files_struct *fsp,
 	case FILE_SUPERSEDE:
 	case FILE_OVERWRITE:
 	case FILE_OVERWRITE_IF:
-		break_to = NO_OPLOCK;
+		will_overwrite = true;
 		break;
 	default:
-		break_to = LEVEL_II_OPLOCK;
+		will_overwrite = false;
 		break;
 	}
 
-	if (have_sharing_violation && (entry->op_type & BATCH_OPLOCK)) {
-		if (share_mode_stale_pid(d, 0)) {
-			return false;
+	for (i=0; i<d->num_share_modes; i++) {
+		struct share_mode_entry *e = &d->share_modes[i];
+		uint32_t e_lease_type = get_lease_type(d, e);
+
+		DEBUG(10, ("entry %u: e_lease_type %u, will_overwrite: %u\n",
+			   (unsigned)i, (unsigned)e_lease_type,
+			   (unsigned)will_overwrite));
+
+		if (e_lease_type & SMB2_LEASE_WRITE) {
+			uint32_t break_to;
+
+			if (share_mode_stale_pid(d, i)) {
+				return false;
+			}
+			if ((e->op_type == LEASE_OPLOCK) &&
+			    (lease != NULL) &&
+			    smb2_lease_key_equal(
+				    &lease->lease_key,
+				    &d->leases[e->lease_idx].lease_key)) {
+				return false;
+			}
+
+			break_to = e_lease_type & ~SMB2_LEASE_WRITE;
+			if (will_overwrite) {
+				/*
+				 * There's no H only lease that we could break
+				 * to
+				 */
+				break_to = SMB2_LEASE_NONE;
+			}
+
+			DEBUG(10, ("breaking SMB2_LEASE_WRITE to %d\n",
+				   (int)break_to));
+			send_break_message(fsp->conn->sconn->msg_ctx, e,
+					   break_to);
+			return true;
 		}
-		send_break_message(fsp->conn->sconn->msg_ctx, entry, break_to);
-		return true;
-	}
-	if (have_sharing_violation) {
-		/*
-		 * Non-batch exclusive is not broken if we have a sharing
-		 * violation
-		 */
-		return false;
-	}
-	if (LEVEL_II_OPLOCK_TYPE(entry->op_type) &&
-	    (break_to == NO_OPLOCK)) {
-		if (share_mode_stale_pid(d, 0)) {
-			return false;
+
+		if (will_overwrite && (e_lease_type & SMB2_LEASE_READ)) {
+			if (share_mode_stale_pid(d, i)) {
+				continue;
+			}
+			DEBUG(10, ("breaking SMB2_LEASE_READ\n"));
+			send_break_message(fsp->conn->sconn->msg_ctx, e,
+					   SMB2_LEASE_NONE);
+			/*
+			 * This is an async break. No need to wait for a
+			 * response.
+			 */
+			continue;
 		}
-		DEBUG(10, ("Asynchronously breaking level2 oplock for "
-			   "create_disposition=%u\n",
-			   (unsigned)create_disposition));
-		send_break_message(fsp->conn->sconn->msg_ctx, entry, break_to);
-		return false;
-	}
-	if (!EXCLUSIVE_OPLOCK_TYPE(entry->op_type)) {
-		/*
-		 * No break for NO_OPLOCK or LEVEL2_OPLOCK oplocks
-		 */
-		return false;
-	}
-	if (share_mode_stale_pid(d, 0)) {
-		return false;
 	}
 
-	send_break_message(fsp->conn->sconn->msg_ctx, entry, break_to);
-	return true;
+	return have_broken;
 }
 
 static bool file_has_brlocks(files_struct *fsp)
@@ -1490,74 +1521,302 @@ static bool file_has_brlocks(files_struct *fsp)
 	return (brl_num_locks(br_lck) > 0);
 }
 
-static NTSTATUS grant_fsp_oplock_type(struct smb_request *req,
-				      struct files_struct *fsp,
-				      struct share_mode_lock *lck,
-				      int oplock_request)
+static int find_share_mode_oplock(struct share_mode_data *d,
+				  const struct GUID *client_guid,
+				  const struct smb2_lease_key *key)
 {
-	bool allow_level2 = (global_client_caps & CAP_LEVEL_II_OPLOCKS) &&
-		            lp_level2_oplocks(SNUM(fsp->conn));
-	bool got_level2_oplock, got_a_none_oplock;
 	uint32_t i;
-	bool ok;
+
+	for (i=0; i<d->num_leases; i++) {
+		struct share_mode_oplock *l = &d->leases[i];
+		if (GUID_equal(client_guid, &l->client_guid) &&
+		    smb2_lease_key_equal(key, &l->lease_key)) {
+			return i;
+		}
+	}
+	return -1;
+}
+
+static bool is_same_lease(const struct share_mode_data *d,
+			  const struct share_mode_entry *e,
+			  const struct smb2_lease *lease)
+{
+	if (e->op_type != LEASE_OPLOCK) {
+		return false;
+	}
+	if (lease == NULL) {
+		return false;
+	}
+	return smb2_lease_key_equal(&d->leases[e->lease_idx].lease_key,
+				    &lease->lease_key);
+}
+
+struct fsp_lease *find_fsp_lease(files_struct *new_fsp,
+				 const struct smb2_lease_key *key)
+{
+	files_struct *fsp;
+
+	/*
+	 * TODO: Measure how expensive this loop is with thousands of open
+	 * handles...
+	 */
+
+	for (fsp = file_find_di_first(new_fsp->conn->sconn, new_fsp->file_id);
+	     fsp != NULL;
+	     fsp = file_find_di_next(fsp)) {
+
+		if (fsp == new_fsp) {
+			continue;
+		}
+		if (fsp->oplock_type != LEASE_OPLOCK) {
+			continue;
+		}
+		if (smb2_lease_key_equal(&fsp->lease->lease.lease_key, key)) {
+			return fsp->lease;
+		}
+	}
+
+	return NULL;
+}
+
+static NTSTATUS grant_fsp_lease(files_struct *fsp, struct share_mode_data *d,
+				const struct smb2_lease *lease,
+				uint32_t *p_lease_idx,
+				uint32_t granted)
+{
+	const struct GUID *client_guid;
+	struct share_mode_oplock *o;
+	struct share_mode_oplock *tmp;
 	NTSTATUS status;
+	int idx;
+
+
+	/*
+	 * TODO: in future we can have multiple connections...
+	 */
+	client_guid = &fsp->conn->sconn->client->connections->smb2.client.guid;
+
+	idx = find_share_mode_oplock(d, client_guid, &lease->lease_key);
+
+	if (idx != -1) {
+
+		bool do_upgrade;
+		uint32_t existing, requested;
+
+		fsp->lease = find_fsp_lease(fsp, &lease->lease_key);
+		if (fsp->lease == NULL) {
+			DEBUG(1, ("Did not find existing lease for file %s\n",
+				  fsp_str_dbg(fsp)));
+			return NT_STATUS_INTERNAL_ERROR;
+		}
+		fsp->lease->ref_count += 1;
+
+		*p_lease_idx = idx;
+		o = &d->leases[idx];
+
+		/*
+		 * Upgrade only if the requested lease is a strict upgrade.
+		 */
+		existing = o->current_state;
+		requested = lease->lease_state;
+
+		/*
+		 * Tricky: This test makes sure that "requested" is a
+		 * strict bitwise superset of "existing".
+		 */
+		do_upgrade = ((existing & requested) == existing);
+
+		/*
+		 * Upgrade only if other leases don't prevent what was asked
+		 * for.
+		 */
+		do_upgrade &= (granted == requested);
+
+		DEBUG(10, ("existing=%"PRIu32", requested=%"PRIu32", "
+			   "granted=%"PRIu32", do_upgrade=%d\n",
+			   existing, requested, granted, (int)do_upgrade));
+
+		if (do_upgrade) {
+			o->current_state = granted;
+		}
+		fsp->lease->lease.lease_state = o->current_state;
+		return NT_STATUS_OK;
+	}
+
+	/*
+	 * Create new lease
+	 */
+
+	tmp = talloc_realloc(d, d->leases, struct share_mode_oplock,
+			     d->num_leases+1);
+	if (tmp == NULL) {
+		/*
+		 * See [MS-SMB2]
+		 */
+		return NT_STATUS_INSUFFICIENT_RESOURCES;
+	}
+	d->leases = tmp;
+
+	fsp->lease = talloc(fsp->conn->sconn, struct fsp_lease);
+	if (fsp->lease == NULL) {
+		return NT_STATUS_INSUFFICIENT_RESOURCES;
+	}
+	fsp->lease->ref_count = 1;
+	fsp->lease->lease = *lease;
+	fsp->lease->lease.lease_state = granted;
+	fsp->lease->lease.lease_epoch += 1;
+
+	*p_lease_idx = d->num_leases;
 
-	/* Start by granting what the client asked for,
-	   but ensure no SAMBA_PRIVATE bits can be set. */
-	fsp->oplock_type = (oplock_request & ~SAMBA_PRIVATE_OPLOCK_MASK);
+	d->leases[d->num_leases] = (struct share_mode_oplock) {
+		.client_guid = *client_guid,
+		.lease_key = lease->lease_key,
+		.epoch = lease->lease_epoch,
+		.current_state = granted,
+	};
 
-	if (fsp->oplock_type == NO_OPLOCK) {
-		goto type_selected;
+	status = leases_db_add(client_guid, &lease->lease_key,
+			       &fsp->file_id, fsp->fsp_name->base_name,
+			       fsp->fsp_name->stream_name);
+	if (!NT_STATUS_IS_OK(status)) {
+		DEBUG(10, ("%s: leases_db_add failed: %s\n", __func__,
+			   nt_errstr(status)));
+		TALLOC_FREE(fsp->lease);
+		return NT_STATUS_INSUFFICIENT_RESOURCES;
 	}
 
+	d->num_leases += 1;
+	d->modified = true;
+
+	return NT_STATUS_OK;
+
+}
+
+static NTSTATUS grant_fsp_oplock_type(struct smb_request *req, files_struct *fsp,
+				      struct share_mode_lock *lck,
+				      int oplock_request,
+				      uint32_t create_disposition,
+				      struct smb2_lease *lease)
+{
+	struct share_mode_data *d = lck->data;
+	bool got_handle_lease, got_oplock;
+	uint32_t i;
+	uint32_t granted;
+	uint32_t lease_idx = UINT32_MAX;
+	NTSTATUS status;
+	bool ret;
+
 	if (oplock_request & INTERNAL_OPEN_ONLY) {
 		/* No oplocks on internal open. */
-		fsp->oplock_type = NO_OPLOCK;
-		goto type_selected;
+		oplock_request = NO_OPLOCK;
+		DEBUG(10,("grant_fsp_oplock_type: oplock type 0x%x on file %s\n",
+			fsp->oplock_type, fsp_str_dbg(fsp)));
+	}
+
+	if (oplock_request == LEASE_OPLOCK) {
+		granted = lease->lease_state;
+
+		if ((granted & (SMB2_LEASE_READ|SMB2_LEASE_WRITE)) == 0) {
+			DEBUG(10, ("No read or write lease requested\n"));
+			granted = SMB2_LEASE_NONE;
+		}
+		if (granted == SMB2_LEASE_WRITE) {
+			DEBUG(10, ("pure write lease requested\n"));
+			granted = SMB2_LEASE_NONE;
+		}
+		if (granted == (SMB2_LEASE_WRITE|SMB2_LEASE_HANDLE)) {
+			DEBUG(10, ("write and handle lease requested\n"));
+			granted = SMB2_LEASE_NONE;
+		}
+	} else {
+		granted = map_oplock_to_lease_type(
+			oplock_request & ~SAMBA_PRIVATE_OPLOCK_MASK);
 	}
 
 	if (lp_locking(fsp->conn->params) && file_has_brlocks(fsp)) {
 		DEBUG(10,("grant_fsp_oplock_type: file %s has byte range locks\n",
 			fsp_str_dbg(fsp)));
-		fsp->oplock_type = NO_OPLOCK;
-		goto type_selected;
+		granted &= ~SMB2_LEASE_READ;
 	}
 
-	got_level2_oplock = false;
-	got_a_none_oplock = false;
+	got_handle_lease = false;
+	got_oplock = false;
+
+	for (i=0; i<d->num_share_modes; i++) {
+		struct share_mode_entry *e = &d->share_modes[i];
+		uint32_t e_lease_type;
 
-	for (i=0; i<lck->data->num_share_modes; i++) {
-		int op_type = lck->data->share_modes[i].op_type;
+		e_lease_type = get_lease_type(d, e);
 
-		if (LEVEL_II_OPLOCK_TYPE(op_type)) {
-			got_level2_oplock = true;
+		if ((granted & SMB2_LEASE_WRITE) &&
+		    !is_same_lease(d, e, lease) &&
+		    !share_mode_stale_pid(d, i)) {
+			/*
+			 * Can grant only one writer
+			 */
+			granted &= ~SMB2_LEASE_WRITE;
+		}
+
+		if ((e_lease_type & SMB2_LEASE_HANDLE) && !got_handle_lease &&
+		    !share_mode_stale_pid(d, i)) {
+			got_handle_lease = true;
 		}
-		if (op_type == NO_OPLOCK) {
-			got_a_none_oplock = true;
+
+		if ((e->op_type != LEASE_OPLOCK) && !got_oplock &&
+		    !share_mode_stale_pid(d, i)) {
+			got_oplock = true;
 		}
 	}
 
-	/*
-	 * Match what was requested (fsp->oplock_type) with
- 	 * what was found in the existing share modes.
- 	 */
+	if (oplock_request == LEASE_OPLOCK) {
 
-	if (got_level2_oplock || got_a_none_oplock) {
-		if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) {
+		fsp->oplock_type = LEASE_OPLOCK;
+
+		if (got_oplock) {
+			granted &= SMB2_LEASE_READ;
+		}
+
+		status = grant_fsp_lease(fsp, lck->data, lease, &lease_idx,
+					 granted);
+		if (!NT_STATUS_IS_OK(status)) {
+			return status;
+
+		}
+		lease->lease_state = d->leases[lease_idx].current_state;
+		DEBUG(10, ("lease_state=%d\n", lease->lease_state));
+	} else {
+		switch (granted) {
+		case SMB2_LEASE_READ|SMB2_LEASE_WRITE|SMB2_LEASE_HANDLE:
+			fsp->oplock_type = BATCH_OPLOCK|EXCLUSIVE_OPLOCK;
+			break;
+		case SMB2_LEASE_READ|SMB2_LEASE_WRITE:
+			fsp->oplock_type = EXCLUSIVE_OPLOCK;
+			break;
+		case SMB2_LEASE_READ|SMB2_LEASE_HANDLE:
+		case SMB2_LEASE_READ:
 			fsp->oplock_type = LEVEL_II_OPLOCK;
+			break;
+		default:
+			fsp->oplock_type = NO_OPLOCK;
+			break;
 		}
-	}
+		if (fsp->oplock_type == LEVEL_II_OPLOCK) {
+			bool allow_level2 =
+				(global_client_caps & CAP_LEVEL_II_OPLOCKS) &&
+				lp_level2_oplocks(SNUM(fsp->conn));
 
-	/*
-	 * Don't grant level2 to clients that don't want them
-	 * or if we've turned them off.
-	 */
-	if (fsp->oplock_type == LEVEL_II_OPLOCK && !allow_level2) {
-		fsp->oplock_type = NO_OPLOCK;
-		goto type_selected;
+			if (!allow_level2) {
+				fsp->oplock_type = NO_OPLOCK;
+			}
+		}
+		if (got_handle_lease) {
+			fsp->oplock_type = NO_OPLOCK;
+		}
 	}
 
- type_selected:
+	DEBUG(10,("grant_fsp_oplock_type: oplock type 0x%x on file %s\n",
+		  fsp->oplock_type, fsp_str_dbg(fsp)));
+
 	status = set_file_oplock(fsp);
 	if (!NT_STATUS_IS_OK(status)) {
 		/*
@@ -1566,22 +1825,16 @@ static NTSTATUS grant_fsp_oplock_type(struct smb_request *req,
 		fsp->oplock_type = NO_OPLOCK;
 	}
 
-	ok = set_share_mode(lck, fsp, get_current_uid(fsp->conn),
+	if (!set_share_mode(lck, fsp, get_current_uid(fsp->conn),
 			    req ? req->mid : 0,
-			    fsp->oplock_type);
-	if (!ok) {
+			    fsp->oplock_type, lease_idx)) {
 		return NT_STATUS_NO_MEMORY;
 	}
-
-	ok = update_num_read_oplocks(fsp, lck);
-	if (!ok) {
+	ret = update_num_read_oplocks(fsp, lck);
+	if (!ret) {
 		del_share_mode(lck, fsp);
 		return NT_STATUS_INTERNAL_ERROR;
 	}
-
-	DEBUG(10,("grant_fsp_oplock_type: oplock type 0x%x on file %s\n",
-		  fsp->oplock_type, fsp_str_dbg(fsp)));
-
 	return NT_STATUS_OK;
 }
 
@@ -2474,8 +2727,10 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
 			smb_panic("validate_oplock_types failed");
 		}
 
-		if (delay_for_oplock(fsp, 0, lck, false, create_disposition)) {
-			schedule_defer_open(lck, fsp->file_id, request_time, req);
+		if (delay_for_oplock(fsp, 0, lease, lck, false,
+				     create_disposition)) {
+			schedule_defer_open(lck, fsp->file_id, request_time,
+					    req);
 			TALLOC_FREE(lck);
 			DEBUG(10, ("Sent oplock break request to kernel "
 				   "oplock holder\n"));
@@ -2596,7 +2851,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
 
 	if ((req != NULL) &&
 	    delay_for_oplock(
-		    fsp, oplock_request, lck,
+		    fsp, oplock_request, lease, lck,
 		    NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION),
 		    create_disposition)) {
 		schedule_defer_open(lck, fsp->file_id, request_time, req);
@@ -2807,7 +3062,7 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
 
 	if (file_existed) {
 		/*
-		 * stat opens on existing files don't get oplocks.
+		 * stat opens on existing files don't get oplocks or leases.
 		 *
 		 * Note that we check for stat open on the *open_access_mask*,
 		 * i.e. the access mask we actually used to do the open,
@@ -2817,7 +3072,11 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
 		 * which adds FILE_WRITE_DATA to open_access_mask.
 		 */
 		if (is_stat_open(open_access_mask)) {
-			oplock_request = NO_OPLOCK;
+			if (lease) {
+				lease->lease_state = SMB2_LEASE_NONE;
+			} else {
+				oplock_request = NO_OPLOCK;
+			}
 		}
 	}
 
@@ -2839,7 +3098,9 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn,
 	 * Setup the oplock info in both the shared memory and
 	 * file structs.
 	 */
-	status = grant_fsp_oplock_type(req, fsp, lck, oplock_request);
+
+	status = grant_fsp_oplock_type(req, fsp, lck, oplock_request,
+				       create_disposition, lease);
 	if (!NT_STATUS_IS_OK(status)) {
 		TALLOC_FREE(lck);
 		fd_close(fsp);
@@ -3337,7 +3598,7 @@ static NTSTATUS open_directory(connection_struct *conn,
 	}
 
 	if (!set_share_mode(lck, fsp, get_current_uid(conn),
-			    req ? req->mid : 0, NO_OPLOCK)) {
+			    req ? req->mid : 0, NO_OPLOCK, UINT32_MAX)) {
 		TALLOC_FREE(lck);
 		fd_close(fsp);
 		file_free(req, fsp);
@@ -3822,6 +4083,48 @@ static NTSTATUS inherit_new_acl(files_struct *fsp)
 }
 
 /*
+ * If we already have a lease, it must match the new file id. [MS-SMB2]
+ * 3.3.5.9.8 speaks about INVALID_PARAMETER if an already used lease key is
+ * used for a different file name.
+ */
+
+struct lease_fname_match_state {
+	const struct smb_filename *fname;
+	bool match;
+};
+
+static void lease_fname_match_parser(
+	struct file_id id, const char *filename, const char *stream_name,
+	void *private_data)
+{
+	struct lease_fname_match_state *state =
+		(struct lease_fname_match_state *)private_data;
+
+	state->match =
+		strequal(filename, state->fname->base_name) &&
+		strequal(stream_name, state->fname->stream_name);
+}
+
+static bool lease_fname_match(struct smbd_server_connection *sconn,
+			      struct smb2_lease_key *lease_key,
+			      const struct smb_filename *fname)
+{
+	struct lease_fname_match_state state =
+		{ .fname = fname, .match = true };
+	NTSTATUS status;
+
+	status = leases_db_parse(&sconn->client->connections->smb2.client.guid,
+				 lease_key, lease_fname_match_parser, &state);
+	if (!NT_STATUS_IS_OK(status)) {
+		/*
+		 * Not found or error means okay: We can make the lease pass
+		 */
+		return true;
+	}
+	return state.match;
+}
+
+/*
  * Wrapper around open_file_ntcreate and open_directory
  */
 
@@ -3877,6 +4180,12 @@ static NTSTATUS create_file_unixpath(connection_struct *conn,
 		oplock_request |= INTERNAL_OPEN_ONLY;
 	}
 
+	if ((lease != NULL) &&
+	    !lease_fname_match(req->sconn, &lease->lease_key, smb_fname)) {
+		status = NT_STATUS_INVALID_PARAMETER;
+		goto fail;
+	}
+
 	if ((conn->fs_capabilities & FILE_NAMED_STREAMS)
 	    && (access_mask & DELETE_ACCESS)
 	    && !is_ntfs_stream_smb_fname(smb_fname)) {
diff --git a/source3/smbd/oplock.c b/source3/smbd/oplock.c
index aa99f68..0a7ac17 100644
--- a/source3/smbd/oplock.c
+++ b/source3/smbd/oplock.c
@@ -116,8 +116,6 @@ static void release_file_oplock(files_struct *fsp)
 
 	flush_write_cache(fsp, SAMBA_OPLOCK_RELEASE_FLUSH);
 	delete_write_cache(fsp);
-
-	TALLOC_FREE(fsp->oplock_timeout);
 }
 
 /****************************************************************************
@@ -144,8 +142,36 @@ static void downgrade_file_oplock(files_struct *fsp)
 
 	flush_write_cache(fsp, SAMBA_OPLOCK_RELEASE_FLUSH);
 	delete_write_cache(fsp);
+}
 
-	TALLOC_FREE(fsp->oplock_timeout);
+uint32_t map_oplock_to_lease_type(uint16_t op_type)
+{
+	uint32_t ret;
+
+	switch(op_type) {
+	case BATCH_OPLOCK:
+	case BATCH_OPLOCK|EXCLUSIVE_OPLOCK:
+		ret = SMB2_LEASE_READ|SMB2_LEASE_WRITE|SMB2_LEASE_HANDLE;
+		break;
+	case EXCLUSIVE_OPLOCK:
+		ret = SMB2_LEASE_READ|SMB2_LEASE_WRITE;
+		break;
+	case LEVEL_II_OPLOCK:
+		ret = SMB2_LEASE_READ;
+		break;
+	default:
+		ret = SMB2_LEASE_NONE;
+		break;
+	}
+	return ret;
+}
+
+uint32_t get_lease_type(struct share_mode_data *d, struct share_mode_entry *e)
+{
+	if (e->op_type == LEASE_OPLOCK) {
+		return d->leases[e->lease_idx].current_state;
+	}
+	return map_oplock_to_lease_type(e->op_type);
 }
 
 bool update_num_read_oplocks(files_struct *fsp, struct share_mode_lock *lck)
@@ -155,26 +181,12 @@ bool update_num_read_oplocks(files_struct *fsp, struct share_mode_lock *lck)
 	uint32_t num_read_oplocks = 0;
 	uint32_t i;
 
-	if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) {
-		/*
-		 * If we're the only one, we don't need a brlock entry
-		 */
-		SMB_ASSERT(d->num_share_modes == 1);
-		SMB_ASSERT(EXCLUSIVE_OPLOCK_TYPE(d->share_modes[0].op_type));
-		return true;
-	}
-
 	for (i=0; i<d->num_share_modes; i++) {
 		struct share_mode_entry *e = &d->share_modes[i];
+		uint32_t e_lease_type = get_lease_type(d, e);
 
-		if (EXCLUSIVE_OPLOCK_TYPE(e->op_type)) {
+		if (e_lease_type & SMB2_LEASE_READ) {
 			num_read_oplocks += 1;
-			continue;
-		}
-
-		if (LEVEL_II_OPLOCK_TYPE(e->op_type)) {
-			num_read_oplocks += 1;
-			continue;
 		}
 	}
 
@@ -225,6 +237,7 @@ bool remove_oplock(files_struct *fsp)
 			 file_id_string_tos(&fsp->file_id)));
 	}
 	release_file_oplock(fsp);
+	TALLOC_FREE(fsp->oplock_timeout);
 
 	ret = update_num_read_oplocks(fsp, lck);
 	if (!ret) {
@@ -262,13 +275,15 @@ bool downgrade_oplock(files_struct *fsp)
 			 fsp_str_dbg(fsp), fsp_fnum_dbg(fsp),
 			 file_id_string_tos(&fsp->file_id)));
 	}
+
 	downgrade_file_oplock(fsp);
+	TALLOC_FREE(fsp->oplock_timeout);
 
 	ret = update_num_read_oplocks(fsp, lck);
 	if (!ret) {
-		DEBUG(0, ("%s: update_num_read_oplocks failed for "
-			 "file %s, %s, %s\n",
-			  __func__, fsp_str_dbg(fsp), fsp_fnum_dbg(fsp),
+		DEBUG(0,("%s: failed to downgrade share oplock "
+			 "for file %s, %s, file_id %s\n",
+			 __func__, fsp_str_dbg(fsp), fsp_fnum_dbg(fsp),
 			 file_id_string_tos(&fsp->file_id)));
 	}
 
@@ -276,6 +291,64 @@ bool downgrade_oplock(files_struct *fsp)
 	return ret;
 }
 
+struct downgrade_lease_fsps_state {
+	struct file_id id;
+	struct share_mode_lock *lck;
+	const struct smb2_lease_key *key;
+	uint32_t new_lease_state;
+	unsigned count;
+};
+
+static struct files_struct *downgrade_lease_fsps(struct files_struct *fsp,
+						 void *private_data)
+{
+	struct downgrade_lease_fsps_state *state =
+		(struct downgrade_lease_fsps_state *)private_data;
+	bool downgraded;
+
+	downgraded = fsp_lease_broken(state->lck, state->id, state->key, fsp,
+				      state->new_lease_state);
+	state->count += downgraded ? 1 : 0;
+	return NULL;
+}
+
+NTSTATUS downgrade_lease(struct smbd_server_connection *sconn,
+			 const struct file_id id,
+			 const struct smb2_lease_key *key,
+			 uint32_t lease_state)
+{
+	struct share_mode_lock *lck;
+	NTSTATUS status;
+
+	DEBUG(10, ("%s: Downgrading %s to %x\n", __func__,
+		   file_id_string_tos(&id), (unsigned)lease_state));
+
+	lck = get_existing_share_mode_lock(talloc_tos(), id);
+	if (lck == NULL) {
+		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+	}
+	status = downgrade_share_lease(sconn, lck, key, lease_state);
+
+	/*
+	 * This sucks. We have to reset fsp->sent_oplock_break on all fsps
+	 * that reference this lease.
+	 */
+	{
+		struct downgrade_lease_fsps_state state = {
+			.id = id, .lck = lck, .key = key,
+			.count = 0, .new_lease_state = lease_state
+		};
+
+		files_forall(sconn, downgrade_lease_fsps, &state);
+
+		/* Paranoia */
+		SMB_ASSERT(state.count > 0);
+	}
+
+	TALLOC_FREE(lck);
+	return status;
+}
+
 /****************************************************************************
  Set up an oplock break message.
 ****************************************************************************/
@@ -447,7 +520,6 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx,
 {
 	struct share_mode_entry msg;
 	files_struct *fsp;
-	bool break_to_level2 = False;
 	bool use_kernel;
 	struct smbd_server_connection *sconn =
 		talloc_get_type_abort(private_data,
@@ -488,23 +560,42 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx,
 		/*
 		 * Nothing to do anymore
 		 */
+		DEBUG(10, ("fsp->sent_oplock_break = %d\n",
+			   fsp->sent_oplock_break));
 		return;
 	}
 
-	if (break_to == fsp->oplock_type) {
-		DEBUG(3, ("Already downgraded oplock on %s: %s\n",
-			  file_id_string_tos(&fsp->file_id),
-			  fsp_str_dbg(fsp)));
-		return;
+	if (!(global_client_caps & CAP_LEVEL_II_OPLOCKS)) {
+		DEBUG(10, ("client_caps without level2 oplocks\n"));
+		break_to &= ~SMB2_LEASE_READ;
 	}
 
 	use_kernel = lp_kernel_oplocks(SNUM(fsp->conn)) && koplocks;
+	if (use_kernel && !(koplocks->flags & KOPLOCKS_LEVEL2_SUPPORTED)) {
+		DEBUG(10, ("Kernel oplocks don't allow level2\n"));
+		break_to &= ~SMB2_LEASE_READ;
+	}
 
-	if ((global_client_caps & CAP_LEVEL_II_OPLOCKS) &&
-	    (break_to != NO_OPLOCK) &&
-	    !(use_kernel && !(koplocks->flags & KOPLOCKS_LEVEL2_SUPPORTED)) &&
-	    lp_level2_oplocks(SNUM(fsp->conn))) {
-		break_to_level2 = True;
+	if (!lp_level2_oplocks(SNUM(fsp->conn))) {
+		DEBUG(10, ("no level2 oplocks by config\n"));
+		break_to &= ~SMB2_LEASE_READ;
+	}
+
+	DEBUG(10, ("msg.op_type=%u, break_to=%u\n",
+		   (unsigned)msg.op_type, (unsigned)break_to));
+
+	if (fsp->oplock_type == NO_OPLOCK) {
+		DEBUG(3, ("Already downgraded oplock to none on %s: %s\n",
+			  file_id_string_tos(&fsp->file_id),
+			  fsp_str_dbg(fsp)));
+		return;
+	}
+	if ((break_to & SMB2_LEASE_READ) &&
+	    (fsp->oplock_type == LEVEL_II_OPLOCK)) {
+		DEBUG(3, ("Already downgraded oplock to level2 on %s: %s\n",
+			  file_id_string_tos(&fsp->file_id),
+			  fsp_str_dbg(fsp)));
+		return;
 	}
 
 	/* Need to wait before sending a break
@@ -514,21 +605,33 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx,
 	}
 
 	if (sconn->using_smb2) {
-		send_break_message_smb2(fsp, break_to_level2 ?
-			OPLOCKLEVEL_II : OPLOCKLEVEL_NONE);
+		send_break_message_smb2(fsp, break_to);
 	} else {
-		send_break_message_smb1(fsp, break_to_level2 ?
-			OPLOCKLEVEL_II : OPLOCKLEVEL_NONE);
+		send_break_message_smb1(fsp, (break_to & SMB2_LEASE_READ) ?
+					OPLOCKLEVEL_II : OPLOCKLEVEL_NONE);
 	}
 
-	if ((fsp->oplock_type == LEVEL_II_OPLOCK) && (break_to == NO_OPLOCK)) {
+	if ((fsp_lease_type(fsp) == SMB2_LEASE_READ) &&
+	    (break_to == SMB2_LEASE_NONE)) {
 		/*
 		 * This is an async break without a reply and thus no timeout
 		 */
-		remove_oplock(fsp);
+		if (fsp->oplock_type == LEASE_OPLOCK) {
+			/*
+			 * We must leave the lease around, it might be
+			 * upgraded later
+			 */
+			downgrade_lease(fsp->conn->sconn, fsp->file_id,
+					&fsp->lease->lease.lease_key,
+					break_to);
+		} else {
+			remove_oplock(fsp);
+		}
 		return;
 	}
-	fsp->sent_oplock_break = break_to_level2 ? LEVEL_II_BREAK_SENT:BREAK_TO_NONE_SENT;
+	fsp->sent_oplock_break = (break_to & SMB2_LEASE_READ) ?
+		LEVEL_II_BREAK_SENT:BREAK_TO_NONE_SENT;
+
 	add_oplock_timeout_handler(fsp);
 }
 
@@ -596,6 +699,7 @@ static void process_kernel_oplock_break(struct messaging_context *msg_ctx,
 struct break_to_none_state {
 	struct smbd_server_connection *sconn;
 	struct file_id id;
+	struct smb2_lease_key lease_key;
 };
 static void do_break_to_none(struct tevent_context *ctx,
 			     struct tevent_immediate *im,
@@ -614,7 +718,7 @@ static void contend_level2_oplocks_begin_default(files_struct *fsp,
 	struct tevent_immediate *im;
 	struct break_to_none_state *state;
 	struct byte_range_lock *brl;
-	uint32_t num_read_oplocks = 0;
+	uint32_t num_read_oplocks;
 
 	/*
 	 * If this file is level II oplocked then we need
@@ -631,6 +735,8 @@ static void contend_level2_oplocks_begin_default(files_struct *fsp,
 		return;
 	}
 
+	num_read_oplocks = 0;
+
 	brl = brl_get_locks_readonly(fsp);
 	if (brl != NULL) {
 		num_read_oplocks = brl_num_read_oplocks(brl);
@@ -642,6 +748,10 @@ static void contend_level2_oplocks_begin_default(files_struct *fsp,
 		DEBUG(10, ("No read oplocks around\n"));
 		return;
 	}
+	if ((num_read_oplocks == 1) && (fsp->oplock_type == LEASE_OPLOCK)) {
+		DEBUG(10, ("We're the only reader, don't break\n"));
+		return;
+	}
 
 	/*
 	 * When we get here we might have a brlock entry locked. Also
@@ -650,7 +760,7 @@ static void contend_level2_oplocks_begin_default(files_struct *fsp,
 	 * anyway, so we postpone this into an immediate event.
 	 */
 
-	state = talloc(sconn, struct break_to_none_state);
+	state = talloc_zero(sconn, struct break_to_none_state);
 	if (state == NULL) {
 		DEBUG(1, ("talloc failed\n"));
 		return;
@@ -658,6 +768,13 @@ static void contend_level2_oplocks_begin_default(files_struct *fsp,
 	state->sconn = sconn;
 	state->id = fsp->file_id;
 
+	if (fsp->oplock_type == LEASE_OPLOCK) {
+		state->lease_key = fsp->lease->lease.lease_key;
+		DEBUG(10, ("Breaking through lease key %"PRIu64"/%"PRIu64"\n",
+			   state->lease_key.data[0],
+			   state->lease_key.data[1]));
+	}
+
 	im = tevent_create_immediate(state);
 	if (im == NULL) {
 		DEBUG(1, ("tevent_create_immediate failed\n"));
@@ -667,14 +784,28 @@ static void contend_level2_oplocks_begin_default(files_struct *fsp,
 	tevent_schedule_immediate(im, sconn->ev_ctx, do_break_to_none, state);
 }
 
+static void send_break_to_none(struct messaging_context *msg_ctx,
+			       const struct share_mode_entry *e)
+{
+	char msg[MSG_SMB_SHARE_MODE_ENTRY_SIZE];
+
+	share_mode_entry_to_message(msg, e);
+	/* Overload entry->op_type */
+	SSVAL(msg, OP_BREAK_MSG_OP_TYPE_OFFSET, NO_OPLOCK);
+
+	messaging_send_buf(msg_ctx, e->pid, MSG_SMB_BREAK_REQUEST,
+			   (uint8 *)msg, sizeof(msg));
+}
+
 static void do_break_to_none(struct tevent_context *ctx,
 			     struct tevent_immediate *im,
 			     void *private_data)
 {
 	struct break_to_none_state *state = talloc_get_type_abort(
 		private_data, struct break_to_none_state);
-	int i;
+	uint32_t i;
 	struct share_mode_lock *lck;
+	struct share_mode_data *d;
 
 	lck = get_existing_share_mode_lock(talloc_tos(), state->id);
 	if (lck == NULL) {
@@ -682,15 +813,61 @@ static void do_break_to_none(struct tevent_context *ctx,
 			  __func__, file_id_string_tos(&state->id)));
 		goto done;
 	}
+	d = lck->data;
 
-	DEBUG(10,("%s: num_share_modes = %d\n", __func__,
-		  lck->data->num_share_modes ));
+	/*
+	 * Walk leases and oplocks separately: We have to send one break per
+	 * lease. If we have multiple share_mode_entry having a common lease,
+	 * we would break the lease twice if we don't walk the leases list
+	 * separately.
+	 */
 
-	for(i = 0; i < lck->data->num_share_modes; i++) {
-		struct share_mode_entry *share_entry = &lck->data->share_modes[i];
-		char msg[MSG_SMB_SHARE_MODE_ENTRY_SIZE];
+	for (i=0; i<d->num_leases; i++) {
+		struct share_mode_oplock *l = &d->leases[i];
+		struct share_mode_entry *e;
+		uint32_t j;
 
-		if (!is_valid_share_mode_entry(share_entry)) {
+		if ((l->current_state & SMB2_LEASE_READ) == 0) {
+			continue;
+		}
+		if ((l->lease_key.data[0] == state->lease_key.data[0]) &&
+		    (l->lease_key.data[1] == state->lease_key.data[1])) {
+			DEBUG(10, ("Don't break our own lease\n"));
+			continue;
+		}
+
+		for (j=0; j<d->num_share_modes; j++) {
+			e = &d->share_modes[j];
+
+			if (!is_valid_share_mode_entry(e)) {
+				continue;
+			}
+			if (e->lease_idx == i) {
+				break;
+			}
+		}
+		if (j == d->num_share_modes) {
+			DEBUG(0, ("leases[%"PRIu32"] has no share mode\n",
+				  i));
+			continue;
+		}
+
+		DEBUG(10, ("Breaking lease# %"PRIu32" with share_entry# "
+			   "%"PRIu32"\n", i, j));
+
+		send_break_to_none(state->sconn->msg_ctx, e);
+	}
+
+	for(i = 0; i < d->num_share_modes; i++) {
+		struct share_mode_entry *e = &d->share_modes[i];
+
+		if (!is_valid_share_mode_entry(e)) {
+			continue;
+		}
+		if (e->op_type == LEASE_OPLOCK) {
+			/*
+			 * Took care of those in the loop above
+			 */
 			continue;
 		}
 
@@ -705,15 +882,15 @@ static void do_break_to_none(struct tevent_context *ctx,
 		 * NO_OPLOCK states. JRA.
 		 */
 
-		DEBUG(10,("%s: share_entry[%i]->op_type == %d\n", __func__,
-			  i, share_entry->op_type ));
+		DEBUG(10, ("%s: share_entry[%i]->op_type == %d\n", __func__,
+			   i, e->op_type ));
 
-		if (share_entry->op_type == NO_OPLOCK) {
+		if (e->op_type == NO_OPLOCK) {
 			continue;
 		}
 
 		/* Paranoia .... */
-		if (EXCLUSIVE_OPLOCK_TYPE(share_entry->op_type)) {
+		if (EXCLUSIVE_OPLOCK_TYPE(e->op_type)) {
 			DEBUG(0,("%s: PANIC. "
 				 "share mode entry %d is an exlusive "
 				 "oplock !\n", __func__, i ));
@@ -721,13 +898,7 @@ static void do_break_to_none(struct tevent_context *ctx,
 			abort();
 		}
 
-		share_mode_entry_to_message(msg, share_entry);
-		/* Overload entry->op_type */
-		SSVAL(msg,OP_BREAK_MSG_OP_TYPE_OFFSET, NO_OPLOCK);
-
-		messaging_send_buf(state->sconn->msg_ctx, share_entry->pid,
-				   MSG_SMB_BREAK_REQUEST,
-				   (uint8 *)msg, sizeof(msg));
+		send_break_to_none(state->sconn->msg_ctx, e);
 	}
 
 	/* We let the message receivers handle removing the oplock state
diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
index 1080895..613af8a 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -369,6 +369,9 @@ files_struct *file_find_dif(struct smbd_server_connection *sconn,
 files_struct *file_find_di_first(struct smbd_server_connection *sconn,
 				 struct file_id id);
 files_struct *file_find_di_next(files_struct *start_fsp);
+struct files_struct *file_find_one_fsp_from_lease_key(
+	struct smbd_server_connection *sconn,
+	const struct smb2_lease_key *lease_key);
 bool file_find_subpath(files_struct *dir_fsp);
 void file_sync_all(connection_struct *conn);
 void fsp_free(files_struct *fsp);
@@ -387,6 +390,7 @@ NTSTATUS file_name_hash(connection_struct *conn,
 			const char *name, uint32_t *p_name_hash);
 NTSTATUS fsp_set_smb_fname(struct files_struct *fsp,
 			   const struct smb_filename *smb_fname_in);
+uint32_t fsp_lease_type(struct files_struct *fsp);
 
 /* The following definitions come from smbd/ipc.c  */
 
@@ -609,6 +613,8 @@ NTSTATUS change_dir_owner_to_parent(connection_struct *conn,
 				    const char *inherit_from_dir,
 				    const char *fname,
 				    SMB_STRUCT_STAT *psbuf);
+struct fsp_lease *find_fsp_lease(files_struct *new_fsp,
+				 const struct smb2_lease_key *key);
 bool is_stat_open(uint32 access_mask);
 struct deferred_open_record;
 bool is_deferred_open_async(const struct deferred_open_record *rec);
@@ -647,12 +653,18 @@ NTSTATUS get_relative_fid_filename(connection_struct *conn,
 
 /* The following definitions come from smbd/oplock.c  */
 
+uint32_t map_oplock_to_lease_type(uint16_t op_type);
+uint32_t get_lease_type(struct share_mode_data *d, struct share_mode_entry *e);
 bool update_num_read_oplocks(files_struct *fsp, struct share_mode_lock *lck);
 
 void break_kernel_oplock(struct messaging_context *msg_ctx, files_struct *fsp);
 NTSTATUS set_file_oplock(files_struct *fsp);
 bool remove_oplock(files_struct *fsp);
 bool downgrade_oplock(files_struct *fsp);
+NTSTATUS downgrade_lease(struct smbd_server_connection *sconn,
+			 const struct file_id id,
+			 const struct smb2_lease_key *key,
+			 uint32_t lease_state);
 void contend_level2_oplocks_begin(files_struct *fsp,
 				  enum level2_contention_type type);
 void contend_level2_oplocks_end(files_struct *fsp,
diff --git a/source3/smbd/server.c b/source3/smbd/server.c
index 0d649e1..60394dd 100644
--- a/source3/smbd/server.c
+++ b/source3/smbd/server.c
@@ -47,6 +47,7 @@
 #include "../lib/util/pidfile.h"
 #include "lib/smbd_shim.h"
 #include "scavenger.h"
+#include "locking/leases_db.h"
 
 struct smbd_open_socket;
 struct smbd_child_pid;
@@ -1450,6 +1451,10 @@ extern void build_options(bool screen);
 	if (!locking_init())
 		exit_daemon("Samba cannot init locking", EACCES);
 
+	if (!leases_db_init(false)) {
+		exit_daemon("Samba cannot init leases", EACCES);
+	}
+
 	if (!smbd_parent_notify_init(NULL, msg_ctx, ev_ctx)) {
 		exit_daemon("Samba cannot init notification", EACCES);
 	}
diff --git a/source3/smbd/smb2_break.c b/source3/smbd/smb2_break.c
index 4492456..cb57504 100644
--- a/source3/smbd/smb2_break.c
+++ b/source3/smbd/smb2_break.c
@@ -25,6 +25,9 @@
 #include "../libcli/smb/smb_common.h"
 #include "../lib/util/tevent_ntstatus.h"
 
+static NTSTATUS smbd_smb2_request_process_lease_break(
+	struct smbd_smb2_request *req);
+
 static struct tevent_req *smbd_smb2_oplock_break_send(TALLOC_CTX *mem_ctx,
 						      struct tevent_context *ev,
 						      struct smbd_smb2_request *smb2req,
@@ -45,6 +48,12 @@ NTSTATUS smbd_smb2_request_process_break(struct smbd_smb2_request *req)
 	struct tevent_req *subreq;
 
 	status = smbd_smb2_request_verify_sizes(req, 0x18);
+	if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
+		/*
+		 * Retry as a lease break
+		 */
+		return smbd_smb2_request_process_lease_break(req);
+	}
 	if (!NT_STATUS_IS_OK(status)) {
 		return smbd_smb2_request_error(req, status);
 	}
@@ -222,16 +231,163 @@ static NTSTATUS smbd_smb2_oplock_break_recv(struct tevent_req *req,
 	return NT_STATUS_OK;
 }
 
+static void smbd_smb2_request_lease_break_done(struct tevent_req *subreq);
+
+static struct tevent_req *smbd_smb2_lease_break_send(
+	TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+	struct smbd_smb2_request *smb2_req, struct smb2_lease_key in_lease_key,
+	uint32_t in_lease_state);
+static NTSTATUS smbd_smb2_lease_break_recv(struct tevent_req *req,
+					   uint32_t *out_lease_state);
+
+
+static NTSTATUS smbd_smb2_request_process_lease_break(
+	struct smbd_smb2_request *req)
+{
+	NTSTATUS status;
+	const uint8_t *inbody;
+	struct smb2_lease_key in_lease_key;
+	uint32_t in_lease_state;
+	struct tevent_req *subreq;
+
+	status = smbd_smb2_request_verify_sizes(req, 0x24);
+	if (!NT_STATUS_IS_OK(status)) {
+		return smbd_smb2_request_error(req, status);
+	}
+
+	inbody = SMBD_SMB2_IN_BODY_PTR(req);
+
+	in_lease_key.data[0] = BVAL(inbody, 8);
+	in_lease_key.data[1] = BVAL(inbody, 16);
+	in_lease_state = IVAL(inbody, 24);
+
+	subreq = smbd_smb2_lease_break_send(req, req->sconn->ev_ctx, req,
+					    in_lease_key, in_lease_state);
+	if (subreq == NULL) {
+		return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
+	}
+	tevent_req_set_callback(subreq, smbd_smb2_request_lease_break_done, req);
+
+	return smbd_smb2_request_pending_queue(req, subreq, 500);
+}
+
+static void smbd_smb2_request_lease_break_done(struct tevent_req *subreq)
+{
+	struct smbd_smb2_request *req = tevent_req_callback_data(
+		subreq, struct smbd_smb2_request);
+	const uint8_t *inbody;
+	struct smb2_lease_key in_lease_key;
+	uint32_t out_lease_state = 0;
+	DATA_BLOB outbody;
+	NTSTATUS status;
+	NTSTATUS error; /* transport error */
+
+	status = smbd_smb2_lease_break_recv(subreq, &out_lease_state);
+	TALLOC_FREE(subreq);
+	if (!NT_STATUS_IS_OK(status)) {
+		error = smbd_smb2_request_error(req, status);
+		if (!NT_STATUS_IS_OK(error)) {
+			smbd_server_connection_terminate(req->xconn,
+							 nt_errstr(error));
+			return;
+		}
+		return;
+	}
+
+	inbody = SMBD_SMB2_IN_BODY_PTR(req);
+
+	in_lease_key.data[0] = BVAL(inbody, 8);
+	in_lease_key.data[1] = BVAL(inbody, 16);
+
+	outbody = smbd_smb2_generate_outbody(req, 0x24);
+	if (outbody.data == NULL) {
+		error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
+		if (!NT_STATUS_IS_OK(error)) {
+			smbd_server_connection_terminate(req->xconn,
+							 nt_errstr(error));
+			return;
+		}
+		return;
+	}
+
+	SSVAL(outbody.data, 0x00, 0x24);	/* struct size */
+	SSVAL(outbody.data, 0x02, 0);		/* reserved */
+	SIVAL(outbody.data, 0x04, 0);		/* flags, must be 0 */
+	SBVAL(outbody.data, 0x08, in_lease_key.data[0]);
+	SBVAL(outbody.data, 0x10, in_lease_key.data[1]);
+	SIVAL(outbody.data, 0x18, out_lease_state);
+	SBVAL(outbody.data, 0x1c, 0);           /* leaseduration, must be 0 */
+
+	error = smbd_smb2_request_done(req, outbody, NULL);
+	if (!NT_STATUS_IS_OK(error)) {
+		smbd_server_connection_terminate(req->xconn,
+						 nt_errstr(error));
+		return;
+	}
+}
+
+struct smbd_smb2_lease_break_state {
+	uint32_t lease_state;
+};
+
+static struct tevent_req *smbd_smb2_lease_break_send(
+	TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+	struct smbd_smb2_request *smb2_req, struct smb2_lease_key in_lease_key,
+	uint32_t in_lease_state)
+{
+	struct tevent_req *req;
+	struct smbd_smb2_lease_break_state *state;
+	struct files_struct *fsp;
+	NTSTATUS status;
+
+	req = tevent_req_create(mem_ctx, &state,
+				struct smbd_smb2_lease_break_state);
+	if (req == NULL) {
+		return NULL;
+	}
+	state->lease_state = in_lease_state;
+
+	fsp = file_find_one_fsp_from_lease_key(smb2_req->sconn, &in_lease_key);
+	if (fsp == NULL) {
+		DEBUG(10, ("No fsp for lease key found\n"));
+		tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+		return tevent_req_post(req, ev);
+	}
+
+	status = downgrade_lease(smb2_req->sconn, fsp->file_id, &in_lease_key,
+				 in_lease_state);
+	if (tevent_req_nterror(req, status)) {
+		DEBUG(10, ("downgrade_lease returned %s\n",
+			   nt_errstr(status)));
+		return tevent_req_post(req, ev);
+	}
+	TALLOC_FREE(fsp->oplock_timeout);
+
+	tevent_req_done(req);
+	return tevent_req_post(req, ev);
+}
+
+static NTSTATUS smbd_smb2_lease_break_recv(struct tevent_req *req,
+					   uint32_t *out_lease_state)
+{
+	struct smbd_smb2_lease_break_state *state = tevent_req_data(
+		req, struct smbd_smb2_lease_break_state);
+	NTSTATUS status;
+
+	if (tevent_req_is_nterror(req, &status)) {
+		return status;
+	}
+	*out_lease_state = state->lease_state;
+	return NT_STATUS_OK;
+}
+
 /*********************************************************
  Create and send an asynchronous
  SMB2 OPLOCK_BREAK_NOTIFICATION.
 *********************************************************/
 
-void send_break_message_smb2(files_struct *fsp, int level)
+void send_break_message_smb2(files_struct *fsp, uint32_t break_to)
 {
-	uint8_t smb2_oplock_level = (level == OPLOCKLEVEL_II) ?
-				SMB2_OPLOCK_LEVEL_II :
-				SMB2_OPLOCK_LEVEL_NONE;
 	NTSTATUS status;
 	struct smbXsrv_connection *xconn = NULL;
 	struct smbXsrv_session *session = NULL;
@@ -257,7 +413,7 @@ void send_break_message_smb2(files_struct *fsp, int level)
 			"for file %s, %s, smb2 level %u session %llu not found\n",
 			fsp_str_dbg(fsp),
 			fsp_fnum_dbg(fsp),
-			(unsigned int)smb2_oplock_level,
+			(unsigned int)break_to,
 			(unsigned long long)fsp->vuid));
 		return;
 	}
@@ -266,13 +422,29 @@ void send_break_message_smb2(files_struct *fsp, int level)
 		"for file %s, %s, smb2 level %u\n",
 		fsp_str_dbg(fsp),
 		fsp_fnum_dbg(fsp),
-		(unsigned int)smb2_oplock_level ));
+		(unsigned int)break_to ));
+
+	if (fsp->oplock_type == LEASE_OPLOCK) {
+		bool no_ack;
 
-	status = smbd_smb2_send_oplock_break(xconn,
-					     session,
-					     fsp->conn->tcon,
-					     fsp->op,
-					     smb2_oplock_level);
+		no_ack = ((fsp->lease->lease.lease_state == SMB2_LEASE_READ) &&
+			  (break_to == SMB2_LEASE_NONE));
+
+		status = smbd_smb2_send_lease_break(
+			xconn, session, fsp->conn->tcon, 0,
+			no_ack ? 0 : SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED,
+			&fsp->lease->lease.lease_key,
+			fsp->lease->lease.lease_state, break_to);
+	} else {
+		uint8_t smb2_oplock_level;
+		smb2_oplock_level = (break_to & SMB2_LEASE_READ) ?
+			SMB2_OPLOCK_LEVEL_II : SMB2_OPLOCK_LEVEL_NONE;
+		status = smbd_smb2_send_oplock_break(xconn,
+						     session,
+						     fsp->conn->tcon,
+						     fsp->op,
+						     smb2_oplock_level);
+	}
 	if (!NT_STATUS_IS_OK(status)) {
 		smbd_server_connection_terminate(xconn,
 						 nt_errstr(status));
diff --git a/source3/smbd/smb2_create.c b/source3/smbd/smb2_create.c
index 48bc486..67d4fc0 100644
--- a/source3/smbd/smb2_create.c
+++ b/source3/smbd/smb2_create.c
@@ -25,6 +25,7 @@
 #include "smbd/globals.h"
 #include "../libcli/smb/smb_common.h"
 #include "../librpc/gen_ndr/ndr_security.h"
+#include "../librpc/gen_ndr/ndr_smb2_lease_struct.h"
 #include "../lib/util/tevent_ntstatus.h"
 #include "messages.h"
 
@@ -40,9 +41,7 @@ int map_smb2_oplock_levels_to_samba(uint8_t in_oplock_level)
 	case SMB2_OPLOCK_LEVEL_BATCH:
 		return BATCH_OPLOCK;
 	case SMB2_OPLOCK_LEVEL_LEASE:
-		DEBUG(2,("map_smb2_oplock_levels_to_samba: "
-			"LEASE_OPLOCK_REQUESTED\n"));
-		return NO_OPLOCK;
+		return LEASE_OPLOCK;
 	default:
 		DEBUG(2,("map_smb2_oplock_levels_to_samba: "
 			"unknown level %u\n",
@@ -59,6 +58,8 @@ static uint8_t map_samba_oplock_levels_to_smb2(int oplock_type)
 		return SMB2_OPLOCK_LEVEL_EXCLUSIVE;
 	} else if (oplock_type == LEVEL_II_OPLOCK) {
 		return SMB2_OPLOCK_LEVEL_II;
+	} else if (oplock_type == LEASE_OPLOCK) {
+		return SMB2_OPLOCK_LEVEL_LEASE;
 	} else {
 		return SMB2_OPLOCK_LEVEL_NONE;
 	}
@@ -374,6 +375,62 @@ static void smbd_smb2_request_create_done(struct tevent_req *tsubreq)
 	}
 }
 
+static bool smb2_lease_key_valid(const struct smb2_lease_key *key)
+{
+	return ((key->data[0] != 0) || (key->data[1] != 0));
+}
+
+static NTSTATUS smbd_smb2_create_durable_lease_check(
+	const char *requested_filename, const struct files_struct *fsp,
+	const struct smb2_lease *lease_ptr)
+{
+	struct smb_filename *smb_fname = NULL;
+	NTSTATUS status;
+
+	if (lease_ptr == NULL) {
+		if (fsp->oplock_type != LEASE_OPLOCK) {
+			return NT_STATUS_OK;
+		}
+		DEBUG(10, ("Reopened file has lease, but no lease "
+			   "requested\n"));
+		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+	}
+
+	if (fsp->oplock_type != LEASE_OPLOCK) {
+		DEBUG(10, ("Lease requested, but reopened file has no "
+			   "lease\n"));
+		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+	}
+
+	if (!smb2_lease_key_equal(&lease_ptr->lease_key,
+				  &fsp->lease->lease.lease_key)) {
+		DEBUG(10, ("Different lease key requested than found "
+			   "in reopened file\n"));
+		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+	}
+
+	status = filename_convert(talloc_tos(), fsp->conn, false,
+				  requested_filename, UCF_PREP_CREATEFILE,
+				  NULL, &smb_fname);
+	if (!NT_STATUS_IS_OK(status)) {
+		DEBUG(10, ("filename_convert returned %s\n",
+			   nt_errstr(status)));
+		return status;
+	}
+
+	if (!strequal(fsp->fsp_name->base_name, smb_fname->base_name)) {
+		DEBUG(10, ("Lease requested for file %s, reopened file "
+			   "is named %s\n", smb_fname->base_name,
+			   fsp->fsp_name->base_name));
+		TALLOC_FREE(smb_fname);
+		return NT_STATUS_INVALID_PARAMETER;
+	}
+
+	TALLOC_FREE(smb_fname);
+
+	return NT_STATUS_OK;
+}
+
 struct smbd_smb2_create_state {
 	struct smbd_smb2_request *smb2req;
 	struct smb_request *smb1req;
@@ -507,6 +564,11 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 			num_blobs_allowed = 1;
 		}
 
+		if (smb2_create_blob_find(&in_context_blobs,
+					  SMB2_CREATE_TAG_RQLS) != NULL) {
+			num_blobs_allowed += 1;
+		}
+
 		if (in_context_blobs.num_blobs != num_blobs_allowed) {
 			tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
 			return tevent_req_post(req, ev);
@@ -539,6 +601,11 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 
 		num_blobs_allowed = 1;
 
+		if (smb2_create_blob_find(&in_context_blobs,
+					  SMB2_CREATE_TAG_RQLS) != NULL) {
+			num_blobs_allowed += 1;
+		}
+
 		if (in_context_blobs.num_blobs != num_blobs_allowed) {
 			tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
 			return tevent_req_post(req, ev);
@@ -600,11 +667,15 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 		struct smb2_create_blob *qfid = NULL;
 		struct GUID _create_guid = GUID_zero();
 		struct GUID *create_guid = NULL;
+		struct smb2_create_blob *rqls = NULL;
 		bool update_open = false;
 		bool durable_requested = false;
 		uint32_t durable_timeout_msec = 0;
 		bool do_durable_reconnect = false;
 		uint64_t persistent_id = 0;
+		struct smb2_lease lease;
+		struct smb2_lease *lease_ptr;
+		ssize_t lease_len = -1;
 
 		exta = smb2_create_blob_find(&in_context_blobs,
 					     SMB2_CREATE_TAG_EXTA);
@@ -618,6 +689,8 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 					     SMB2_CREATE_TAG_TWRP);
 		qfid = smb2_create_blob_find(&in_context_blobs,
 					     SMB2_CREATE_TAG_QFID);
+		rqls = smb2_create_blob_find(&in_context_blobs,
+					     SMB2_CREATE_TAG_RQLS);
 
 		fname = talloc_strdup(state, in_name);
 		if (tevent_req_nomem(fname, req)) {
@@ -804,6 +877,39 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 			}
 		}
 
+		lease_ptr = NULL;
+
+		if (rqls) {
+			lease_len = smb2_lease_pull(
+				rqls->data.data, rqls->data.length, &lease);
+			if (lease_len == -1) {
+				tevent_req_nterror(
+					req, NT_STATUS_INVALID_PARAMETER);
+				return tevent_req_post(req, ev);
+			}
+			lease_ptr = &lease;
+
+			if (DEBUGLEVEL >= 10) {
+				DEBUG(10, ("Got lease request size %d\n",
+					   (int)lease_len));
+				NDR_PRINT_DEBUG(smb2_lease, lease_ptr);
+			}
+
+			if (!smb2_lease_key_valid(&lease.lease_key)) {
+				lease_ptr = NULL;
+				requested_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
+			}
+
+			/* TODO client->connections ... */
+			if ((smb2req->sconn->client->connections->protocol <
+			     PROTOCOL_SMB3_00)
+			    && (lease_len == 52)) {
+				DEBUG(10, ("v2 lease key only for SMB3\n"));
+				lease_ptr = NULL;
+				requested_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
+			}
+		}
+
 		/* these are ignored for SMB2 */
 		in_create_options &= ~(0x10);/* NTCREATEX_OPTIONS_SYNC_ALERT */
 		in_create_options &= ~(0x20);/* NTCREATEX_OPTIONS_ASYNC_ALERT */
@@ -863,6 +969,18 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 				return tevent_req_post(req, ev);
 			}
 
+			DEBUG(10, ("result->oplock_type=%u, lease_ptr==%p\n",
+				   (unsigned)result->oplock_type, lease_ptr));
+
+			status = smbd_smb2_create_durable_lease_check(
+				fname, result, lease_ptr);
+
+			if (!NT_STATUS_IS_OK(status)) {
+				close_file(smb1req, result, SHUTDOWN_CLOSE);
+				tevent_req_nterror(req, status);
+				return tevent_req_post(req, ev);
+			}
+
 			data_blob_free(&op->global->backend_cookie);
 			op->global->backend_cookie = new_cookie;
 
@@ -948,7 +1066,7 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 						     in_create_options,
 						     in_file_attributes,
 						     map_smb2_oplock_levels_to_samba(requested_oplock_level),
-						     NULL,
+						     lease_ptr,
 						     allocation_size,
 						     0, /* private_flags */
 						     sec_desc,
@@ -1003,7 +1121,7 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 		}
 
 		if (durable_requested &&
-		    BATCH_OPLOCK_TYPE(result->oplock_type))
+		    (fsp_lease_type(result) & SMB2_LEASE_HANDLE))
 		{
 			status = SMB_VFS_DURABLE_COOKIE(result,
 						op,
@@ -1086,6 +1204,29 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 				return tevent_req_post(req, ev);
 			}
 		}
+
+		if ((rqls != NULL) && (result->oplock_type == LEASE_OPLOCK)) {
+			uint8_t buf[52];
+
+			lease = result->lease->lease;
+			lease.lease_flags &=
+				SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET;
+
+			if (!smb2_lease_push(&lease, buf, lease_len)) {
+				tevent_req_nterror(
+					req, NT_STATUS_INTERNAL_ERROR);
+				return tevent_req_post(req, ev);
+			}
+
+			status = smb2_create_blob_add(
+				state, &out_context_blobs,
+				SMB2_CREATE_TAG_RQLS,
+				data_blob_const(buf, lease_len));
+			if (!NT_STATUS_IS_OK(status)) {
+				tevent_req_nterror(req, status);
+				return tevent_req_post(req, ev);
+			}
+		}
 	}
 
 	smb2req->compat_chain_fsp = smb1req->chain_fsp;
diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c
index 689bfd7..36ac0d7 100644
--- a/source3/smbd/smb2_server.c
+++ b/source3/smbd/smb2_server.c
@@ -2888,6 +2888,31 @@ NTSTATUS smbd_smb2_send_oplock_break(struct smbXsrv_connection *xconn,
 	return smbd_smb2_send_break(xconn, session, tcon, body, sizeof(body));
 }
 
+NTSTATUS smbd_smb2_send_lease_break(struct smbXsrv_connection *xconn,
+				    struct smbXsrv_session *session,
+				    struct smbXsrv_tcon *tcon,
+				    uint16_t new_epoch,
+				    uint32_t lease_flags,
+				    struct smb2_lease_key *lease_key,
+				    uint32_t current_lease_state,
+				    uint32_t new_lease_state)
+{
+	uint8_t body[0x2c];
+
+	SSVAL(body, 0x00, sizeof(body));
+	SSVAL(body, 0x02, new_epoch);
+	SIVAL(body, 0x04, lease_flags);
+	SBVAL(body, 0x08, lease_key->data[0]);
+	SBVAL(body, 0x10, lease_key->data[1]);
+	SIVAL(body, 0x18, current_lease_state);
+	SIVAL(body, 0x1c, new_lease_state);
+	SIVAL(body, 0x20, 0);		/* BreakReason, MUST be 0 */
+	SIVAL(body, 0x24, 0);		/* AccessMaskHint, MUST be 0 */
+	SIVAL(body, 0x28, 0);		/* ShareMaskHint, MUST be 0 */
+
+	return smbd_smb2_send_break(xconn, session, tcon, body, sizeof(body));
+}
+
 static bool is_smb2_recvfile_write(struct smbd_smb2_request_read_state *state)
 {
 	NTSTATUS status;
diff --git a/source3/utils/status.c b/source3/utils/status.c
index 978d3c5..f46f336 100644
--- a/source3/utils/status.c
+++ b/source3/utils/status.c
@@ -177,6 +177,8 @@ static int print_share_mode(const struct share_mode_entry *e,
 			d_printf("BATCH           ");
 		} else if (e->op_type & LEVEL_II_OPLOCK) {
 			d_printf("LEVEL_II        ");
+		} else if (e->op_type == LEASE_OPLOCK) {
+			d_printf("LEASE           ");
 		} else {
 			d_printf("NONE            ");
 		}
diff --git a/source3/wscript_build b/source3/wscript_build
index 836d7b3..7a0fd7f 100755
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -619,6 +619,7 @@ bld.SAMBA3_LIBRARY('smbd_base',
                    LIBAFS
                    RPC_SERVICE
                    NDR_SMBXSRV
+                   LEASES_DB
                    LIBASYS
                    sysquotas
                    ccan-hash
@@ -637,9 +638,14 @@ bld.SAMBA3_SUBSYSTEM('LOCKING',
                     deps='''
                     tdb_compat
                     talloc
+                    LEASES_DB
                     NDR_OPEN_FILES
                     FNAME_UTIL''')
 
+bld.SAMBA3_SUBSYSTEM('LEASES_DB',
+                    source='locking/leases_db.c',
+                    deps='NDR_LEASES_DB')
+
 if bld.CONFIG_GET("WITH_PROFILE"):
     bld.SAMBA3_SUBSYSTEM('PROFILE',
                          source='profile/profile.c',
-- 
1.9.1


From c6b38f9ed9bb52a2e1a33316317ab49f283ae216 Mon Sep 17 00:00:00 2001
From: Volker Lendecke <vl at samba.org>
Date: Tue, 23 Sep 2014 02:28:38 +0200
Subject: [PATCH 02/46] s3: leases - epoch handling

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

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


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

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

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


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

Will enable us to solve the dynamic share path problem.

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 source3/librpc/idl/leases_db.idl |  24 +++
 source3/locking/leases_db.c      | 368 +++++++++++++++++++++++++++++++++++++++
 source3/locking/leases_db.h      |  46 +++++
 3 files changed, 438 insertions(+)
 create mode 100644 source3/librpc/idl/leases_db.idl
 create mode 100644 source3/locking/leases_db.c
 create mode 100644 source3/locking/leases_db.h

diff --git a/source3/librpc/idl/leases_db.idl b/source3/librpc/idl/leases_db.idl
new file mode 100644
index 0000000..28ec479
--- /dev/null
+++ b/source3/librpc/idl/leases_db.idl
@@ -0,0 +1,24 @@
+#include "idl_types.h"
+
+import "misc.idl";
+import "smb2_lease_struct.idl";
+import "file_id.idl";
+
+[
+	pointer_default(unique)
+]
+
+interface leases_db
+{
+	typedef [public] struct {
+	    GUID client_guid;
+	    smb2_lease_key lease_key;
+	} leases_db_key;
+
+	typedef [public] struct {
+		uint32 num_file_ids;
+		[size_is(num_file_ids)] file_id ids[];
+		[string,charset(UTF8)] char *filename;
+		[string,charset(UTF8)] char *stream_name;
+	} leases_db_value;
+}
diff --git a/source3/locking/leases_db.c b/source3/locking/leases_db.c
new file mode 100644
index 0000000..29b8e35
--- /dev/null
+++ b/source3/locking/leases_db.c
@@ -0,0 +1,368 @@
+/*
+   Unix SMB/CIFS implementation.
+   Map lease keys to file ids
+   Copyright (C) Volker Lendecke 2013
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "locking/leases_db.h"
+#include "dbwrap/dbwrap.h"
+#include "dbwrap/dbwrap_open.h"
+#include "util_tdb.h"
+#include "ndr.h"
+#include "librpc/gen_ndr/ndr_leases_db.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_LOCKING
+
+/* the leases database handle */
+static struct db_context *leases_db;
+
+bool leases_db_init(bool read_only)
+{
+	if (leases_db) {
+		return true;
+	}
+
+	leases_db = db_open(NULL, lock_path("leases.tdb"), 0,
+			    TDB_DEFAULT|TDB_VOLATILE|TDB_CLEAR_IF_FIRST|
+			    TDB_INCOMPATIBLE_HASH,
+			    read_only ? O_RDONLY : O_RDWR|O_CREAT, 0644,
+			    DBWRAP_LOCK_ORDER_2, DBWRAP_FLAG_NONE);
+
+	if (leases_db == NULL) {
+		DEBUG(1, ("ERROR: Failed to initialise leases database\n"));
+		return false;
+	}
+
+	return true;
+}
+
+static bool leases_db_key(TALLOC_CTX *mem_ctx,
+			  const struct GUID *client_guid,
+			  const struct smb2_lease_key *lease_key,
+			  TDB_DATA *key)
+{
+	struct leases_db_key db_key = {
+		.client_guid = *client_guid,
+		.lease_key = *lease_key };
+	DATA_BLOB blob;
+	enum ndr_err_code ndr_err;
+
+	if (DEBUGLEVEL >= 10) {
+		DEBUG(10, ("%s:\n", __func__));
+		NDR_PRINT_DEBUG(leases_db_key, &db_key);
+	}
+
+	ndr_err = ndr_push_struct_blob(
+		&blob, mem_ctx, &db_key,
+		(ndr_push_flags_fn_t)ndr_push_leases_db_key);
+	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+		DEBUG(10, ("%s: ndr_push_struct_blob_failed: %s\n",
+			   __func__, ndr_errstr(ndr_err)));
+		return false;
+	}
+
+	*key = make_tdb_data(blob.data, blob.length);
+	return true;
+}
+
+NTSTATUS leases_db_add(const struct GUID *client_guid,
+		       const struct smb2_lease_key *lease_key,
+		       const struct file_id *id,
+		       const char *filename,
+		       const char *stream_name)
+{
+	TDB_DATA db_key, db_value;
+	DATA_BLOB blob;
+	struct db_record *rec;
+	NTSTATUS status;
+	bool ok;
+	struct leases_db_value new_value;
+	struct leases_db_value *value = NULL;
+	enum ndr_err_code ndr_err;
+
+	if (!leases_db_init(false)) {
+		return NT_STATUS_INTERNAL_ERROR;
+	}
+
+	ok = leases_db_key(talloc_tos(), client_guid, lease_key, &db_key);
+	if (!ok) {
+		DEBUG(10, ("%s: leases_db_key failed\n", __func__));
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	rec = dbwrap_fetch_locked(leases_db, talloc_tos(), db_key);
+	TALLOC_FREE(db_key.dptr);
+	if (rec == NULL) {
+		return NT_STATUS_INTERNAL_ERROR;
+	}
+
+	db_value = dbwrap_record_get_value(rec);
+	if (db_value.dsize != 0) {
+		uint32_t i;
+
+		DEBUG(10, ("%s: record exists\n", __func__));
+
+		value = talloc(talloc_tos(), struct leases_db_value);
+		if (value == NULL) {
+			status = NT_STATUS_NO_MEMORY;
+			goto out;
+		}
+
+		blob.data = db_value.dptr;
+		blob.length = db_value.dsize;
+
+		ndr_err = ndr_pull_struct_blob_all(
+			&blob, value, value,
+			(ndr_pull_flags_fn_t)ndr_pull_leases_db_value);
+		if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+			DEBUG(10, ("%s: ndr_pull_struct_blob_failed: %s\n",
+				   __func__, ndr_errstr(ndr_err)));
+			status = ndr_map_error2ntstatus(ndr_err);
+			goto out;
+		}
+
+		/* id must be unique. */
+		for (i = 0; i < value->num_file_ids; i++) {
+			if (file_id_equal(id, &value->ids[i])) {
+				status = NT_STATUS_OBJECT_NAME_COLLISION;
+				goto out;
+			}
+		}
+
+		value->ids = talloc_realloc(value, value->ids, struct file_id,
+					value->num_file_ids + 1);
+		if (value->ids == NULL) {
+			status = NT_STATUS_NO_MEMORY;
+			goto out;
+		}
+		value->ids[value->num_file_ids] = *id;
+		value->num_file_ids += 1;
+
+	} else {
+		new_value = (struct leases_db_value) {
+			.num_file_ids = 1,
+			.ids = id,
+			.filename = filename,
+			.stream_name = stream_name,
+		};
+		value = &new_value;
+	}
+
+	ndr_err = ndr_push_struct_blob(
+		&blob, talloc_tos(), value,
+		(ndr_push_flags_fn_t)ndr_push_leases_db_value);
+	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+		DEBUG(10, ("%s: ndr_push_struct_blob_failed: %s\n",
+			   __func__, ndr_errstr(ndr_err)));
+		status = ndr_map_error2ntstatus(ndr_err);
+		goto out;
+	}
+
+	db_value = make_tdb_data(blob.data, blob.length);
+
+	status = dbwrap_record_store(rec, db_value, 0);
+	if (!NT_STATUS_IS_OK(status)) {
+		DEBUG(10, ("%s: dbwrap_record_store returned %s\n",
+			   __func__, nt_errstr(status)));
+	}
+
+  out:
+
+	if (value != &new_value) {
+		TALLOC_FREE(value);
+	}
+	TALLOC_FREE(rec);
+	return status;
+}
+
+NTSTATUS leases_db_del(const struct GUID *client_guid,
+		       const struct smb2_lease_key *lease_key,
+		       const struct file_id *id)
+{
+	TDB_DATA db_key, db_value;
+	struct db_record *rec;
+	NTSTATUS status;
+	struct leases_db_value *value;
+	enum ndr_err_code ndr_err;
+	DATA_BLOB blob;
+	uint32_t i;
+	bool ok;
+
+	if (!leases_db_init(false)) {
+		return NT_STATUS_INTERNAL_ERROR;
+	}
+
+	ok = leases_db_key(talloc_tos(), client_guid, lease_key, &db_key);
+	if (!ok) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	rec = dbwrap_fetch_locked(leases_db, talloc_tos(), db_key);
+	TALLOC_FREE(db_key.dptr);
+	if (rec == NULL) {
+		return NT_STATUS_NOT_FOUND;
+	}
+	db_value = dbwrap_record_get_value(rec);
+	if (db_value.dsize == 0) {
+		status = NT_STATUS_INTERNAL_ERROR;
+		goto out;
+	}
+
+	value = talloc(talloc_tos(), struct leases_db_value);
+	if (value == NULL) {
+		status = NT_STATUS_NO_MEMORY;
+		goto out;
+	}
+
+	blob.data = db_value.dptr;
+	blob.length = db_value.dsize;
+
+	ndr_err = ndr_pull_struct_blob_all(
+		&blob, value, value,
+		(ndr_pull_flags_fn_t)ndr_pull_leases_db_value);
+	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+		DEBUG(10, ("%s: ndr_pull_struct_blob_failed: %s\n",
+			   __func__, ndr_errstr(ndr_err)));
+		status = ndr_map_error2ntstatus(ndr_err);
+		goto out;
+	}
+
+	/* id must exist. */
+	for (i = 0; i < value->num_file_ids; i++) {
+		if (file_id_equal(id, &value->ids[i])) {
+			break;
+		}
+	}
+
+	if (i == value->num_file_ids) {
+		status = NT_STATUS_NOT_FOUND;
+		goto out;
+	}
+
+	value->ids[i] = value->ids[value->num_file_ids-1];
+	value->num_file_ids -= 1;
+
+	if (value->num_file_ids == 0) {
+		status = dbwrap_record_delete(rec);
+	} else {
+		ndr_err = ndr_push_struct_blob(
+			&blob, talloc_tos(), value,
+			(ndr_push_flags_fn_t)ndr_push_leases_db_value);
+		if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+			DEBUG(10, ("%s: ndr_push_struct_blob_failed: %s\n",
+				   __func__, ndr_errstr(ndr_err)));
+			status = ndr_map_error2ntstatus(ndr_err);
+			goto out;
+		}
+
+		db_value = make_tdb_data(blob.data, blob.length);
+
+		status = dbwrap_record_store(rec, db_value, 0);
+		if (!NT_STATUS_IS_OK(status)) {
+			DEBUG(10, ("%s: dbwrap_record_store returned %s\n",
+				   __func__, nt_errstr(status)));
+		}
+	}
+
+  out:
+
+	TALLOC_FREE(value);
+	TALLOC_FREE(rec);
+	return status;
+}
+
+struct leases_db_fetch_state {
+	void (*parser)(uint32_t num_file_ids,
+			struct file_id *ids, const char *filename,
+			const char *stream_name, void *private_data);
+	void *private_data;
+	NTSTATUS status;
+};
+
+static void leases_db_parser(TDB_DATA key, TDB_DATA data, void *private_data)
+{
+	struct leases_db_fetch_state *state =
+		(struct leases_db_fetch_state *)private_data;
+	DATA_BLOB blob = { .data = data.dptr, .length = data.dsize };
+	enum ndr_err_code ndr_err;
+	struct leases_db_value *value;
+
+	value = talloc(talloc_tos(), struct leases_db_value);
+	if (value == NULL) {
+		state->status = NT_STATUS_NO_MEMORY;
+		return;
+	}
+
+	ndr_err = ndr_pull_struct_blob_all(
+		&blob, value, value,
+		(ndr_pull_flags_fn_t)ndr_pull_leases_db_value);
+	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+		DEBUG(10, ("%s: ndr_pull_struct_blob_failed: %s\n",
+			   __func__, ndr_errstr(ndr_err)));
+		TALLOC_FREE(value);
+		state->status = ndr_map_error2ntstatus(ndr_err);
+		return;
+	}
+
+	state->parser(value->num_file_ids,
+			value->ids, value->filename, value->stream_name,
+			state->private_data);
+
+	TALLOC_FREE(value);
+	state->status = NT_STATUS_OK;
+}
+
+NTSTATUS leases_db_parse(const struct GUID *client_guid,
+			 const struct smb2_lease_key *lease_key,
+			 void (*parser)(uint32_t num_file_ids,
+					struct file_id *ids,
+					const char *filename,
+					const char *stream_name,
+					void *private_data),
+			 void *private_data)
+{
+	TDB_DATA db_key;
+	struct leases_db_fetch_state state;
+	NTSTATUS status;
+	bool ok;
+
+	if (!leases_db_init(true)) {
+		return NT_STATUS_INTERNAL_ERROR;
+	}
+
+	ok = leases_db_key(talloc_tos(), client_guid, lease_key, &db_key);
+	if (!ok) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	state = (struct leases_db_fetch_state) {
+		.parser = parser,
+		.private_data = private_data,
+		.status = NT_STATUS_OK
+	};
+
+	status = dbwrap_parse_record(leases_db, db_key, leases_db_parser,
+				     &state);
+	TALLOC_FREE(db_key.dptr);
+	if (!NT_STATUS_IS_OK(status)) {
+		return status;
+	}
+	return state.status;
+}
diff --git a/source3/locking/leases_db.h b/source3/locking/leases_db.h
new file mode 100644
index 0000000..f570356
--- /dev/null
+++ b/source3/locking/leases_db.h
@@ -0,0 +1,46 @@
+/*
+ *  Unix SMB/CIFS implementation.
+ *  leases.tdb functions
+ *
+ *  Copyright (C) Volker Lendecke 2014
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _LEASES_DB_H_
+#define _LEASES_DB_H_
+
+struct GUID;
+struct smb2_lease_key;
+struct file_id;
+
+bool leases_db_init(bool read_only);
+NTSTATUS leases_db_add(const struct GUID *client_guid,
+		       const struct smb2_lease_key *lease_key,
+		       const struct file_id *id,
+		       const char *filename,
+		       const char *stream_name);
+NTSTATUS leases_db_del(const struct GUID *client_guid,
+		       const struct smb2_lease_key *lease_key,
+		       const struct file_id *id);
+NTSTATUS leases_db_parse(const struct GUID *client_guid,
+			 const struct smb2_lease_key *lease_key,
+			 void (*parser)(uint32_t num_file_ids,
+					struct file_id *ids,
+					const char *filename,
+					const char *stream_name,
+					void *private_data),
+			 void *private_data);
+
+#endif /* _LEASES_DB_H_ */
-- 
1.9.1


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

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

diff --git a/source3/locking/locking.c b/source3/locking/locking.c
index 170fa1d..8132c71 100644
--- a/source3/locking/locking.c
+++ b/source3/locking/locking.c
@@ -618,6 +618,85 @@ bool is_valid_share_mode_entry(const struct share_mode_entry *e)
 }
 
 /*
+ * See if we need to remove a lease being referred to by a
+ * share mode that is being marked stale or deleted.
+ */
+
+static void remove_share_mode_lease(struct share_mode_data *d,
+				    struct share_mode_entry *e)
+{
+	struct GUID client_guid;
+	struct smb2_lease_key lease_key;
+	uint16_t op_type;
+	uint32_t lease_idx;
+	uint32_t i;
+
+	op_type = e->op_type;
+	e->op_type = NO_OPLOCK;
+
+	d->modified = true;
+
+	if (op_type != LEASE_OPLOCK) {
+		return;
+	}
+
+	/*
+	 * This used to reference a lease. If there's no other one referencing
+	 * it, remove it.
+	 */
+
+	lease_idx = e->lease_idx;
+	e->lease_idx = UINT32_MAX;
+
+	for (i=0; i<d->num_share_modes; i++) {
+		if (d->share_modes[i].stale) {
+			continue;
+		}
+		if (e == &d->share_modes[i]) {
+			/* Not ourselves. */
+			continue;
+		}
+		if (d->share_modes[i].lease_idx == lease_idx) {
+			break;
+		}
+	}
+	if (i < d->num_share_modes) {
+		/*
+		 * Found another one
+		 */
+		return;
+	}
+
+	memcpy(&client_guid,
+		&d->leases[lease_idx].client_guid,
+		sizeof(client_guid));
+	lease_key = d->leases[lease_idx].lease_key;
+
+	d->num_leases -= 1;
+	d->leases[lease_idx] = d->leases[d->num_leases];
+
+	/*
+	 * We changed the lease array. Fix all references to it.
+	 */
+	for (i=0; i<d->num_share_modes; i++) {
+		if (d->share_modes[i].lease_idx == d->num_leases) {
+			d->share_modes[i].lease_idx = lease_idx;
+		}
+	}
+
+	{
+		NTSTATUS status;
+
+		status = leases_db_del(&client_guid,
+					&lease_key,
+					&e->id);
+
+		DEBUG(10, ("%s: leases_db_del returned %s\n", __func__,
+			   nt_errstr(status)));
+	}
+}
+
+/*
  * In case d->share_modes[i] conflicts with something or otherwise is
  * being used, we need to make sure the corresponding process still
  * exists.
@@ -675,6 +754,8 @@ bool share_mode_stale_pid(struct share_mode_data *d, uint32_t idx)
 		}
 	}
 
+	remove_share_mode_lease(d, e);
+
 	d->modified = true;
 	return true;
 }
@@ -773,6 +854,7 @@ bool del_share_mode(struct share_mode_lock *lck, files_struct *fsp)
 	if (e == NULL) {
 		return False;
 	}
+	remove_share_mode_lease(lck->data, e);
 	*e = lck->data->share_modes[lck->data->num_share_modes-1];
 	lck->data->num_share_modes -= 1;
 	lck->data->modified = True;
@@ -822,67 +904,14 @@ bool remove_share_oplock(struct share_mode_lock *lck, files_struct *fsp)
 {
 	struct share_mode_data *d = lck->data;
 	struct share_mode_entry *e;
-	uint16_t op_type;
-	uint32_t lease_idx;
-	uint32_t i;
 
 	e = find_share_mode_entry(lck, fsp);
 	if (e == NULL) {
 		return False;
 	}
 
-	op_type = e->op_type;
-	e->op_type = NO_OPLOCK;
-
+	remove_share_mode_lease(d, e);
 	d->modified = True;
-
-	if (op_type != LEASE_OPLOCK) {
-		return true;
-	}
-
-	/*
-	 * This used to reference a lease. If there's no other one referencing
-	 * it, remove it.
-	 */
-
-	lease_idx = e->lease_idx;
-	e->lease_idx = UINT32_MAX;
-
-	for (i=0; i<d->num_share_modes; i++) {
-		if (d->share_modes[i].lease_idx == lease_idx) {
-			break;
-		}
-	}
-	if (i < d->num_share_modes) {
-		/*
-		 * Found another one
-		 */
-		return true;
-	}
-
-	d->num_leases -= 1;
-	d->leases[lease_idx] = d->leases[d->num_leases];
-
-	/*
-	 * We changed the lease array. Fix all references to it.
-	 */
-	for (i=0; i<d->num_share_modes; i++) {
-		if (d->share_modes[i].lease_idx == d->num_leases) {
-			d->share_modes[i].lease_idx = lease_idx;
-		}
-	}
-
-	{
-		NTSTATUS status;
-
-		status = leases_db_del(
-			&fsp->conn->sconn->client->connections->smb2.client.guid,
-			&fsp->lease->lease.lease_key);
-
-		DEBUG(10, ("%s: leases_db_del returned %s\n", __func__,
-			   nt_errstr(status)));
-	}
-
 	return true;
 }
 
-- 
1.9.1


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

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

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


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

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

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

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

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


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

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

diff --git a/source3/smbd/files.c b/source3/smbd/files.c
index 13d1138..6a987c4 100644
--- a/source3/smbd/files.c
+++ b/source3/smbd/files.c
@@ -774,3 +774,8 @@ uint32_t fsp_lease_type(struct files_struct *fsp)
 	}
 	return map_oplock_to_lease_type(fsp->oplock_type);
 }
+
+const struct GUID *fsp_client_guid(const files_struct *fsp)
+{
+	return &fsp->conn->sconn->client->connections->smb2.client.guid;
+}
diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
index 22b1a82..ecf89a7 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -391,6 +391,7 @@ NTSTATUS file_name_hash(connection_struct *conn,
 NTSTATUS fsp_set_smb_fname(struct files_struct *fsp,
 			   const struct smb_filename *smb_fname_in);
 uint32_t fsp_lease_type(struct files_struct *fsp);
+const struct GUID *fsp_client_guid(const files_struct *fsp);
 
 /* The following definitions come from smbd/ipc.c  */
 
-- 
1.9.1


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

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

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


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

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

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


From aaaa1b9b3c51421b5780d7cfac25b60716982764 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 11/46] s3:param: Add "smb2 leases" parameter. Default "false".

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

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


From a695459428c283d56e670853330a9e9677eb4702 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 12/46] s3:smb2_negprot: announce support for SMB2.1 leases.

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

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

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


From cb105c8931c71607d56ac7680922b0dd8a072145 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 13/46] selftest:Samba3: use "smb2 leases = yes"

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

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


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

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

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


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

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

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


From 6a0176105b825aaaa6e1f77c807e8dd14d30e774 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 30 Oct 2014 10:10:07 +0100
Subject: [PATCH 16/46] SQ grant_fsp_oplock_type: minimize the diff

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

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


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

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

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


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

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

diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index af99f36..f9effe3 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1727,6 +1727,10 @@ static NTSTATUS grant_fsp_oplock_type(struct smb_request *req,
 	if (oplock_request == LEASE_OPLOCK) {
 		granted = lease->lease_state;
 
+		if (lp_kernel_oplocks(SNUM(fsp->conn))) {
+			DEBUG(10, ("No lease granted because kernel oplocks are enabled\n"));
+			granted = SMB2_LEASE_NONE;
+		}
 		if ((granted & (SMB2_LEASE_READ|SMB2_LEASE_WRITE)) == 0) {
 			DEBUG(10, ("No read or write lease requested\n"));
 			granted = SMB2_LEASE_NONE;
@@ -1821,14 +1825,14 @@ static NTSTATUS grant_fsp_oplock_type(struct smb_request *req,
 			fsp->oplock_type = NO_OPLOCK;
 			break;
 		}
-	}
 
-	status = set_file_oplock(fsp);
-	if (!NT_STATUS_IS_OK(status)) {
-		/*
-		 * Could not get the kernel oplock
-		 */
-		fsp->oplock_type = NO_OPLOCK;
+		status = set_file_oplock(fsp);
+		if (!NT_STATUS_IS_OK(status)) {
+			/*
+			 * Could not get the kernel oplock
+			 */
+			fsp->oplock_type = NO_OPLOCK;
+		}
 	}
 
 	ok = set_share_mode(lck, fsp, get_current_uid(fsp->conn),
-- 
1.9.1


From 9d358d58470c506430113bae69522f4e41a0434d Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Tue, 11 Nov 2014 22:09:35 +0100
Subject: [PATCH 19/46] SQ smb2_create only allow leases if negotiated

---
 source3/smbd/smb2_create.c | 33 ++++++++++++++-------------------
 1 file changed, 14 insertions(+), 19 deletions(-)

diff --git a/source3/smbd/smb2_create.c b/source3/smbd/smb2_create.c
index 38ef04c..0c49c70 100644
--- a/source3/smbd/smb2_create.c
+++ b/source3/smbd/smb2_create.c
@@ -479,6 +479,7 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 	struct smb2_create_blob *dh2c = NULL;
 	struct smb2_create_blob *dhnq = NULL;
 	struct smb2_create_blob *dh2q = NULL;
+	struct smb2_create_blob *rqls = NULL;
 	struct smbXsrv_open *op = NULL;
 
 	ZERO_STRUCT(out_context_blobs);
@@ -525,6 +526,10 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 				     SMB2_CREATE_TAG_DH2Q);
 	dh2c = smb2_create_blob_find(&in_context_blobs,
 				     SMB2_CREATE_TAG_DH2C);
+	if (smb2req->xconn->smb2.server.capabilities & SMB2_CAP_LEASING) {
+		rqls = smb2_create_blob_find(&in_context_blobs,
+					     SMB2_CREATE_TAG_RQLS);
+	}
 
 	if ((dhnc && dh2c) || (dhnc && dh2q) || (dh2c && dhnq) ||
 	    (dh2q && dh2c))
@@ -564,8 +569,7 @@ 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) {
+		if (rqls != NULL) {
 			num_blobs_allowed += 1;
 		}
 
@@ -601,8 +605,7 @@ 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) {
+		if (rqls != NULL) {
 			num_blobs_allowed += 1;
 		}
 
@@ -667,14 +670,13 @@ 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;
+		struct smb2_lease *lease_ptr = NULL;
 		ssize_t lease_len = -1;
 
 		exta = smb2_create_blob_find(&in_context_blobs,
@@ -689,8 +691,6 @@ 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)) {
@@ -877,8 +877,6 @@ 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);
@@ -900,18 +898,15 @@ 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) &&
+			if ((smb2req->xconn->protocol < PROTOCOL_SMB3_00) &&
 			    (lease.lease_version != 1)) {
 				DEBUG(10, ("v2 lease key only for SMB3\n"));
 				lease_ptr = NULL;
+			}
+		}
+
+		if (requested_oplock_level == SMB2_OPLOCK_LEVEL_LEASE) {
+			if (lease_ptr == NULL) {
 				requested_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
 			}
 		}
-- 
1.9.1


From 93f7bb3731ea0489541c2dc1663f17e1bfaa65ef Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Wed, 12 Nov 2014 02:24:49 +0100
Subject: [PATCH 20/46] SQ only update the epoch number on change...

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

diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index f9effe3..afc9cd4 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1636,6 +1636,11 @@ static NTSTATUS grant_fsp_lease(files_struct *fsp, struct share_mode_data *d,
 		do_upgrade = ((existing & requested) == existing);
 
 		/*
+		 * Upgrade only if there's a change.
+		 */
+		do_upgrade &= (granted != existing);
+
+		/*
 		 * Upgrade only if other leases don't prevent what was asked
 		 * for.
 		 */
-- 
1.9.1


From 8bb588709992ca18bc7e8652a402e7db5b9bddbe Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Tue, 11 Nov 2014 15:10:56 -0800
Subject: [PATCH 21/46] s3: leases : If we are requested to send a break
 message from another smbd, ensure we're in sync with the current lease state.

If a lease is shared between two smbd processes the state can get out of sync.

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 source3/smbd/oplock.c | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/source3/smbd/oplock.c b/source3/smbd/oplock.c
index e97ade6..d28914c 100644
--- a/source3/smbd/oplock.c
+++ b/source3/smbd/oplock.c
@@ -598,9 +598,7 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx,
 		return;
 	}
 
-	if ((fsp->oplock_type == LEASE_OPLOCK) &&
-	    (fsp->lease->lease.lease_version != 1)) {
-		/* Need to increment the epoch */
+	if (fsp->oplock_type == LEASE_OPLOCK) {
 		struct share_mode_lock *lck;
 		int idx;
 
@@ -615,8 +613,13 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx,
 		if (idx != -1) {
 			struct share_mode_oplock *o;
 			o = &lck->data->leases[idx];
-			o->epoch += 1;
-			fsp->lease->lease.lease_epoch = o->epoch;
+			/* Ensure we're in sync with current lease state. */
+			fsp->lease->lease.lease_state = o->current_state;
+			if (fsp->lease->lease.lease_version != 1) {
+				/* Need to increment the epoch */
+				o->epoch += 1;
+				fsp->lease->lease.lease_epoch = o->epoch;
+			}
 		}
 
 		TALLOC_FREE(lck);
-- 
1.9.1


From 8f50da6e4b2b3e9a262b56c2edb6a25c8260503e Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Tue, 11 Nov 2014 16:29:40 -0800
Subject: [PATCH 22/46] s3: leases: I just had an epiphany. As we need to keep
 lease state in sync, *always* increment epoch on changes.

Internally treat all leases as v2, smb2_lease_push() will zero out the epoch on v1 reply.

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 source3/smbd/open.c   | 10 ++++------
 source3/smbd/oplock.c |  8 +++-----
 2 files changed, 7 insertions(+), 11 deletions(-)

diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index afc9cd4..fd2bae1 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1593,6 +1593,8 @@ struct fsp_lease *find_fsp_lease(files_struct *new_fsp,
 	new_fsp->lease->ref_count = 1;
 	new_fsp->lease->lease.lease_key = *key;
 	new_fsp->lease->lease.lease_state = o->current_state;
+	/* Internally treat all leases as version 2 with epoch. */
+	new_fsp->lease->lease.lease_version = 2;
 	new_fsp->lease->lease.lease_epoch = o->epoch;
 	return new_fsp->lease;
 }
@@ -1678,9 +1680,7 @@ 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;
-	if (fsp->lease->lease.lease_version > 1) {
-		fsp->lease->lease.lease_epoch += 1;
-	}
+	fsp->lease->lease.lease_epoch += 1;
 
 	*p_lease_idx = d->num_leases;
 
@@ -4316,9 +4316,7 @@ static NTSTATUS create_file_unixpath(connection_struct *conn,
 		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;
-			}
+			lease->lease_epoch += 1;
 		} else if (!NT_STATUS_IS_OK(status)) {
 			goto fail;
 		}
diff --git a/source3/smbd/oplock.c b/source3/smbd/oplock.c
index d28914c..5523de9 100644
--- a/source3/smbd/oplock.c
+++ b/source3/smbd/oplock.c
@@ -615,11 +615,9 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx,
 			o = &lck->data->leases[idx];
 			/* Ensure we're in sync with current lease state. */
 			fsp->lease->lease.lease_state = o->current_state;
-			if (fsp->lease->lease.lease_version != 1) {
-				/* Need to increment the epoch */
-				o->epoch += 1;
-				fsp->lease->lease.lease_epoch = o->epoch;
-			}
+			/* Need to increment the epoch */
+			o->epoch += 1;
+			fsp->lease->lease.lease_epoch = o->epoch;
 		}
 
 		TALLOC_FREE(lck);
-- 
1.9.1


From d9917803c19d538f8f3301664cfe1506467240bc Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Tue, 11 Nov 2014 16:31:37 -0800
Subject: [PATCH 23/46] s3: leases: Remember to update epoch on lease update
 (e.g. from RH -> RWH).

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

diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index fd2bae1..3f576d8 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1654,6 +1654,8 @@ static NTSTATUS grant_fsp_lease(files_struct *fsp, struct share_mode_data *d,
 
 		if (do_upgrade) {
 			o->current_state = granted;
+			o->epoch += 1;
+			fsp->lease->lease.lease_epoch = o->epoch;
 		}
 		fsp->lease->lease.lease_state = o->current_state;
 		return NT_STATUS_OK;
-- 
1.9.1


From cc0e345f509dc292979a8ca653b9345d8df3938b Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Tue, 11 Nov 2014 19:33:13 +0100
Subject: [PATCH 24/46] s4:libcli/smb2: add new_epoch to struct
 smb2_lease_break

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/libcli/raw/interfaces.h | 1 +
 source4/libcli/smb2/transport.c | 1 +
 2 files changed, 2 insertions(+)

diff --git a/source4/libcli/raw/interfaces.h b/source4/libcli/raw/interfaces.h
index 9003c12..05f5da0 100644
--- a/source4/libcli/raw/interfaces.h
+++ b/source4/libcli/raw/interfaces.h
@@ -57,6 +57,7 @@ struct smb2_handle {
 
 struct smb2_lease_break {
 	struct smb2_lease current_lease;
+	uint16_t new_epoch; /* only for v2 leases */
 	uint32_t break_flags;
 	uint32_t new_lease_state;
 	uint32_t break_reason; /* should be 0 */
diff --git a/source4/libcli/smb2/transport.c b/source4/libcli/smb2/transport.c
index 9b0c146..d9c3815 100644
--- a/source4/libcli/smb2/transport.c
+++ b/source4/libcli/smb2/transport.c
@@ -390,6 +390,7 @@ static void smb2_transport_break_handler(struct tevent_req *subreq)
 		struct smb2_lease_break lb;
 
 		ZERO_STRUCT(lb);
+		lb.new_epoch =			SVAL(body, 0x2);
 		lb.break_flags =		SVAL(body, 0x4);
 		memcpy(&lb.current_lease.lease_key, body+0x8,
 		    sizeof(struct smb2_lease_key));
-- 
1.9.1


From 05399a090f560b79c3f9c3b83f78a4bd7585dc5a Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Wed, 12 Nov 2014 11:13:15 +0100
Subject: [PATCH 25/46] s4:libcli/smb2: initialize ls->lease_version

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source4/libcli/smb2/create.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/source4/libcli/smb2/create.c b/source4/libcli/smb2/create.c
index 10fe4f7..550069a 100644
--- a/source4/libcli/smb2/create.c
+++ b/source4/libcli/smb2/create.c
@@ -385,9 +385,11 @@ NTSTATUS smb2_create_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx, struct
 			switch (io->out.blobs.blobs[i].data.length) {
 			case 32:
 				ls = &io->out.lease_response;
+				ls->lease_version = 1;
 				break;
 			case 52:
 				ls = &io->out.lease_response_v2;
+				ls->lease_version = 2;
 				break;
 			default:
 				smb2_request_destroy(req);
-- 
1.9.1


From 76719360f8ff363ad05bdcc62bd58f0e0afe1ca5 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Wed, 12 Nov 2014 07:19:49 +0100
Subject: [PATCH 26/46] s4:torture/smb2: skip lease tests if the server doesn't
 support them

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

diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c
index 4326958..e06faea 100644
--- a/source4/torture/smb2/lease.c
+++ b/source4/torture/smb2/lease.c
@@ -142,12 +142,14 @@ static bool test_lease_request(struct torture_context *tctx,
 	CHECK_LEASE(&io, "RHW", true, LEASE1);
 
 	/* But will reject leases on directories. */
-	smb2_lease_create(&io, &ls, true, dname, LEASE2, smb2_util_lease_state("RHW"));
-	status = smb2_create(tree, mem_ctx, &io);
-	CHECK_STATUS(status, NT_STATUS_OK);
-	CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_DIRECTORY);
-	CHECK_LEASE(&io, "", false, 0);
-	smb2_util_close(tree, io.out.file.handle);
+	if (!(caps & SMB2_CAP_DIRECTORY_LEASING)) {
+		smb2_lease_create(&io, &ls, true, dname, LEASE2, smb2_util_lease_state("RHW"));
+		status = smb2_create(tree, mem_ctx, &io);
+		CHECK_STATUS(status, NT_STATUS_OK);
+		CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_DIRECTORY);
+		CHECK_LEASE(&io, "", false, 0);
+		smb2_util_close(tree, io.out.file.handle);
+	}
 
 	/* Also rejects multiple files leased under the same key. */
 	smb2_lease_create(&io, &ls, true, fname2, LEASE1, smb2_util_lease_state("RHW"));
@@ -1191,6 +1193,21 @@ static bool test_lease_v2_request_parent(struct torture_context *tctx,
 	NTSTATUS status;
 	const char *fname = "lease.dat";
 	bool ret = true;
+	uint32_t caps;
+	enum protocol_types protocol;
+
+	caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
+	if (!(caps & SMB2_CAP_LEASING)) {
+		torture_skip(tctx, "leases are not supported");
+	}
+	if (!(caps & SMB2_CAP_DIRECTORY_LEASING)) {
+		torture_skip(tctx, "directory leases are not supported");
+	}
+
+	protocol = smbXcli_conn_protocol(tree->session->transport->conn);
+	if (protocol < PROTOCOL_SMB3_00) {
+		torture_skip(tctx, "v2 leases are not supported");
+	}
 
 	smb2_util_unlink(tree, fname);
 
@@ -1230,6 +1247,7 @@ static bool test_lease_break_twice(struct torture_context *tctx,
 	const char *fname = "lease.dat";
 	bool ret = true;
 	uint32_t caps;
+	enum protocol_types protocol;
 
 	caps = smb2cli_conn_server_capabilities(
 		tree->session->transport->conn);
@@ -1237,6 +1255,11 @@ static bool test_lease_break_twice(struct torture_context *tctx,
 		torture_skip(tctx, "leases are not supported");
 	}
 
+	protocol = smbXcli_conn_protocol(tree->session->transport->conn);
+	if (protocol < PROTOCOL_SMB3_00) {
+		torture_skip(tctx, "v2 leases are not supported");
+	}
+
 	smb2_util_unlink(tree, fname);
 
 	ZERO_STRUCT(break_info);
@@ -1297,6 +1320,21 @@ static bool test_lease_v2_request(struct torture_context *tctx,
 	const char *dnamefname = "lease.dir\\lease.dat";
 	const char *dnamefname2 = "lease.dir\\lease2.dat";
 	bool ret = true;
+	uint32_t caps;
+	enum protocol_types protocol;
+
+	caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
+	if (!(caps & SMB2_CAP_LEASING)) {
+		torture_skip(tctx, "leases are not supported");
+	}
+	if (!(caps & SMB2_CAP_DIRECTORY_LEASING)) {
+		torture_skip(tctx, "directory leases are not supported");
+	}
+
+	protocol = smbXcli_conn_protocol(tree->session->transport->conn);
+	if (protocol < PROTOCOL_SMB3_00) {
+		torture_skip(tctx, "v2 leases are not supported");
+	}
 
 	smb2_util_unlink(tree, fname);
 	smb2_deltree(tree, dname);
@@ -1427,6 +1465,18 @@ static bool test_lease_v2_epoch1(struct torture_context *tctx,
 	const char *fname = "lease.dat";
 	bool ret = true;
 	NTSTATUS status;
+	uint32_t caps;
+	enum protocol_types protocol;
+
+	caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
+	if (!(caps & SMB2_CAP_LEASING)) {
+		torture_skip(tctx, "leases are not supported");
+	}
+
+	protocol = smbXcli_conn_protocol(tree->session->transport->conn);
+	if (protocol < PROTOCOL_SMB3_00) {
+		torture_skip(tctx, "v2 leases are not supported");
+	}
 
 	smb2_util_unlink(tree, fname);
 
-- 
1.9.1


From b0ac312fa73a4320be1b257be37613649ec92e03 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Wed, 12 Nov 2014 07:23:31 +0100
Subject: [PATCH 27/46] s4:torture/smb2: make lease tests more reliable by
 calling torture_wait_for_lease_break()

They now work against w2k8r2, w2012, w2012r2.

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

diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c
index e06faea..5aade0c 100644
--- a/source4/torture/smb2/lease.c
+++ b/source4/torture/smb2/lease.c
@@ -406,14 +406,35 @@ static struct {
 	int oplock_failures;
 } break_info;
 
+#define CHECK_NO_BREAK(tctx)	\
+	do {								\
+		torture_wait_for_lease_break(tctx);			\
+		CHECK_VAL(break_info.failures, 0);			\
+		CHECK_VAL(break_info.count, 0);				\
+		CHECK_VAL(break_info.oplock_failures, 0);		\
+		CHECK_VAL(break_info.oplock_count, 0);			\
+	} while(0)
+
+#define CHECK_OPLOCK_BREAK(__brokento)	\
+	do {								\
+		torture_wait_for_lease_break(tctx);			\
+		CHECK_VAL(break_info.oplock_count, 1);			\
+		CHECK_VAL(break_info.oplock_failures, 0);		\
+		CHECK_VAL(break_info.oplock_level,			\
+			  smb2_util_oplock_level(__brokento)); \
+		break_info.held_oplock_level = break_info.oplock_level; \
+	} while(0)
+
 #define CHECK_BREAK_INFO(__oldstate, __state, __key)			\
 	do {								\
+		torture_wait_for_lease_break(tctx);			\
 		CHECK_VAL(break_info.failures, 0);			\
 		CHECK_VAL(break_info.count, 1);				\
 		CHECK_LEASE_BREAK(&break_info.lease_break, (__oldstate), \
 		    (__state), (__key));				\
 		if (break_info.lease_break.break_flags &		\
 		    SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED) {	\
+			torture_wait_for_lease_break(tctx);		\
 			CHECK_LEASE_BREAK_ACK(&break_info.lease_break_ack, \
 				              (__state), (__key));	\
 		}							\
@@ -819,9 +840,7 @@ static bool test_lease_nobreakself(struct torture_context *tctx,
 	/* Make sure we don't break ourselves on write */
 
 	status = smb2_util_write(tree, h1, &c, 0, 1);
-	torture_wait_for_lease_break(tctx);
-	CHECK_VAL(break_info.count, 1);
-	CHECK_VAL(break_info.failures, 0);
+	CHECK_STATUS(status, NT_STATUS_OK);
 	CHECK_BREAK_INFO("R", "", LEASE2);
 
 	/* Try the other way round. First, upgrade LEASE2 to R again */
@@ -837,18 +856,14 @@ static bool test_lease_nobreakself(struct torture_context *tctx,
 
 	ZERO_STRUCT(break_info);
 	status = smb2_util_write(tree, h2, &c, 0, 1);
-	torture_wait_for_lease_break(tctx);
-	CHECK_VAL(break_info.count, 1);
-	CHECK_VAL(break_info.failures, 0);
+	CHECK_STATUS(status, NT_STATUS_OK);
 	CHECK_BREAK_INFO("R", "", LEASE1);
 
 	/* .. and break LEASE2 via h1 */
 
 	ZERO_STRUCT(break_info);
 	status = smb2_util_write(tree, h1, &c, 0, 1);
-	torture_wait_for_lease_break(tctx);
-	CHECK_VAL(break_info.count, 1);
-	CHECK_VAL(break_info.failures, 0);
+	CHECK_STATUS(status, NT_STATUS_OK);
 	CHECK_BREAK_INFO("R", "", LEASE2);
 
 done:
@@ -992,8 +1007,7 @@ static bool test_lease_oplock(struct torture_context *tctx,
 		if (smb2_util_lease_state(held) != smb2_util_lease_state(brokento)) {
 			CHECK_BREAK_INFO(held, brokento, LEASE1);
 		} else {
-			CHECK_VAL(break_info.count, 0);
-			CHECK_VAL(break_info.failures, 0);
+			CHECK_NO_BREAK(tctx);
 		}
 
 		smb2_util_close(tree, h);
@@ -1033,13 +1047,9 @@ static bool test_lease_oplock(struct torture_context *tctx,
 		CHECK_LEASE(&io, granted, true, LEASE1);
 
 		if (smb2_util_oplock_level(held) != smb2_util_oplock_level(brokento)) {
-			CHECK_VAL(break_info.oplock_count, 1);
-			CHECK_VAL(break_info.oplock_failures, 0);
-			CHECK_VAL(break_info.oplock_level, smb2_util_oplock_level(brokento));
-			break_info.held_oplock_level = break_info.oplock_level;
+			CHECK_OPLOCK_BREAK(brokento);
 		} else {
-			CHECK_VAL(break_info.oplock_count, 0);
-			CHECK_VAL(break_info.oplock_failures, 0);
+			CHECK_NO_BREAK(tctx);
 		}
 
 		smb2_util_close(tree, h);
@@ -1141,8 +1151,7 @@ static bool test_lease_multibreak(struct torture_context *tctx,
 	break_info.held_oplock_level = io.out.oplock_level;
 
 	/* Verify no breaks. */
-	CHECK_VAL(break_info.count, 0);
-	CHECK_VAL(break_info.failures, 0);
+	CHECK_NO_BREAK(tctx);
 
 	/* Open for truncate, force a break. */
 	smb2_generic_create(&io, NULL, false, fname,
@@ -1165,9 +1174,7 @@ static bool test_lease_multibreak(struct torture_context *tctx,
 	CHECK_STATUS(status, NT_STATUS_OK);
 
 	/* Verify one oplock break, one lease break. */
-	CHECK_VAL(break_info.oplock_count, 1);
-	CHECK_VAL(break_info.oplock_failures, 0);
-	CHECK_VAL(break_info.oplock_level, smb2_util_oplock_level(""));
+	CHECK_OPLOCK_BREAK("");
 	CHECK_BREAK_INFO("R", "", LEASE1);
 
  done:
@@ -1384,9 +1391,7 @@ static bool test_lease_v2_request(struct torture_context *tctx,
 	CHECK_LEASE_V2(&io, "RHW", true, LEASE3,
 		       SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET, LEASE2);
 
-	torture_wait_for_lease_break(tctx);
-	CHECK_VAL(break_info.count, 0);
-	CHECK_VAL(break_info.failures, 0);
+	CHECK_NO_BREAK(tctx);
 
 	ZERO_STRUCT(io);
 	smb2_lease_v2_create_share(&io, &ls, false, dnamefname2,
@@ -1400,10 +1405,7 @@ static bool test_lease_v2_request(struct torture_context *tctx,
 	CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
 	CHECK_LEASE_V2(&io, "RHW", true, LEASE4, 0, 0);
 
-	torture_wait_for_lease_break(tctx);
-	torture_wait_for_lease_break(tctx);
 	CHECK_BREAK_INFO("RH", "", LEASE2);
-	torture_wait_for_lease_break(tctx);
 
 	ZERO_STRUCT(break_info);
 
@@ -1429,16 +1431,19 @@ static bool test_lease_v2_request(struct torture_context *tctx,
 	status = smb2_write(tree, &w);
 	CHECK_STATUS(status, NT_STATUS_OK);
 
-	smb_msleep(2000);
-	torture_wait_for_lease_break(tctx);
-	CHECK_VAL(break_info.count, 0);
-	CHECK_VAL(break_info.failures, 0);
-
+	/*
+	 * Wait 4 seconds in order to check if the write time
+	 * was updated (after 2 seconds).
+	 */
+	smb_msleep(4000);
+	CHECK_NO_BREAK(tctx);
+
+	/*
+	 * only the close on the modified file break the
+	 * directory lease.
+	 */
 	smb2_util_close(tree, h4);
-	torture_wait_for_lease_break(tctx);
-	torture_wait_for_lease_break(tctx);
 	CHECK_BREAK_INFO("RH", "", LEASE2);
-	torture_wait_for_lease_break(tctx);
 
  done:
 	smb2_util_close(tree, h1);
-- 
1.9.1


From 3f0d6a840758c6637300e711da26b0b33133768d Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Wed, 12 Nov 2014 07:24:16 +0100
Subject: [PATCH 28/46] s4:torture/smb2: lease per test fnames

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

diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c
index 5aade0c..ef88473 100644
--- a/source4/torture/smb2/lease.c
+++ b/source4/torture/smb2/lease.c
@@ -116,10 +116,10 @@ static bool test_lease_request(struct torture_context *tctx,
 	struct smb2_lease ls;
 	struct smb2_handle h1, h2;
 	NTSTATUS status;
-	const char *fname = "lease.dat";
-	const char *fname2 = "lease2.dat";
-	const char *sname = "lease.dat:stream";
-	const char *dname = "lease.dir";
+	const char *fname = "lease_request.dat";
+	const char *fname2 = "lease_request.2.dat";
+	const char *sname = "lease_request.dat:stream";
+	const char *dname = "lease_request.dir";
 	bool ret = true;
 	int i;
 	uint32_t caps;
@@ -204,7 +204,7 @@ static bool test_lease_upgrade(struct torture_context *tctx,
 	struct smb2_lease ls;
 	struct smb2_handle h, hnew;
 	NTSTATUS status;
-	const char *fname = "lease.dat";
+	const char *fname = "lease_upgrade.dat";
 	bool ret = true;
 	uint32_t caps;
 
@@ -328,7 +328,7 @@ static bool test_lease_upgrade2(struct torture_context *tctx,
 	NTSTATUS status;
 	struct smb2_create io;
 	struct smb2_lease ls;
-	const char *fname = "lease.dat";
+	const char *fname = "lease_upgrade2.dat";
 	bool ret = true;
 	int i;
 	uint32_t caps;
@@ -545,7 +545,7 @@ static bool test_lease_upgrade3(struct torture_context *tctx,
 	NTSTATUS status;
 	struct smb2_create io;
 	struct smb2_lease ls;
-	const char *fname = "upgrade3.dat";
+	const char *fname = "lease_upgrade3.dat";
 	bool ret = true;
 	int i;
 	uint32_t caps;
@@ -711,7 +711,7 @@ static bool test_lease_break(struct torture_context *tctx,
 	struct smb2_lease ls;
 	struct smb2_handle h, h2, h3;
 	NTSTATUS status;
-	const char *fname = "lease.dat";
+	const char *fname = "lease_break.dat";
 	bool ret = true;
 	int i;
 	uint32_t caps;
@@ -803,7 +803,7 @@ static bool test_lease_nobreakself(struct torture_context *tctx,
 	struct smb2_lease ls;
 	struct smb2_handle h1, h2;
 	NTSTATUS status;
-	const char *fname = "lease.dat";
+	const char *fname = "lease_nobreakself.dat";
 	bool ret = true;
 	uint32_t caps;
 	char c = 0;
@@ -958,7 +958,7 @@ static bool test_lease_oplock(struct torture_context *tctx,
 	struct smb2_lease ls;
 	struct smb2_handle h, h2;
 	NTSTATUS status;
-	const char *fname = "lease.dat";
+	const char *fname = "lease_oplock.dat";
 	bool ret = true;
 	int i;
 	uint32_t caps;
@@ -1079,7 +1079,7 @@ static bool test_lease_multibreak(struct torture_context *tctx,
 	struct smb2_handle h, h2, h3;
 	struct smb2_write w;
 	NTSTATUS status;
-	const char *fname = "lease.dat";
+	const char *fname = "lease_multibreak.dat";
 	bool ret = true;
 	uint32_t caps;
 
@@ -1198,7 +1198,7 @@ static bool test_lease_v2_request_parent(struct torture_context *tctx,
 	struct smb2_handle h1;
 	uint64_t parent = LEASE2;
 	NTSTATUS status;
-	const char *fname = "lease.dat";
+	const char *fname = "lease_v2_request_parent.dat";
 	bool ret = true;
 	uint32_t caps;
 	enum protocol_types protocol;
@@ -1251,7 +1251,7 @@ static bool test_lease_break_twice(struct torture_context *tctx,
 	struct smb2_lease ls;
 	struct smb2_handle h1;
 	NTSTATUS status;
-	const char *fname = "lease.dat";
+	const char *fname = "lease_break_twice.dat";
 	bool ret = true;
 	uint32_t caps;
 	enum protocol_types protocol;
@@ -1322,10 +1322,10 @@ static bool test_lease_v2_request(struct torture_context *tctx,
 	struct smb2_handle h1, h2, h3, h4, h5;
 	struct smb2_write w;
 	NTSTATUS status;
-	const char *fname = "lease.dat";
-	const char *dname = "lease.dir";
-	const char *dnamefname = "lease.dir\\lease.dat";
-	const char *dnamefname2 = "lease.dir\\lease2.dat";
+	const char *fname = "lease_v2_request.dat";
+	const char *dname = "lease_v2_request.dir";
+	const char *dnamefname = "lease_v2_request.dir\\lease.dat";
+	const char *dnamefname2 = "lease_v2_request.dir\\lease2.dat";
 	bool ret = true;
 	uint32_t caps;
 	enum protocol_types protocol;
@@ -1467,7 +1467,7 @@ static bool test_lease_v2_epoch1(struct torture_context *tctx,
 	struct smb2_create io;
 	struct smb2_lease ls;
 	struct smb2_handle h;
-	const char *fname = "lease.dat";
+	const char *fname = "lease_v2_epoch1.dat";
 	bool ret = true;
 	NTSTATUS status;
 	uint32_t caps;
-- 
1.9.1


From bf1461f839196e0696676d5e187856be056a785c Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Wed, 12 Nov 2014 17:00:44 +0100
Subject: [PATCH 29/46] s4:torture/smb2: verify lease_flags in
 CHECK_LEASE_BREAK()

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

diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c
index ef88473..999c9d4 100644
--- a/source4/torture/smb2/lease.c
+++ b/source4/torture/smb2/lease.c
@@ -377,10 +377,18 @@ static bool test_lease_upgrade2(struct torture_context *tctx,
 
 #define CHECK_LEASE_BREAK(__lb, __oldstate, __state, __key)		\
 	do {								\
-		CHECK_VAL((__lb)->new_lease_state, smb2_util_lease_state(__state));	\
-		CHECK_VAL((__lb)->current_lease.lease_state, smb2_util_lease_state(__oldstate)); \
+		uint16_t __new = smb2_util_lease_state(__state); \
+		uint16_t __old = smb2_util_lease_state(__oldstate); \
+		CHECK_VAL((__lb)->new_lease_state, __new);	\
+		CHECK_VAL((__lb)->current_lease.lease_state, __old); \
 		CHECK_VAL((__lb)->current_lease.lease_key.data[0], (__key)); \
 		CHECK_VAL((__lb)->current_lease.lease_key.data[1], ~(__key)); \
+		if (__old & (SMB2_LEASE_WRITE | SMB2_LEASE_HANDLE)) { \
+			CHECK_VAL((__lb)->break_flags, \
+				  SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED);	\
+		} else { \
+			CHECK_VAL((__lb)->break_flags, 0); \
+		} \
 	} while(0)
 
 #define CHECK_LEASE_BREAK_ACK(__lba, __state, __key)			\
-- 
1.9.1


From 4ace5a7f23eabb71b9b67fe028217db1ca2fd6fd Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Tue, 11 Nov 2014 19:35:07 +0100
Subject: [PATCH 30/46] s4:torture/smb2: always verify the v2 lease epoch.

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

diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c
index 999c9d4..d10dcc5 100644
--- a/source4/torture/smb2/lease.c
+++ b/source4/torture/smb2/lease.c
@@ -53,6 +53,7 @@
 
 #define CHECK_LEASE(__io, __state, __oplevel, __key)			\
 	do {								\
+		CHECK_VAL((__io)->out.lease_response.lease_version, 1); \
 		if (__oplevel) {					\
 			CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); \
 			CHECK_VAL((__io)->out.lease_response.lease_key.data[0], (__key)); \
@@ -67,10 +68,12 @@
 									\
 		CHECK_VAL((__io)->out.lease_response.lease_flags, 0);	\
 		CHECK_VAL((__io)->out.lease_response.lease_duration, 0); \
+		CHECK_VAL((__io)->out.lease_response.lease_epoch, 0); \
 	} while(0)
 
-#define CHECK_LEASE_V2(__io, __state, __oplevel, __key, __flags, __parent) \
+#define CHECK_LEASE_V2(__io, __state, __oplevel, __key, __flags, __parent, __epoch) \
 	do {								\
+		CHECK_VAL((__io)->out.lease_response_v2.lease_version, 2); \
 		if (__oplevel) {					\
 			CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); \
 			CHECK_VAL((__io)->out.lease_response_v2.lease_key.data[0], (__key)); \
@@ -89,6 +92,7 @@
 			CHECK_VAL((__io)->out.lease_response_v2.parent_lease_key.data[1], ~(__parent)); \
 		} \
 		CHECK_VAL((__io)->out.lease_response_v2.lease_duration, 0); \
+		CHECK_VAL((__io)->out.lease_response_v2.lease_epoch, (__epoch)); \
 	} while(0)
 
 static const uint64_t LEASE1 = 0xBADC0FFEE0DDF00Dull;
@@ -147,7 +151,7 @@ static bool test_lease_request(struct torture_context *tctx,
 		status = smb2_create(tree, mem_ctx, &io);
 		CHECK_STATUS(status, NT_STATUS_OK);
 		CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_DIRECTORY);
-		CHECK_LEASE(&io, "", false, 0);
+		CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE);
 		smb2_util_close(tree, io.out.file.handle);
 	}
 
@@ -403,6 +407,7 @@ static bool test_lease_upgrade2(struct torture_context *tctx,
 
 static struct {
 	struct smb2_lease_break lease_break;
+	struct smb2_transport *lease_transport;
 	struct smb2_lease_break_ack lease_break_ack;
 	int count;
 	int failures;
@@ -433,7 +438,7 @@ static struct {
 		break_info.held_oplock_level = break_info.oplock_level; \
 	} while(0)
 
-#define CHECK_BREAK_INFO(__oldstate, __state, __key)			\
+#define _CHECK_BREAK_INFO(__oldstate, __state, __key)			\
 	do {								\
 		torture_wait_for_lease_break(tctx);			\
 		CHECK_VAL(break_info.failures, 0);			\
@@ -448,6 +453,20 @@ static struct {
 		}							\
 	} while(0)
 
+#define CHECK_BREAK_INFO(__oldstate, __state, __key)			\
+	do {								\
+		_CHECK_BREAK_INFO(__oldstate, __state, __key);		\
+		CHECK_VAL(break_info.lease_break.new_epoch, 0);		\
+	} while(0)
+
+#define CHECK_BREAK_INFO_V2(__transport, __oldstate, __state, __key, __epoch) \
+	do {								\
+		_CHECK_BREAK_INFO(__oldstate, __state, __key);		\
+		CHECK_VAL(break_info.lease_break.new_epoch, __epoch);	\
+		CHECK_VAL((uintptr_t)break_info.lease_transport, \
+			  (uintptr_t)__transport);	\
+	} while(0)
+
 static void torture_lease_break_callback(struct smb2_request *req)
 {
 	NTSTATUS status;
@@ -468,6 +487,7 @@ static bool torture_lease_handler(struct smb2_transport *transport,
 	struct smb2_lease_break_ack io;
 	struct smb2_request *req;
 
+	break_info.lease_transport = transport;
 	break_info.lease_break = *lb;
 	break_info.count++;
 
@@ -765,8 +785,7 @@ static bool test_lease_break(struct torture_context *tctx,
 		if (smb2_util_lease_state(held) != smb2_util_lease_state(brokento)) {
 			CHECK_BREAK_INFO(held, brokento, LEASE1);
 		} else {
-			CHECK_VAL(break_info.count, 0);
-			CHECK_VAL(break_info.failures, 0);
+			CHECK_NO_BREAK(tctx);
 		}
 
 		ZERO_STRUCT(break_info);
@@ -1233,14 +1252,15 @@ static bool test_lease_v2_request_parent(struct torture_context *tctx,
 				   smb2_util_share_access("RWD"),
 				   LEASE1, &parent,
 				   smb2_util_lease_state("RHW"),
-				   0);
+				   0x11);
 
 	status = smb2_create(tree, mem_ctx, &io);
 	CHECK_STATUS(status, NT_STATUS_OK);
 	h1 = io.out.file.handle;
 	CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
 	CHECK_LEASE_V2(&io, "RHW", true, LEASE1,
-		       SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET, LEASE2);
+		       SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET, LEASE2,
+		       ls.lease_epoch + 1);
 
  done:
 	smb2_util_close(tree, h1);
@@ -1256,7 +1276,8 @@ static bool test_lease_break_twice(struct torture_context *tctx,
 {
 	TALLOC_CTX *mem_ctx = talloc_new(tctx);
 	struct smb2_create io;
-	struct smb2_lease ls;
+	struct smb2_lease ls1;
+	struct smb2_lease ls2;
 	struct smb2_handle h1;
 	NTSTATUS status;
 	const char *fname = "lease_break_twice.dat";
@@ -1279,17 +1300,16 @@ static bool test_lease_break_twice(struct torture_context *tctx,
 
 	ZERO_STRUCT(break_info);
 	ZERO_STRUCT(io);
-	ZERO_STRUCT(ls);
 
 	smb2_lease_v2_create_share(
-		&io, &ls, false, fname, smb2_util_share_access("RWD"),
-		LEASE1, NULL, smb2_util_lease_state("RWH"), 0);
+		&io, &ls1, false, fname, smb2_util_share_access("RWD"),
+		LEASE1, NULL, smb2_util_lease_state("RWH"), 0x11);
 
 	status = smb2_create(tree, mem_ctx, &io);
 	CHECK_STATUS(status, NT_STATUS_OK);
 	h1 = io.out.file.handle;
 	CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
-	CHECK_LEASE_V2(&io, "RHW", true, LEASE1, 0, 0);
+	CHECK_LEASE_V2(&io, "RHW", true, LEASE1, 0, 0, ls1.lease_epoch + 1);
 
 	tree->session->transport->lease.handler = torture_lease_handler;
 	tree->session->transport->lease.private_data = tree;
@@ -1297,22 +1317,25 @@ static bool test_lease_break_twice(struct torture_context *tctx,
 	ZERO_STRUCT(break_info);
 
 	smb2_lease_v2_create_share(
-		&io, &ls, false, fname, smb2_util_share_access("R"),
-		LEASE2, NULL, smb2_util_lease_state("RWH"), 0);
+		&io, &ls2, false, fname, smb2_util_share_access("R"),
+		LEASE2, NULL, smb2_util_lease_state("RWH"), 0x22);
 
 	status = smb2_create(tree, mem_ctx, &io);
 	CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
-	CHECK_BREAK_INFO("RWH", "RW", LEASE1);
+	CHECK_BREAK_INFO_V2(tree->session->transport,
+			    "RWH", "RW", LEASE1, ls1.lease_epoch + 2);
 
 	smb2_lease_v2_create_share(
-		&io, &ls, false, fname, smb2_util_share_access("RWD"),
-		LEASE2, NULL, smb2_util_lease_state("RWH"), 0);
+		&io, &ls2, false, fname, smb2_util_share_access("RWD"),
+		LEASE2, NULL, smb2_util_lease_state("RWH"), 0x22);
 
 	ZERO_STRUCT(break_info);
 
 	status = smb2_create(tree, mem_ctx, &io);
 	CHECK_STATUS(status, NT_STATUS_OK);
-	CHECK_BREAK_INFO("RW", "R", LEASE1);
+	CHECK_LEASE_V2(&io, "RH", true, LEASE2, 0, 0, ls2.lease_epoch + 1);
+	CHECK_BREAK_INFO_V2(tree->session->transport,
+			    "RW", "R", LEASE1, ls1.lease_epoch + 3);
 
 done:
 	smb2_util_close(tree, h1);
@@ -1326,7 +1349,7 @@ static bool test_lease_v2_request(struct torture_context *tctx,
 {
 	TALLOC_CTX *mem_ctx = talloc_new(tctx);
 	struct smb2_create io;
-	struct smb2_lease ls;
+	struct smb2_lease ls1, ls2, ls2t, ls3, ls4;
 	struct smb2_handle h1, h2, h3, h4, h5;
 	struct smb2_write w;
 	NTSTATUS status;
@@ -1362,73 +1385,75 @@ static bool test_lease_v2_request(struct torture_context *tctx,
 	ZERO_STRUCT(break_info);
 
 	ZERO_STRUCT(io);
-	smb2_lease_v2_create_share(&io, &ls, false, fname,
+	smb2_lease_v2_create_share(&io, &ls1, false, fname,
 				   smb2_util_share_access("RWD"),
 				   LEASE1, NULL,
 				   smb2_util_lease_state("RHW"),
-				   0);
+				   0x11);
 
 	status = smb2_create(tree, mem_ctx, &io);
 	CHECK_STATUS(status, NT_STATUS_OK);
 	h1 = io.out.file.handle;
 	CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
-	CHECK_LEASE_V2(&io, "RHW", true, LEASE1, 0, 0);
+	CHECK_LEASE_V2(&io, "RHW", true, LEASE1, 0, 0, ls1.lease_epoch + 1);
 
 	ZERO_STRUCT(io);
-	smb2_lease_v2_create_share(&io, &ls, true, dname,
+	smb2_lease_v2_create_share(&io, &ls2, true, dname,
 				   smb2_util_share_access("RWD"),
 				   LEASE2, NULL,
 				   smb2_util_lease_state("RHW"),
-				   0);
+				   0x22);
 	status = smb2_create(tree, mem_ctx, &io);
 	CHECK_STATUS(status, NT_STATUS_OK);
 	h2 = io.out.file.handle;
 	CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_DIRECTORY);
-	CHECK_LEASE_V2(&io, "RH", true, LEASE2, 0, 0);
+	CHECK_LEASE_V2(&io, "RH", true, LEASE2, 0, 0, ls2.lease_epoch + 1);
 
 	ZERO_STRUCT(io);
-	smb2_lease_v2_create_share(&io, &ls, false, dnamefname,
+	smb2_lease_v2_create_share(&io, &ls3, false, dnamefname,
 				   smb2_util_share_access("RWD"),
 				   LEASE3, &LEASE2,
 				   smb2_util_lease_state("RHW"),
-				   0);
+				   0x33);
 	status = smb2_create(tree, mem_ctx, &io);
 	CHECK_STATUS(status, NT_STATUS_OK);
 	h3 = io.out.file.handle;
 	CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
 	CHECK_LEASE_V2(&io, "RHW", true, LEASE3,
-		       SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET, LEASE2);
+		       SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET, LEASE2,
+		       ls3.lease_epoch + 1);
 
 	CHECK_NO_BREAK(tctx);
 
 	ZERO_STRUCT(io);
-	smb2_lease_v2_create_share(&io, &ls, false, dnamefname2,
+	smb2_lease_v2_create_share(&io, &ls4, false, dnamefname2,
 				   smb2_util_share_access("RWD"),
 				   LEASE4, NULL,
 				   smb2_util_lease_state("RHW"),
-				   0);
+				   0x44);
 	status = smb2_create(tree, mem_ctx, &io);
 	CHECK_STATUS(status, NT_STATUS_OK);
 	h4 = io.out.file.handle;
 	CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
-	CHECK_LEASE_V2(&io, "RHW", true, LEASE4, 0, 0);
+	CHECK_LEASE_V2(&io, "RHW", true, LEASE4, 0, 0, ls4.lease_epoch + 1);
 
-	CHECK_BREAK_INFO("RH", "", LEASE2);
+	CHECK_BREAK_INFO_V2(tree->session->transport,
+			    "RH", "", LEASE2, ls2.lease_epoch + 2);
 
 	ZERO_STRUCT(break_info);
 
 	ZERO_STRUCT(io);
-	smb2_lease_v2_create_share(&io, &ls, true, dname,
+	smb2_lease_v2_create_share(&io, &ls2t, true, dname,
 				   smb2_util_share_access("RWD"),
 				   LEASE2, NULL,
 				   smb2_util_lease_state("RHW"),
-				   0);
+				   0x222);
 	io.in.create_disposition = NTCREATEX_DISP_OPEN;
 	status = smb2_create(tree, mem_ctx, &io);
 	CHECK_STATUS(status, NT_STATUS_OK);
 	h5 = io.out.file.handle;
 	CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_DIRECTORY);
-	CHECK_LEASE_V2(&io, "RH", true, LEASE2, 0, 0);
+	CHECK_LEASE_V2(&io, "RH", true, LEASE2, 0, 0, ls2.lease_epoch+3);
 	smb2_util_close(tree, h5);
 
 	ZERO_STRUCT(w);
@@ -1451,7 +1476,9 @@ static bool test_lease_v2_request(struct torture_context *tctx,
 	 * directory lease.
 	 */
 	smb2_util_close(tree, h4);
-	CHECK_BREAK_INFO("RH", "", LEASE2);
+
+	CHECK_BREAK_INFO_V2(tree->session->transport,
+			    "RH", "", LEASE2, ls2.lease_epoch+4);
 
  done:
 	smb2_util_close(tree, h1);
@@ -1505,26 +1532,26 @@ static bool test_lease_v2_epoch1(struct torture_context *tctx,
 				   smb2_util_share_access("RWD"),
 				   LEASE1, NULL,
 				   smb2_util_lease_state("RHW"),
-				   0);
-	ls.lease_epoch = 0x4711;
-
+				   0x4711);
 	status = smb2_create(tree, mem_ctx, &io);
 	CHECK_STATUS(status, NT_STATUS_OK);
 	h = io.out.file.handle;
 	CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
-	CHECK_LEASE_V2(&io, "RHW", true, LEASE1, 0, 0);
-	CHECK_VAL(io.out.lease_response_v2.lease_epoch, ls.lease_epoch + 1);
+	CHECK_LEASE_V2(&io, "RHW", true, LEASE1, 0, 0, ls.lease_epoch + 1);
 	smb2_util_close(tree, h);
 	smb2_util_unlink(tree, fname);
 
-	ls.lease_state = smb2_util_lease_state("RH");
+	smb2_lease_v2_create_share(&io, &ls, false, fname,
+				   smb2_util_share_access("RWD"),
+				   LEASE1, NULL,
+				   smb2_util_lease_state("RHW"),
+				   0x11);
 
 	status = smb2_create(tree, mem_ctx, &io);
 	CHECK_STATUS(status, NT_STATUS_OK);
 	h = io.out.file.handle;
 	CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
-	CHECK_LEASE_V2(&io, "RH", true, LEASE1, 0, 0);
-	CHECK_VAL(io.out.lease_response_v2.lease_epoch, ls.lease_epoch + 1);
+	CHECK_LEASE_V2(&io, "RWH", true, LEASE1, 0, 0, ls.lease_epoch + 1);
 	smb2_util_close(tree, h);
 
 done:
-- 
1.9.1


From 9e5e1189934dfa4b21bd4a59093e86a1293dda83 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Wed, 12 Nov 2014 09:15:58 +0100
Subject: [PATCH 31/46] s4:torture/smb2: don't check the lease break connection
 against samba3

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

diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c
index d10dcc5..2c5cbd0 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 { \
@@ -463,8 +464,10 @@ static struct {
 	do {								\
 		_CHECK_BREAK_INFO(__oldstate, __state, __key);		\
 		CHECK_VAL(break_info.lease_break.new_epoch, __epoch);	\
-		CHECK_VAL((uintptr_t)break_info.lease_transport, \
-			  (uintptr_t)__transport);	\
+		if (!TARGET_IS_SAMBA3(tctx)) {				\
+			CHECK_VAL((uintptr_t)break_info.lease_transport, \
+				  (uintptr_t)__transport);		\
+		} \
 	} while(0)
 
 static void torture_lease_break_callback(struct smb2_request *req)
-- 
1.9.1


From 41aa844a7fe4d0fdd521ceb4329221dfa12059e5 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Wed, 12 Nov 2014 15:41:50 +0100
Subject: [PATCH 32/46] s4:torture/smb2: pass the expected flags to
 CHECK_LEASE()

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

diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c
index 2c5cbd0..d81ddd3 100644
--- a/source4/torture/smb2/lease.c
+++ b/source4/torture/smb2/lease.c
@@ -52,7 +52,7 @@
 		CHECK_VAL((__io)->out.reserved2, 0);			\
 	} while(0)
 
-#define CHECK_LEASE(__io, __state, __oplevel, __key)			\
+#define CHECK_LEASE(__io, __state, __oplevel, __key, __flags)		\
 	do {								\
 		CHECK_VAL((__io)->out.lease_response.lease_version, 1); \
 		if (__oplevel) {					\
@@ -67,7 +67,7 @@
 			CHECK_VAL((__io)->out.lease_response.lease_state, 0); \
 		}							\
 									\
-		CHECK_VAL((__io)->out.lease_response.lease_flags, 0);	\
+		CHECK_VAL((__io)->out.lease_response.lease_flags, (__flags));	\
 		CHECK_VAL((__io)->out.lease_response.lease_duration, 0); \
 		CHECK_VAL((__io)->out.lease_response.lease_epoch, 0); \
 	} while(0)
@@ -144,7 +144,7 @@ static bool test_lease_request(struct torture_context *tctx,
 	CHECK_STATUS(status, NT_STATUS_OK);
 	h1 = io.out.file.handle;
 	CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
-	CHECK_LEASE(&io, "RHW", true, LEASE1);
+	CHECK_LEASE(&io, "RHW", true, LEASE1, 0);
 
 	/* But will reject leases on directories. */
 	if (!(caps & SMB2_CAP_DIRECTORY_LEASING)) {
@@ -167,7 +167,7 @@ static bool test_lease_request(struct torture_context *tctx,
 	h2 = io.out.file.handle;
 	CHECK_STATUS(status, NT_STATUS_OK);
 	CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
-	CHECK_LEASE(&io, "RHW", true, LEASE2);
+	CHECK_LEASE(&io, "RHW", true, LEASE2, 0);
 	smb2_util_close(tree, h2);
 
 	smb2_util_close(tree, h1);
@@ -184,7 +184,7 @@ static bool test_lease_request(struct torture_context *tctx,
 		h2 = io.out.file.handle;
 		CHECK_STATUS(status, NT_STATUS_OK);
 		CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
-		CHECK_LEASE(&io, request_results[i][1], true, LEASE1);
+		CHECK_LEASE(&io, request_results[i][1], true, LEASE1, 0);
 		smb2_util_close(tree, io.out.file.handle);
 	}
 
@@ -225,7 +225,7 @@ static bool test_lease_upgrade(struct torture_context *tctx,
 	status = smb2_create(tree, mem_ctx, &io);
 	CHECK_STATUS(status, NT_STATUS_OK);
 	CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
-	CHECK_LEASE(&io, "RH", true, LEASE1);
+	CHECK_LEASE(&io, "RH", true, LEASE1, 0);
 	h = io.out.file.handle;
 
 	/* Upgrades (sidegrades?) to RW leave us with an RH. */
@@ -233,7 +233,7 @@ static bool test_lease_upgrade(struct torture_context *tctx,
 	status = smb2_create(tree, mem_ctx, &io);
 	CHECK_STATUS(status, NT_STATUS_OK);
 	CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
-	CHECK_LEASE(&io, "RH", true, LEASE1);
+	CHECK_LEASE(&io, "RH", true, LEASE1, 0);
 	hnew = io.out.file.handle;
 
 	smb2_util_close(tree, hnew);
@@ -243,7 +243,7 @@ static bool test_lease_upgrade(struct torture_context *tctx,
 	status = smb2_create(tree, mem_ctx, &io);
 	CHECK_STATUS(status, NT_STATUS_OK);
 	CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
-	CHECK_LEASE(&io, "RHW", true, LEASE1);
+	CHECK_LEASE(&io, "RHW", true, LEASE1, 0);
 	hnew = io.out.file.handle;
 
 	smb2_util_close(tree, h);
@@ -254,7 +254,7 @@ static bool test_lease_upgrade(struct torture_context *tctx,
 	status = smb2_create(tree, mem_ctx, &io);
 	CHECK_STATUS(status, NT_STATUS_OK);
 	CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
-	CHECK_LEASE(&io, "RHW", true, LEASE1);
+	CHECK_LEASE(&io, "RHW", true, LEASE1, 0);
 	hnew = io.out.file.handle;
 
 	smb2_util_close(tree, hnew);
@@ -353,7 +353,7 @@ static bool test_lease_upgrade2(struct torture_context *tctx,
 		status = smb2_create(tree, mem_ctx, &io);
 		CHECK_STATUS(status, NT_STATUS_OK);
 		CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
-		CHECK_LEASE(&io, t.initial, true, LEASE1);
+		CHECK_LEASE(&io, t.initial, true, LEASE1, 0);
 		h = io.out.file.handle;
 
 		/* Upgrade. */
@@ -361,7 +361,7 @@ static bool test_lease_upgrade2(struct torture_context *tctx,
 		status = smb2_create(tree, mem_ctx, &io);
 		CHECK_STATUS(status, NT_STATUS_OK);
 		CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
-		CHECK_LEASE(&io, t.expected, true, LEASE1);
+		CHECK_LEASE(&io, t.expected, true, LEASE1, 0);
 		hnew = io.out.file.handle;
 
 		smb2_util_close(tree, hnew);
@@ -603,7 +603,7 @@ static bool test_lease_upgrade3(struct torture_context *tctx,
 		status = smb2_create(tree, mem_ctx, &io);
 		CHECK_STATUS(status, NT_STATUS_OK);
 		CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
-		CHECK_LEASE(&io, t.held1, true, LEASE1);
+		CHECK_LEASE(&io, t.held1, true, LEASE1, 0);
 		h = io.out.file.handle;
 
 		/* grab second lease */
@@ -611,7 +611,7 @@ static bool test_lease_upgrade3(struct torture_context *tctx,
 		status = smb2_create(tree, mem_ctx, &io);
 		CHECK_STATUS(status, NT_STATUS_OK);
 		CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
-		CHECK_LEASE(&io, t.held2, true, LEASE2);
+		CHECK_LEASE(&io, t.held2, true, LEASE2, 0);
 		h2 = io.out.file.handle;
 
 		/* no break has happened */
@@ -623,7 +623,7 @@ static bool test_lease_upgrade3(struct torture_context *tctx,
 		status = smb2_create(tree, mem_ctx, &io);
 		CHECK_STATUS(status, NT_STATUS_OK);
 		CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
-		CHECK_LEASE(&io, t.upgraded_to, true, LEASE1);
+		CHECK_LEASE(&io, t.upgraded_to, true, LEASE1, 0);
 		hnew = io.out.file.handle;
 
 		/* no break has happened */
@@ -775,7 +775,7 @@ static bool test_lease_break(struct torture_context *tctx,
 		CHECK_STATUS(status, NT_STATUS_OK);
 		h = io.out.file.handle;
 		CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
-		CHECK_LEASE(&io, held, true, LEASE1);
+		CHECK_LEASE(&io, held, true, LEASE1, 0);
 
 		/* Possibly contend lease. */
 		smb2_lease_create(&io, &ls, false, fname, LEASE2, smb2_util_lease_state(contend));
@@ -783,7 +783,7 @@ static bool test_lease_break(struct torture_context *tctx,
 		CHECK_STATUS(status, NT_STATUS_OK);
 		h2 = io.out.file.handle;
 		CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
-		CHECK_LEASE(&io, granted, true, LEASE2);
+		CHECK_LEASE(&io, granted, true, LEASE2, 0);
 
 		if (smb2_util_lease_state(held) != smb2_util_lease_state(brokento)) {
 			CHECK_BREAK_INFO(held, brokento, LEASE1);
@@ -802,7 +802,7 @@ static bool test_lease_break(struct torture_context *tctx,
 		CHECK_STATUS(status, NT_STATUS_OK);
 		h3 = io.out.file.handle;
 		CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
-		CHECK_LEASE(&io, brokento, true, LEASE1);
+		CHECK_LEASE(&io, brokento, true, LEASE1, 0);
 		CHECK_VAL(break_info.count, 0);
 		CHECK_VAL(break_info.failures, 0);
 
@@ -853,14 +853,14 @@ static bool test_lease_nobreakself(struct torture_context *tctx,
 	CHECK_STATUS(status, NT_STATUS_OK);
 	h1 = io.out.file.handle;
 	CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
-	CHECK_LEASE(&io, "R", true, LEASE1);
+	CHECK_LEASE(&io, "R", true, LEASE1, 0);
 
 	smb2_lease_create(&io, &ls, false, fname, LEASE2,
 			  smb2_util_lease_state("R"));
 	status = smb2_create(tree, mem_ctx, &io);
 	CHECK_STATUS(status, NT_STATUS_OK);
 	h2 = io.out.file.handle;
-	CHECK_LEASE(&io, "R", true, LEASE2);
+	CHECK_LEASE(&io, "R", true, LEASE2, 0);
 
 	ZERO_STRUCT(break_info);
 
@@ -879,7 +879,7 @@ static bool test_lease_nobreakself(struct torture_context *tctx,
 			  smb2_util_lease_state("R"));
 	status = smb2_create(tree, mem_ctx, &io);
 	CHECK_STATUS(status, NT_STATUS_OK);
-	CHECK_LEASE(&io, "R", true, LEASE2);
+	CHECK_LEASE(&io, "R", true, LEASE2, 0);
 	smb2_util_close(tree, io.out.file.handle);
 
 	/* Now break LEASE1 via h2 */
@@ -1023,7 +1023,7 @@ static bool test_lease_oplock(struct torture_context *tctx,
 		CHECK_STATUS(status, NT_STATUS_OK);
 		h = io.out.file.handle;
 		CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
-		CHECK_LEASE(&io, held, true, LEASE1);
+		CHECK_LEASE(&io, held, true, LEASE1, 0);
 
 		/* Does an oplock contend the lease? */
 		smb2_oplock_create(&io, fname, smb2_util_oplock_level(contend));
@@ -1074,7 +1074,7 @@ static bool test_lease_oplock(struct torture_context *tctx,
 		CHECK_STATUS(status, NT_STATUS_OK);
 		h2 = io.out.file.handle;
 		CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
-		CHECK_LEASE(&io, granted, true, LEASE1);
+		CHECK_LEASE(&io, granted, true, LEASE1, 0);
 
 		if (smb2_util_oplock_level(held) != smb2_util_oplock_level(brokento)) {
 			CHECK_OPLOCK_BREAK(brokento);
@@ -1133,14 +1133,14 @@ static bool test_lease_multibreak(struct torture_context *tctx,
 	CHECK_STATUS(status, NT_STATUS_OK);
 	h = io.out.file.handle;
 	CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
-	CHECK_LEASE(&io, "RH", true, LEASE1);
+	CHECK_LEASE(&io, "RH", true, LEASE1, 0);
 
 	smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RHW"));
 	status = smb2_create(tree, mem_ctx, &io);
 	CHECK_STATUS(status, NT_STATUS_OK);
 	h2 = io.out.file.handle;
 	CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
-	CHECK_LEASE(&io, "RHW", true, LEASE1);
+	CHECK_LEASE(&io, "RHW", true, LEASE1, 0);
 
 	/* Contend with LEASE2. */
 	smb2_lease_create(&io, &ls, false, fname, LEASE2, smb2_util_lease_state("RHW"));
@@ -1148,7 +1148,7 @@ static bool test_lease_multibreak(struct torture_context *tctx,
 	CHECK_STATUS(status, NT_STATUS_OK);
 	h3 = io.out.file.handle;
 	CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
-	CHECK_LEASE(&io, "RH", true, LEASE2);
+	CHECK_LEASE(&io, "RH", true, LEASE2, 0);
 
 	/* Verify that we were only sent one break. */
 	CHECK_BREAK_INFO("RHW", "RH", LEASE1);
@@ -1169,7 +1169,7 @@ static bool test_lease_multibreak(struct torture_context *tctx,
 	CHECK_STATUS(status, NT_STATUS_OK);
 	h = io.out.file.handle;
 	CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
-	CHECK_LEASE(&io, "R", true, LEASE1);
+	CHECK_LEASE(&io, "R", true, LEASE1, 0);
 
 	/* Grab a level-II oplock. */
 	smb2_oplock_create(&io, fname, smb2_util_oplock_level("s"));
-- 
1.9.1


From ab892dff9681e4b5661505d9db2c4aacce75a1fb Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Tue, 11 Nov 2014 19:35:59 +0100
Subject: [PATCH 33/46] s4:torture/smb2: add smb2.lease.[v2_]complex1 tests

These tests verify the lease state is consistent between two connections
with the same client_guid.

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

diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c
index d81ddd3..ac20946 100644
--- a/source4/torture/smb2/lease.c
+++ b/source4/torture/smb2/lease.c
@@ -1563,6 +1563,277 @@ done:
 	return ret;
 }
 
+static bool test_lease_complex1(struct torture_context *tctx,
+				struct smb2_tree *tree1a)
+{
+	TALLOC_CTX *mem_ctx = talloc_new(tctx);
+	struct smb2_create io1;
+	struct smb2_create io2;
+	struct smb2_lease ls1;
+	struct smb2_lease ls2;
+	struct smb2_handle h, h2, h3;
+	struct smb2_write w;
+	NTSTATUS status;
+	const char *fname = "lease_complex1.dat";
+	bool ret = true;
+	uint32_t caps;
+	struct smb2_tree *tree1b = NULL;
+	struct smbcli_options options1;
+
+	options1 = tree1a->session->transport->options;
+
+	caps = smb2cli_conn_server_capabilities(tree1a->session->transport->conn);
+	if (!(caps & SMB2_CAP_LEASING)) {
+		torture_skip(tctx, "leases are not supported");
+	}
+
+	tree1a->session->transport->lease.handler = torture_lease_handler;
+	tree1a->session->transport->lease.private_data = tree1a;
+	tree1a->session->transport->oplock.handler = torture_oplock_handler;
+	tree1a->session->transport->oplock.private_data = tree1a;
+
+	/* create a new connection (same client_guid) */
+	if (!torture_smb2_connection_ext(tctx, 0, &options1, &tree1b)) {
+		torture_warning(tctx, "couldn't reconnect, bailing\n");
+		ret = false;
+		goto done;
+	}
+
+	tree1b->session->transport->lease.handler = torture_lease_handler;
+	tree1b->session->transport->lease.private_data = tree1b;
+	tree1b->session->transport->oplock.handler = torture_oplock_handler;
+	tree1b->session->transport->oplock.private_data = tree1b;
+
+	smb2_util_unlink(tree1a, fname);
+
+	ZERO_STRUCT(break_info);
+
+	/* Grab R lease over connection 1a */
+	smb2_lease_create(&io1, &ls1, false, fname, LEASE1, smb2_util_lease_state("R"));
+	status = smb2_create(tree1a, mem_ctx, &io1);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h = io1.out.file.handle;
+	CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE(&io1, "R", true, LEASE1, 0);
+
+	/* Upgrade to RWH over connection 1b */
+	ls1.lease_state = smb2_util_lease_state("RWH");
+	status = smb2_create(tree1b, mem_ctx, &io1);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h2 = io1.out.file.handle;
+	CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE(&io1, "RHW", true, LEASE1, 0);
+
+	/* close over connection 1b */
+	status = smb2_util_close(tree1b, h2);
+	CHECK_STATUS(status, NT_STATUS_OK);
+
+	/* Contend with LEASE2. */
+	smb2_lease_create(&io2, &ls2, false, fname, LEASE2, smb2_util_lease_state("RHW"));
+	status = smb2_create(tree1b, mem_ctx, &io2);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h3 = io2.out.file.handle;
+	CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE(&io2, "RH", true, LEASE2, 0);
+
+	/* Verify that we were only sent one break. */
+	CHECK_BREAK_INFO("RHW", "RH", LEASE1);
+
+	/* again RH over connection 1b doesn't change the epoch */
+	ls1.lease_state = smb2_util_lease_state("RH");
+	status = smb2_create(tree1b, mem_ctx, &io1);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h2 = io1.out.file.handle;
+	CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE(&io1, "RH", true, LEASE1, 0);
+
+	/* close over connection 1b */
+	status = smb2_util_close(tree1b, h2);
+	CHECK_STATUS(status, NT_STATUS_OK);
+
+	ZERO_STRUCT(break_info);
+
+	ZERO_STRUCT(w);
+	w.in.file.handle = h;
+	w.in.offset      = 0;
+	w.in.data        = data_blob_talloc(mem_ctx, NULL, 4096);
+	memset(w.in.data.data, 'o', w.in.data.length);
+	status = smb2_write(tree1a, &w);
+	CHECK_STATUS(status, NT_STATUS_OK);
+
+	ls2.lease_epoch += 1;
+	CHECK_BREAK_INFO("RH", "", LEASE2);
+
+	ZERO_STRUCT(break_info);
+
+	ZERO_STRUCT(w);
+	w.in.file.handle = h3;
+	w.in.offset      = 0;
+	w.in.data        = data_blob_talloc(mem_ctx, NULL, 4096);
+	memset(w.in.data.data, 'o', w.in.data.length);
+	status = smb2_write(tree1b, &w);
+	CHECK_STATUS(status, NT_STATUS_OK);
+
+	ls1.lease_epoch += 1;
+	CHECK_BREAK_INFO("RH", "", LEASE1);
+
+ done:
+	smb2_util_close(tree1a, h);
+	smb2_util_close(tree1b, h2);
+	smb2_util_close(tree1b, h3);
+
+	smb2_util_unlink(tree1a, fname);
+
+	talloc_free(mem_ctx);
+
+	return ret;
+}
+
+static bool test_lease_v2_complex1(struct torture_context *tctx,
+				   struct smb2_tree *tree1a)
+{
+	TALLOC_CTX *mem_ctx = talloc_new(tctx);
+	struct smb2_create io1;
+	struct smb2_create io2;
+	struct smb2_lease ls1;
+	struct smb2_lease ls2;
+	struct smb2_handle h, h2, h3;
+	struct smb2_write w;
+	NTSTATUS status;
+	const char *fname = "lease_v2_complex1.dat";
+	bool ret = true;
+	uint32_t caps;
+	enum protocol_types protocol;
+	struct smb2_tree *tree1b = NULL;
+	struct smbcli_options options1;
+
+	options1 = tree1a->session->transport->options;
+
+	caps = smb2cli_conn_server_capabilities(tree1a->session->transport->conn);
+	if (!(caps & SMB2_CAP_LEASING)) {
+		torture_skip(tctx, "leases are not supported");
+	}
+
+	protocol = smbXcli_conn_protocol(tree1a->session->transport->conn);
+	if (protocol < PROTOCOL_SMB3_00) {
+		torture_skip(tctx, "v2 leases are not supported");
+	}
+
+	tree1a->session->transport->lease.handler = torture_lease_handler;
+	tree1a->session->transport->lease.private_data = tree1a;
+	tree1a->session->transport->oplock.handler = torture_oplock_handler;
+	tree1a->session->transport->oplock.private_data = tree1a;
+
+	/* create a new connection (same client_guid) */
+	if (!torture_smb2_connection_ext(tctx, 0, &options1, &tree1b)) {
+		torture_warning(tctx, "couldn't reconnect, bailing\n");
+		ret = false;
+		goto done;
+	}
+
+	tree1b->session->transport->lease.handler = torture_lease_handler;
+	tree1b->session->transport->lease.private_data = tree1b;
+	tree1b->session->transport->oplock.handler = torture_oplock_handler;
+	tree1b->session->transport->oplock.private_data = tree1b;
+
+	smb2_util_unlink(tree1a, fname);
+
+	ZERO_STRUCT(break_info);
+
+	/* Grab R lease over connection 1a */
+	smb2_lease_v2_create(&io1, &ls1, false, fname, LEASE1, NULL,
+			     smb2_util_lease_state("R"), 0x4711);
+	status = smb2_create(tree1a, mem_ctx, &io1);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h = io1.out.file.handle;
+	CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
+	ls1.lease_epoch += 1;
+	CHECK_LEASE_V2(&io1, "R", true, LEASE1,
+		       0, 0, ls1.lease_epoch);
+
+	/* Upgrade to RWH over connection 1b */
+	ls1.lease_state = smb2_util_lease_state("RWH");
+	status = smb2_create(tree1b, mem_ctx, &io1);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h2 = io1.out.file.handle;
+	CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	ls1.lease_epoch += 1;
+	CHECK_LEASE_V2(&io1, "RHW", true, LEASE1,
+		       0, 0, ls1.lease_epoch);
+
+	/* close over connection 1b */
+	status = smb2_util_close(tree1b, h2);
+	CHECK_STATUS(status, NT_STATUS_OK);
+
+	/* Contend with LEASE2. */
+	smb2_lease_v2_create(&io2, &ls2, false, fname, LEASE2, NULL,
+			     smb2_util_lease_state("RWH"), 0x11);
+	status = smb2_create(tree1b, mem_ctx, &io2);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h3 = io2.out.file.handle;
+	CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	ls2.lease_epoch += 1;
+	CHECK_LEASE_V2(&io2, "RH", true, LEASE2,
+		       0, 0, ls2.lease_epoch);
+
+	/* Verify that we were only sent one break. */
+	ls1.lease_epoch += 1;
+	CHECK_BREAK_INFO_V2(tree1a->session->transport,
+			    "RHW", "RH", LEASE1, ls1.lease_epoch);
+
+	/* again RH over connection 1b doesn't change the epoch */
+	ls1.lease_state = smb2_util_lease_state("RH");
+	status = smb2_create(tree1b, mem_ctx, &io1);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h2 = io1.out.file.handle;
+	CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE_V2(&io1, "RH", true, LEASE1,
+		       0, 0, ls1.lease_epoch);
+
+	/* close over connection 1b */
+	status = smb2_util_close(tree1b, h2);
+	CHECK_STATUS(status, NT_STATUS_OK);
+
+	ZERO_STRUCT(break_info);
+
+	ZERO_STRUCT(w);
+	w.in.file.handle = h;
+	w.in.offset      = 0;
+	w.in.data        = data_blob_talloc(mem_ctx, NULL, 4096);
+	memset(w.in.data.data, 'o', w.in.data.length);
+	status = smb2_write(tree1a, &w);
+	CHECK_STATUS(status, NT_STATUS_OK);
+
+	ls2.lease_epoch += 1;
+	CHECK_BREAK_INFO_V2(tree1a->session->transport,
+			    "RH", "", LEASE2, ls2.lease_epoch);
+
+	ZERO_STRUCT(break_info);
+
+	ZERO_STRUCT(w);
+	w.in.file.handle = h3;
+	w.in.offset      = 0;
+	w.in.data        = data_blob_talloc(mem_ctx, NULL, 4096);
+	memset(w.in.data.data, 'o', w.in.data.length);
+	status = smb2_write(tree1b, &w);
+	CHECK_STATUS(status, NT_STATUS_OK);
+
+	ls1.lease_epoch += 1;
+	CHECK_BREAK_INFO_V2(tree1a->session->transport,
+			    "RH", "", LEASE1, ls1.lease_epoch);
+
+ done:
+	smb2_util_close(tree1a, h);
+	smb2_util_close(tree1b, h2);
+	smb2_util_close(tree1b, h3);
+
+	smb2_util_unlink(tree1a, fname);
+
+	talloc_free(mem_ctx);
+
+	return ret;
+}
+
 struct torture_suite *torture_smb2_lease_init(void)
 {
 	struct torture_suite *suite =
@@ -1579,10 +1850,12 @@ struct torture_suite *torture_smb2_lease_init(void)
 	torture_suite_add_1smb2_test(suite, "break", test_lease_break);
 	torture_suite_add_1smb2_test(suite, "oplock", test_lease_oplock);
 	torture_suite_add_1smb2_test(suite, "multibreak", test_lease_multibreak);
+	torture_suite_add_1smb2_test(suite, "complex1", test_lease_complex1);
 	torture_suite_add_1smb2_test(suite, "v2_request_parent",
 				     test_lease_v2_request_parent);
 	torture_suite_add_1smb2_test(suite, "v2_request", test_lease_v2_request);
 	torture_suite_add_1smb2_test(suite, "v2_epoch1", test_lease_v2_epoch1);
+	torture_suite_add_1smb2_test(suite, "v2_complex1", test_lease_v2_complex1);
 
 	suite->description = talloc_strdup(suite, "SMB2-LEASE tests");
 
-- 
1.9.1


From 925c4ba8915daaeb379e55e77878c843c1181e32 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Wed, 12 Nov 2014 09:53:45 +0100
Subject: [PATCH 34/46] s4:torture/smb2: add smb2.lease.v2_epoch[2|3] tests

They demonstrate that the lease version (v1 or v2) is selected
by the first open. All following opens using the other version
still get the lease version of the first open.

This implies that the server has to remember the lease version
more globaly.

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

diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c
index ac20946..14ce739 100644
--- a/source4/torture/smb2/lease.c
+++ b/source4/torture/smb2/lease.c
@@ -1563,6 +1563,214 @@ done:
 	return ret;
 }
 
+static bool test_lease_v2_epoch2(struct torture_context *tctx,
+				 struct smb2_tree *tree)
+{
+	TALLOC_CTX *mem_ctx = talloc_new(tctx);
+	struct smb2_create io;
+	struct smb2_lease ls1v2, ls1v2t, ls1v1;
+	struct smb2_handle hv2 = {}, hv1 = {};
+	const char *fname = "lease_v2_epoch2.dat";
+	bool ret = true;
+	NTSTATUS status;
+	uint32_t caps;
+	enum protocol_types protocol;
+
+	caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
+	if (!(caps & SMB2_CAP_LEASING)) {
+		torture_skip(tctx, "leases are not supported");
+	}
+
+	protocol = smbXcli_conn_protocol(tree->session->transport->conn);
+	if (protocol < PROTOCOL_SMB3_00) {
+		torture_skip(tctx, "v2 leases are not supported");
+	}
+
+	smb2_util_unlink(tree, fname);
+
+	tree->session->transport->lease.handler	= torture_lease_handler;
+	tree->session->transport->lease.private_data = tree;
+	tree->session->transport->oplock.handler = torture_oplock_handler;
+	tree->session->transport->oplock.private_data = tree;
+
+	ZERO_STRUCT(break_info);
+
+	ZERO_STRUCT(io);
+	smb2_lease_v2_create_share(&io, &ls1v2, false, fname,
+				   smb2_util_share_access("RWD"),
+				   LEASE1, NULL,
+				   smb2_util_lease_state("R"),
+				   0x4711);
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	hv2 = io.out.file.handle;
+	CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE_V2(&io, "R", true, LEASE1, 0, 0, ls1v2.lease_epoch + 1);
+
+	ZERO_STRUCT(io);
+	smb2_lease_create_share(&io, &ls1v1, false, fname,
+				smb2_util_share_access("RWD"),
+				LEASE1,
+				smb2_util_lease_state("RH"));
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	hv1 = io.out.file.handle;
+	CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE_V2(&io, "RH", true, LEASE1, 0, 0, ls1v2.lease_epoch + 2);
+
+	smb2_util_close(tree, hv2);
+
+	ZERO_STRUCT(io);
+	smb2_lease_v2_create_share(&io, &ls1v2t, false, fname,
+				   smb2_util_share_access("RWD"),
+				   LEASE1, NULL,
+				   smb2_util_lease_state("RHW"),
+				   0x11);
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	hv2 = io.out.file.handle;
+	CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE_V2(&io, "RHW", true, LEASE1, 0, 0, ls1v2.lease_epoch + 3);
+
+	smb2_util_close(tree, hv2);
+
+	smb2_oplock_create(&io, fname, SMB2_OPLOCK_LEVEL_NONE);
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	hv2 = io.out.file.handle;
+	CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE);
+
+	CHECK_BREAK_INFO_V2(tree->session->transport,
+			    "RWH", "RH", LEASE1, ls1v2.lease_epoch + 4);
+
+	smb2_util_close(tree, hv2);
+	smb2_util_close(tree, hv1);
+
+	ZERO_STRUCT(io);
+	smb2_lease_create_share(&io, &ls1v1, false, fname,
+				smb2_util_share_access("RWD"),
+				LEASE1,
+				smb2_util_lease_state("RHW"));
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	hv1 = io.out.file.handle;
+	CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE(&io, "RHW", true, LEASE1, 0);
+
+	smb2_util_close(tree, hv1);
+
+done:
+	smb2_util_close(tree, hv2);
+	smb2_util_close(tree, hv1);
+	smb2_util_unlink(tree, fname);
+	talloc_free(mem_ctx);
+	return ret;
+}
+
+static bool test_lease_v2_epoch3(struct torture_context *tctx,
+				 struct smb2_tree *tree)
+{
+	TALLOC_CTX *mem_ctx = talloc_new(tctx);
+	struct smb2_create io;
+	struct smb2_lease ls1v1 = {}, ls1v1t = {},ls1v2 = {};
+	struct smb2_handle hv1 = {}, hv2 = {};
+	const char *fname = "lease_v2_epoch3.dat";
+	bool ret = true;
+	NTSTATUS status;
+	uint32_t caps;
+	enum protocol_types protocol;
+
+	caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
+	if (!(caps & SMB2_CAP_LEASING)) {
+		torture_skip(tctx, "leases are not supported");
+	}
+
+	protocol = smbXcli_conn_protocol(tree->session->transport->conn);
+	if (protocol < PROTOCOL_SMB3_00) {
+		torture_skip(tctx, "v2 leases are not supported");
+	}
+
+	smb2_util_unlink(tree, fname);
+
+	tree->session->transport->lease.handler	= torture_lease_handler;
+	tree->session->transport->lease.private_data = tree;
+	tree->session->transport->oplock.handler = torture_oplock_handler;
+	tree->session->transport->oplock.private_data = tree;
+
+	ZERO_STRUCT(break_info);
+
+	ZERO_STRUCT(io);
+	smb2_lease_create_share(&io, &ls1v1, false, fname,
+				smb2_util_share_access("RWD"),
+				LEASE1,
+				smb2_util_lease_state("R"));
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	hv1 = io.out.file.handle;
+	CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE(&io, "R", true, LEASE1, 0);
+
+	ZERO_STRUCT(io);
+	smb2_lease_v2_create_share(&io, &ls1v2, false, fname,
+				   smb2_util_share_access("RWD"),
+				   LEASE1, NULL,
+				   smb2_util_lease_state("RW"),
+				   0x4711);
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	hv2 = io.out.file.handle;
+	CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE(&io, "RW", true, LEASE1, 0);
+
+	smb2_util_close(tree, hv1);
+
+	ZERO_STRUCT(io);
+	smb2_lease_create_share(&io, &ls1v1t, false, fname,
+				smb2_util_share_access("RWD"),
+				LEASE1,
+				smb2_util_lease_state("RWH"));
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	hv1 = io.out.file.handle;
+	CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE(&io, "RWH", true, LEASE1, 0);
+
+	smb2_util_close(tree, hv1);
+
+	smb2_oplock_create(&io, fname, SMB2_OPLOCK_LEVEL_NONE);
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	hv1 = io.out.file.handle;
+	CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE);
+
+	CHECK_BREAK_INFO("RWH", "RH", LEASE1);
+
+	smb2_util_close(tree, hv1);
+	smb2_util_close(tree, hv2);
+
+	ZERO_STRUCT(io);
+	smb2_lease_v2_create_share(&io, &ls1v2, false, fname,
+				   smb2_util_share_access("RWD"),
+				   LEASE1, NULL,
+				   smb2_util_lease_state("RWH"),
+				   0x4711);
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	hv2 = io.out.file.handle;
+	CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE_V2(&io, "RHW", true, LEASE1, 0, 0, ls1v2.lease_epoch + 1);
+	smb2_util_close(tree, hv2);
+
+done:
+	smb2_util_close(tree, hv2);
+	smb2_util_close(tree, hv1);
+	smb2_util_unlink(tree, fname);
+	talloc_free(mem_ctx);
+	return ret;
+}
+
 static bool test_lease_complex1(struct torture_context *tctx,
 				struct smb2_tree *tree1a)
 {
@@ -1855,6 +2063,8 @@ struct torture_suite *torture_smb2_lease_init(void)
 				     test_lease_v2_request_parent);
 	torture_suite_add_1smb2_test(suite, "v2_request", test_lease_v2_request);
 	torture_suite_add_1smb2_test(suite, "v2_epoch1", test_lease_v2_epoch1);
+	torture_suite_add_1smb2_test(suite, "v2_epoch2", test_lease_v2_epoch2);
+	torture_suite_add_1smb2_test(suite, "v2_epoch3", test_lease_v2_epoch3);
 	torture_suite_add_1smb2_test(suite, "v2_complex1", test_lease_v2_complex1);
 
 	suite->description = talloc_strdup(suite, "SMB2-LEASE tests");
-- 
1.9.1


From 963c20a1618c80164a240eeeb93dc6a1bb500ec3 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Wed, 12 Nov 2014 15:43:56 +0100
Subject: [PATCH 35/46] s4:torture/smb2: make it possible to skip the automatic
 ack of lease breaks.

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

diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c
index 14ce739..b9df9ba 100644
--- a/source4/torture/smb2/lease.c
+++ b/source4/torture/smb2/lease.c
@@ -406,9 +406,10 @@ static bool test_lease_upgrade2(struct torture_context *tctx,
 		CHECK_VAL((__lba)->out.lease.lease_duration, 0);	\
 	} while(0)
 
-static struct {
+static struct torture_lease_break {
 	struct smb2_lease_break lease_break;
 	struct smb2_transport *lease_transport;
+	bool lease_skip_ack;
 	struct smb2_lease_break_ack lease_break_ack;
 	int count;
 	int failures;
@@ -446,8 +447,10 @@ static struct {
 		CHECK_VAL(break_info.count, 1);				\
 		CHECK_LEASE_BREAK(&break_info.lease_break, (__oldstate), \
 		    (__state), (__key));				\
-		if (break_info.lease_break.break_flags &		\
-		    SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED) {	\
+		if (!break_info.lease_skip_ack && \
+		    (break_info.lease_break.break_flags &		\
+		     SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED))	\
+		{	\
 			torture_wait_for_lease_break(tctx);		\
 			CHECK_LEASE_BREAK_ACK(&break_info.lease_break_ack, \
 				              (__state), (__key));	\
@@ -494,6 +497,10 @@ static bool torture_lease_handler(struct smb2_transport *transport,
 	break_info.lease_break = *lb;
 	break_info.count++;
 
+	if (break_info.lease_skip_ack) {
+		return true;
+	}
+
 	if (lb->break_flags & SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED) {
 		ZERO_STRUCT(io);
 		io.in.lease.lease_key = lb->current_lease.lease_key;
-- 
1.9.1


From 717d27d9cff18c57247081eebc1852bcf1ca7374 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Wed, 12 Nov 2014 15:43:56 +0100
Subject: [PATCH 36/46] s4:torture/smb2: smb2.lease.breaking1 test

This demonstrates a race case where the client reuses a lease,
while the server already sent a break.

The open succeeds with SMB2_LEASE_FLAG_BREAK_IN_PROGRESS being set.

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

diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c
index b9df9ba..ee6d094 100644
--- a/source4/torture/smb2/lease.c
+++ b/source4/torture/smb2/lease.c
@@ -1778,6 +1778,119 @@ done:
 	return ret;
 }
 
+static bool test_lease_breaking1(struct torture_context *tctx,
+				 struct smb2_tree *tree)
+{
+	TALLOC_CTX *mem_ctx = talloc_new(tctx);
+	struct smb2_create io1 = {};
+	struct smb2_create io2 = {};
+	struct smb2_lease ls1 = {};
+	struct smb2_handle h1a = {};
+	struct smb2_handle h1b = {};
+	struct smb2_handle h2 = {};
+	struct smb2_request *req2 = NULL;
+	struct smb2_lease_break_ack ack = {};
+	const char *fname = "lease_breaking1.dat";
+	bool ret = true;
+	NTSTATUS status;
+	uint32_t caps;
+	enum protocol_types protocol;
+
+	caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
+	if (!(caps & SMB2_CAP_LEASING)) {
+		torture_skip(tctx, "leases are not supported");
+	}
+
+	protocol = smbXcli_conn_protocol(tree->session->transport->conn);
+	if (protocol < PROTOCOL_SMB3_00) {
+		torture_skip(tctx, "v2 leases are not supported");
+	}
+
+	smb2_util_unlink(tree, fname);
+
+	tree->session->transport->lease.handler	= torture_lease_handler;
+	tree->session->transport->lease.private_data = tree;
+	tree->session->transport->oplock.handler = torture_oplock_handler;
+	tree->session->transport->oplock.private_data = tree;
+
+	/*
+	 * we defer acking the lease break.
+	 */
+	ZERO_STRUCT(break_info);
+	break_info.lease_skip_ack = true;
+
+	smb2_lease_create_share(&io1, &ls1, false, fname,
+				smb2_util_share_access("RWD"),
+				LEASE1,
+				smb2_util_lease_state("RWH"));
+	status = smb2_create(tree, mem_ctx, &io1);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h1a = io1.out.file.handle;
+	CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE(&io1, "RWH", true, LEASE1, 0);
+
+	/*
+	 * a conflicting open is blocked until we ack the
+	 * lease break
+	 */
+	smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
+	req2 = smb2_create_send(tree, &io2);
+	torture_assert(tctx, req2 != NULL, "smb2_create_send");
+
+	/*
+	 * we got the lease break, but defer the ack.
+	 */
+	CHECK_BREAK_INFO("RWH", "RH", LEASE1);
+
+	torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending");
+
+	ack.in.lease.lease_key =
+		break_info.lease_break.current_lease.lease_key;
+	ack.in.lease.lease_state =
+		break_info.lease_break.new_lease_state;
+	ZERO_STRUCT(break_info);
+
+	/*
+	 * a open using the same lease key is still works,
+	 * but reports SMB2_LEASE_FLAG_BREAK_IN_PROGRESS
+	 */
+	status = smb2_create(tree, mem_ctx, &io1);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h1b = io1.out.file.handle;
+	CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE(&io1, "RWH", true, LEASE1, SMB2_LEASE_FLAG_BREAK_IN_PROGRESS);
+	smb2_util_close(tree, h1b);
+
+	CHECK_NO_BREAK(tctx);
+
+	torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending");
+
+	/*
+	 * We ack the lease break.
+	 */
+	status = smb2_lease_break_ack(tree, &ack);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	CHECK_LEASE_BREAK_ACK(&ack, "RH", LEASE1);
+
+	torture_assert(tctx, req2->cancel.can_cancel,
+		       "req2 can_cancel");
+
+	status = smb2_create_recv(req2, tctx, &io2);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h2 = io2.out.file.handle;
+	CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE);
+
+	CHECK_NO_BREAK(tctx);
+done:
+	smb2_util_close(tree, h1a);
+	smb2_util_close(tree, h1b);
+	smb2_util_close(tree, h2);
+	smb2_util_unlink(tree, fname);
+	talloc_free(mem_ctx);
+	return ret;
+}
+
 static bool test_lease_complex1(struct torture_context *tctx,
 				struct smb2_tree *tree1a)
 {
@@ -2065,6 +2178,7 @@ struct torture_suite *torture_smb2_lease_init(void)
 	torture_suite_add_1smb2_test(suite, "break", test_lease_break);
 	torture_suite_add_1smb2_test(suite, "oplock", test_lease_oplock);
 	torture_suite_add_1smb2_test(suite, "multibreak", test_lease_multibreak);
+	torture_suite_add_1smb2_test(suite, "breaking1", test_lease_breaking1);
 	torture_suite_add_1smb2_test(suite, "complex1", test_lease_complex1);
 	torture_suite_add_1smb2_test(suite, "v2_request_parent",
 				     test_lease_v2_request_parent);
-- 
1.9.1


From 760bf0a02b0fc853bd46cd8a47bb8afff444298c Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Wed, 12 Nov 2014 15:43:56 +0100
Subject: [PATCH 37/46] s4:torture/smb2: smb2.lease.breaking2 test

This demonstrates a race case where the client reuses a lease,
while the server already sent a break.

The open succeeds with SMB2_LEASE_FLAG_BREAK_IN_PROGRESS being set.

This is more complex that smb2.lease.breaking1 as it generates breaks
from RWH => RH => R => NONE.

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

diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c
index ee6d094..221d45c 100644
--- a/source4/torture/smb2/lease.c
+++ b/source4/torture/smb2/lease.c
@@ -1891,6 +1891,200 @@ done:
 	return ret;
 }
 
+static bool test_lease_breaking2(struct torture_context *tctx,
+				 struct smb2_tree *tree)
+{
+	TALLOC_CTX *mem_ctx = talloc_new(tctx);
+	struct smb2_create io1 = {};
+	struct smb2_create io2 = {};
+	struct smb2_create io3 = {};
+	struct smb2_lease ls1 = {};
+	struct smb2_handle h1a = {};
+	struct smb2_handle h1b = {};
+	struct smb2_handle h2 = {};
+	struct smb2_handle h3 = {};
+	struct smb2_request *req2 = NULL;
+	struct smb2_request *req3 = NULL;
+	struct torture_lease_break break_info_tmp = {};
+	struct smb2_lease_break_ack ack = {};
+	const char *fname = "lease_breaking2.dat";
+	bool ret = true;
+	NTSTATUS status;
+	uint32_t caps;
+	enum protocol_types protocol;
+
+	caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
+	if (!(caps & SMB2_CAP_LEASING)) {
+		torture_skip(tctx, "leases are not supported");
+	}
+
+	protocol = smbXcli_conn_protocol(tree->session->transport->conn);
+	if (protocol < PROTOCOL_SMB3_00) {
+		torture_skip(tctx, "v2 leases are not supported");
+	}
+
+	smb2_util_unlink(tree, fname);
+
+	tree->session->transport->lease.handler	= torture_lease_handler;
+	tree->session->transport->lease.private_data = tree;
+	tree->session->transport->oplock.handler = torture_oplock_handler;
+	tree->session->transport->oplock.private_data = tree;
+
+	/*
+	 * we defer acking the lease break.
+	 */
+	ZERO_STRUCT(break_info);
+	break_info.lease_skip_ack = true;
+
+	smb2_lease_create_share(&io1, &ls1, false, fname,
+				smb2_util_share_access("RWD"),
+				LEASE1,
+				smb2_util_lease_state("RWH"));
+	status = smb2_create(tree, mem_ctx, &io1);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h1a = io1.out.file.handle;
+	CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE(&io1, "RWH", true, LEASE1, 0);
+
+	/*
+	 * a conflicting open is blocked until we ack the
+	 * lease break
+	 */
+	smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
+	req2 = smb2_create_send(tree, &io2);
+	torture_assert(tctx, req2 != NULL, "smb2_create_send");
+
+	/*
+	 * we got the lease break, but defer the ack.
+	 */
+	CHECK_BREAK_INFO("RWH", "RH", LEASE1);
+
+	torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending");
+
+	/*
+	 * a open using the same lease key is still works,
+	 * but reports SMB2_LEASE_FLAG_BREAK_IN_PROGRESS
+	 */
+	status = smb2_create(tree, mem_ctx, &io1);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h1b = io1.out.file.handle;
+	CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE(&io1, "RWH", true, LEASE1, SMB2_LEASE_FLAG_BREAK_IN_PROGRESS);
+	smb2_util_close(tree, h1b);
+
+	/*
+	 * a conflicting open with NTCREATEX_DISP_OVERWRITE
+	 * doesn't trigger an immediate lease break to none.
+	 */
+	break_info_tmp = break_info;
+	ZERO_STRUCT(break_info);
+	smb2_oplock_create(&io3, fname, SMB2_OPLOCK_LEVEL_NONE);
+	io3.in.create_disposition = NTCREATEX_DISP_OVERWRITE;
+	req3 = smb2_create_send(tree, &io3);
+	torture_assert(tctx, req3 != NULL, "smb2_create_send");
+	CHECK_NO_BREAK(tctx);
+	break_info = break_info_tmp;
+
+	torture_assert(tctx, req3->state == SMB2_REQUEST_RECV, "req3 pending");
+
+	ack.in.lease.lease_key =
+		break_info.lease_break.current_lease.lease_key;
+	ack.in.lease.lease_state =
+		break_info.lease_break.new_lease_state;
+	ZERO_STRUCT(break_info);
+
+	/*
+	 * a open using the same lease key is still works,
+	 * but reports SMB2_LEASE_FLAG_BREAK_IN_PROGRESS
+	 */
+	status = smb2_create(tree, mem_ctx, &io1);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h1b = io1.out.file.handle;
+	CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE(&io1, "RWH", true, LEASE1, SMB2_LEASE_FLAG_BREAK_IN_PROGRESS);
+	smb2_util_close(tree, h1b);
+
+	CHECK_NO_BREAK(tctx);
+
+	/*
+	 * We ack the lease break, but defer acking the next break (to "R")
+	 */
+	break_info.lease_skip_ack = true;
+	status = smb2_lease_break_ack(tree, &ack);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	CHECK_LEASE_BREAK_ACK(&ack, "RH", LEASE1);
+
+	/*
+	 * We got an additional break downgrading to just "R"
+	 * while we defer the ack.
+	 */
+	CHECK_BREAK_INFO("RH", "R", LEASE1);
+
+	ack.in.lease.lease_key =
+		break_info.lease_break.current_lease.lease_key;
+	ack.in.lease.lease_state =
+		break_info.lease_break.new_lease_state;
+	ZERO_STRUCT(break_info);
+
+	/*
+	 * a open using the same lease key is still works,
+	 * but reports SMB2_LEASE_FLAG_BREAK_IN_PROGRESS
+	 */
+	status = smb2_create(tree, mem_ctx, &io1);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h1b = io1.out.file.handle;
+	CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE(&io1, "RH", true, LEASE1, SMB2_LEASE_FLAG_BREAK_IN_PROGRESS);
+	smb2_util_close(tree, h1b);
+
+	CHECK_NO_BREAK(tctx);
+
+	torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending");
+	torture_assert(tctx, req3->state == SMB2_REQUEST_RECV, "req3 pending");
+
+	/*
+	 * We ack the downgrade to "R" and get an immediate break to none
+	 */
+	status = smb2_lease_break_ack(tree, &ack);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	CHECK_LEASE_BREAK_ACK(&ack, "R", LEASE1);
+
+	/*
+	 * We get the downgrade to none.
+	 */
+	CHECK_BREAK_INFO("R", "", LEASE1);
+
+	torture_assert(tctx, req2->cancel.can_cancel,
+		       "req2 can_cancel");
+	torture_assert(tctx, req3->cancel.can_cancel,
+		       "req2 can_cancel");
+
+	ZERO_STRUCT(break_info);
+
+	status = smb2_create_recv(req2, tctx, &io2);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h2 = io2.out.file.handle;
+	CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE);
+
+	status = smb2_create_recv(req3, tctx, &io3);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h3 = io3.out.file.handle;
+	CHECK_CREATED(&io3, TRUNCATED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_VAL(io3.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE);
+
+	CHECK_NO_BREAK(tctx);
+done:
+	smb2_util_close(tree, h1a);
+	smb2_util_close(tree, h1b);
+	smb2_util_close(tree, h2);
+	smb2_util_close(tree, h3);
+
+	smb2_util_unlink(tree, fname);
+	talloc_free(mem_ctx);
+	return ret;
+}
+
 static bool test_lease_complex1(struct torture_context *tctx,
 				struct smb2_tree *tree1a)
 {
@@ -2179,6 +2373,7 @@ struct torture_suite *torture_smb2_lease_init(void)
 	torture_suite_add_1smb2_test(suite, "oplock", test_lease_oplock);
 	torture_suite_add_1smb2_test(suite, "multibreak", test_lease_multibreak);
 	torture_suite_add_1smb2_test(suite, "breaking1", test_lease_breaking1);
+	torture_suite_add_1smb2_test(suite, "breaking2", test_lease_breaking2);
 	torture_suite_add_1smb2_test(suite, "complex1", test_lease_complex1);
 	torture_suite_add_1smb2_test(suite, "v2_request_parent",
 				     test_lease_v2_request_parent);
-- 
1.9.1


From 264417460d43f6caaf8d499ab938092127391049 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Wed, 12 Nov 2014 22:47:13 -0800
Subject: [PATCH 38/46] s3: leases: When processing an oplock break message, if
 we need to increment the epoch remember to write it out again.

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

diff --git a/source3/smbd/oplock.c b/source3/smbd/oplock.c
index 5523de9..3e2b951 100644
--- a/source3/smbd/oplock.c
+++ b/source3/smbd/oplock.c
@@ -618,6 +618,8 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx,
 			/* Need to increment the epoch */
 			o->epoch += 1;
 			fsp->lease->lease.lease_epoch = o->epoch;
+			/* And remember to write it out again.. */
+			lck->data->modified = true;
 		}
 
 		TALLOC_FREE(lck);
-- 
1.9.1


From af6d327bdf98689b70878905e995ed4ef004d7ff Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Wed, 12 Nov 2014 22:56:38 -0800
Subject: [PATCH 39/46] s3: leases : Ensure we set the lease_version correctly
 from the requesting client (where we can).

It matters when sending lease break requests - the epoch
field must be correctly sent for v2 leases.

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 source3/smbd/durable.c    | 15 ++++++++++++++-
 source3/smbd/open.c       | 12 +++++++++---
 source3/smbd/proto.h      |  1 +
 source3/smbd/smb2_break.c |  2 +-
 4 files changed, 25 insertions(+), 5 deletions(-)

diff --git a/source3/smbd/durable.c b/source3/smbd/durable.c
index 6e668ce..28e8a42a 100644
--- a/source3/smbd/durable.c
+++ b/source3/smbd/durable.c
@@ -727,11 +727,24 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
 	if (fsp->oplock_type == LEASE_OPLOCK) {
 		struct share_mode_oplock *o = &lck->data->leases[e->lease_idx];
 		struct smb2_lease_key key;
+		uint16_t lease_version;
 
 		key.data[0] = o->lease_key.data[0];
 		key.data[1] = o->lease_key.data[1];
 
-		fsp->lease = find_fsp_lease(fsp, &key, o);
+		/*
+		 * We don't pass down the reqesting lease
+		 * version - we should. For now infer from
+		 * the protocol.
+		 */
+
+		if (fsp->conn->sconn->client->connections->protocol >= PROTOCOL_SMB3_00) {
+			lease_version = 2;
+		} else {
+			lease_version = 1;
+		}
+
+		fsp->lease = find_fsp_lease(fsp, &key, lease_version, o);
 		if (fsp->lease == NULL) {
 			TALLOC_FREE(lck);
 			fsp_free(fsp);
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index 3f576d8..a2033b2 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1560,6 +1560,7 @@ static bool is_same_lease(const files_struct *fsp,
 
 struct fsp_lease *find_fsp_lease(files_struct *new_fsp,
 				 const struct smb2_lease_key *key,
+				 uint16_t lease_version,
 				 const struct share_mode_oplock *o)
 {
 	files_struct *fsp;
@@ -1593,8 +1594,12 @@ struct fsp_lease *find_fsp_lease(files_struct *new_fsp,
 	new_fsp->lease->ref_count = 1;
 	new_fsp->lease->lease.lease_key = *key;
 	new_fsp->lease->lease.lease_state = o->current_state;
-	/* Internally treat all leases as version 2 with epoch. */
-	new_fsp->lease->lease.lease_version = 2;
+	/*
+	 * We internally treat all leases as V2 and update
+	 * the epoch, but when sending breaks it matters if
+	 * the requesting lease was v1 or v2.
+	 */
+	new_fsp->lease->lease.lease_version = lease_version;
 	new_fsp->lease->lease.lease_epoch = o->epoch;
 	return new_fsp->lease;
 }
@@ -1616,7 +1621,8 @@ static NTSTATUS grant_fsp_lease(files_struct *fsp, struct share_mode_data *d,
 		bool do_upgrade;
 		uint32_t existing, requested;
 
-		fsp->lease = find_fsp_lease(fsp, &lease->lease_key, o);
+		fsp->lease = find_fsp_lease(fsp, &lease->lease_key,
+					lease->lease_version, o);
 		if (fsp->lease == NULL) {
 			DEBUG(1, ("Did not find existing lease for file %s\n",
 				  fsp_str_dbg(fsp)));
diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
index ecf89a7..22dddf4 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -617,6 +617,7 @@ NTSTATUS change_dir_owner_to_parent(connection_struct *conn,
 struct share_mode_oplock;
 struct fsp_lease *find_fsp_lease(files_struct *new_fsp,
 				 const struct smb2_lease_key *key,
+				 uint16_t lease_version,
 				 const struct share_mode_oplock *o);
 bool is_stat_open(uint32 access_mask);
 struct deferred_open_record;
diff --git a/source3/smbd/smb2_break.c b/source3/smbd/smb2_break.c
index abe6709..9ab118d 100644
--- a/source3/smbd/smb2_break.c
+++ b/source3/smbd/smb2_break.c
@@ -431,7 +431,7 @@ void send_break_message_smb2(files_struct *fsp, uint32_t break_to)
 		no_ack = ((fsp->lease->lease.lease_state == SMB2_LEASE_READ) &&
 			  (break_to == SMB2_LEASE_NONE));
 
-		if (xconn->protocol >= PROTOCOL_SMB3_00) {
+		if (fsp->lease->lease.lease_version > 1) {
 			new_epoch = fsp->lease->lease.lease_epoch;
 		} else {
 			new_epoch = 0;
-- 
1.9.1


From 3dc3fb850daee3d14d99bc1b1e4141421d7ed7aa Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Wed, 12 Nov 2014 22:59:45 -0800
Subject: [PATCH 40/46] s3: leases - correctly send back
 SMB2_LEASE_FLAG_BREAK_IN_PROGRESS if we're waiting for a lease break
 response.

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 source3/smbd/oplock.c      | 17 +++++++++++++++++
 source3/smbd/proto.h       |  1 +
 source3/smbd/smb2_create.c |  6 ++++++
 3 files changed, 24 insertions(+)

diff --git a/source3/smbd/oplock.c b/source3/smbd/oplock.c
index 3e2b951..4755ca6 100644
--- a/source3/smbd/oplock.c
+++ b/source3/smbd/oplock.c
@@ -937,6 +937,23 @@ done:
 	return;
 }
 
+bool break_in_progress(const files_struct *fsp)
+{
+	files_struct *new_fsp;
+
+	for (new_fsp = file_find_di_first(fsp->conn->sconn, fsp->file_id);
+			new_fsp != NULL;
+			new_fsp = file_find_di_next(new_fsp)) {
+		if (new_fsp == fsp) {
+			continue;
+		}
+		if (new_fsp->sent_oplock_break != NO_BREAK_SENT) {
+			return true;
+		}
+	}
+	return false;
+}
+
 void smbd_contend_level2_oplocks_begin(files_struct *fsp,
 				  enum level2_contention_type type)
 {
diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
index 22dddf4..f507696 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -672,6 +672,7 @@ NTSTATUS downgrade_lease(struct smbd_server_connection *sconn,
 			 const struct file_id id,
 			 const struct smb2_lease_key *key,
 			 uint32_t lease_state);
+bool break_in_progress(const files_struct *fsp);
 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/smb2_create.c b/source3/smbd/smb2_create.c
index 0c49c70..b076596 100644
--- a/source3/smbd/smb2_create.c
+++ b/source3/smbd/smb2_create.c
@@ -1213,6 +1213,12 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 			lease.lease_flags &=
 				SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET;
 
+			/* Is there a break in progress ? */
+			if (break_in_progress(result)) {
+				lease.lease_flags |=
+					SMB2_LEASE_FLAG_BREAK_IN_PROGRESS;
+			}
+
 			if (!smb2_lease_push(&lease, buf, lease_len)) {
 				tevent_req_nterror(
 					req, NT_STATUS_INTERNAL_ERROR);
-- 
1.9.1


From fae5126c08f8f50f39faebece5d1d5d8b16fd34c Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 13 Nov 2014 12:10:46 +0100
Subject: [PATCH 41/46] s3:smb2_create: send interim responses after 0.5
 milliseconds

We don't have to care about delayed NT_STATUS_SHARING_VIOLATION anymore
after the following commit:

    commit 4111fcfd4f570d39d46a0d414546ca62c7b609be
    Author: Jeremy Allison <jra at samba.org>
    Date:   Thu May 2 11:12:47 2013 -0700

        Only do the 1 second delay for sharing violations for SMB1, not SMB2.

        Match Windows behavior.

Signed-off-by: Stefan Metzmacher <metze at samba.org>
---
 source3/smbd/smb2_create.c | 8 +-------
 1 file changed, 1 insertion(+), 7 deletions(-)

diff --git a/source3/smbd/smb2_create.c b/source3/smbd/smb2_create.c
index b076596..4ff9090 100644
--- a/source3/smbd/smb2_create.c
+++ b/source3/smbd/smb2_create.c
@@ -242,13 +242,7 @@ NTSTATUS smbd_smb2_request_process_create(struct smbd_smb2_request *smb2req)
 	}
 	tevent_req_set_callback(tsubreq, smbd_smb2_request_create_done, smb2req);
 
-	/*
-	 * For now we keep the logic that we do not send STATUS_PENDING
-	 * for sharing violations, so we just wait 2 seconds.
-	 *
-	 * TODO: we need more tests for this.
-	 */
-	return smbd_smb2_request_pending_queue(smb2req, tsubreq, 2000000);
+	return smbd_smb2_request_pending_queue(smb2req, tsubreq, 500);
 }
 
 static uint64_t get_mid_from_smb2req(struct smbd_smb2_request *smb2req)
-- 
1.9.1


From a032e3f2d6ab011ba3494c8e6b4d364109486765 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 13 Nov 2014 09:47:20 +0100
Subject: [PATCH 42/46] source3/librpc/idl/open_files.idl lease_version

---
 source3/librpc/idl/open_files.idl | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/source3/librpc/idl/open_files.idl b/source3/librpc/idl/open_files.idl
index 48e6bbe..9c1095b 100644
--- a/source3/librpc/idl/open_files.idl
+++ b/source3/librpc/idl/open_files.idl
@@ -56,7 +56,8 @@ interface open_files
 		 */
 		share_mode_caching      breaking_to_state;
 		boolean8                breaking;
-		uint16			epoch;
+		uint16                  lease_version;
+		uint16                  epoch;
 	} share_mode_oplock;
 
 	typedef [public] struct {
-- 
1.9.1


From 47997ec29224b58e9ba6427c144b7002d5fb52dc Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 13 Nov 2014 09:47:49 +0100
Subject: [PATCH 43/46] grant_fsp_lease store lease_version...

---
 source3/smbd/open.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index a2033b2..94d8140 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1695,6 +1695,7 @@ 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 = fsp->lease->lease.lease_key,
+		.lease_version = fsp->lease->lease.lease_version,
 		.epoch = fsp->lease->lease.lease_epoch,
 		.current_state = granted,
 	};
-- 
1.9.1


From 98bfdae9fb1de7294f0b131fa06504ccd1d8e303 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 13 Nov 2014 09:48:15 +0100
Subject: [PATCH 44/46] sq find_fsp_lease load lease_version from database

---
 source3/smbd/durable.c | 15 +--------------
 source3/smbd/open.c    |  6 ++----
 source3/smbd/proto.h   |  1 -
 3 files changed, 3 insertions(+), 19 deletions(-)

diff --git a/source3/smbd/durable.c b/source3/smbd/durable.c
index 28e8a42a..6e668ce 100644
--- a/source3/smbd/durable.c
+++ b/source3/smbd/durable.c
@@ -727,24 +727,11 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
 	if (fsp->oplock_type == LEASE_OPLOCK) {
 		struct share_mode_oplock *o = &lck->data->leases[e->lease_idx];
 		struct smb2_lease_key key;
-		uint16_t lease_version;
 
 		key.data[0] = o->lease_key.data[0];
 		key.data[1] = o->lease_key.data[1];
 
-		/*
-		 * We don't pass down the reqesting lease
-		 * version - we should. For now infer from
-		 * the protocol.
-		 */
-
-		if (fsp->conn->sconn->client->connections->protocol >= PROTOCOL_SMB3_00) {
-			lease_version = 2;
-		} else {
-			lease_version = 1;
-		}
-
-		fsp->lease = find_fsp_lease(fsp, &key, lease_version, o);
+		fsp->lease = find_fsp_lease(fsp, &key, o);
 		if (fsp->lease == NULL) {
 			TALLOC_FREE(lck);
 			fsp_free(fsp);
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index 94d8140..804b6f0 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1560,7 +1560,6 @@ static bool is_same_lease(const files_struct *fsp,
 
 struct fsp_lease *find_fsp_lease(files_struct *new_fsp,
 				 const struct smb2_lease_key *key,
-				 uint16_t lease_version,
 				 const struct share_mode_oplock *o)
 {
 	files_struct *fsp;
@@ -1599,7 +1598,7 @@ struct fsp_lease *find_fsp_lease(files_struct *new_fsp,
 	 * the epoch, but when sending breaks it matters if
 	 * the requesting lease was v1 or v2.
 	 */
-	new_fsp->lease->lease.lease_version = lease_version;
+	new_fsp->lease->lease.lease_version = o->lease_version;
 	new_fsp->lease->lease.lease_epoch = o->epoch;
 	return new_fsp->lease;
 }
@@ -1621,8 +1620,7 @@ static NTSTATUS grant_fsp_lease(files_struct *fsp, struct share_mode_data *d,
 		bool do_upgrade;
 		uint32_t existing, requested;
 
-		fsp->lease = find_fsp_lease(fsp, &lease->lease_key,
-					lease->lease_version, o);
+		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)));
diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
index f507696..c8e404e 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -617,7 +617,6 @@ NTSTATUS change_dir_owner_to_parent(connection_struct *conn,
 struct share_mode_oplock;
 struct fsp_lease *find_fsp_lease(files_struct *new_fsp,
 				 const struct smb2_lease_key *key,
-				 uint16_t lease_version,
 				 const struct share_mode_oplock *o);
 bool is_stat_open(uint32 access_mask);
 struct deferred_open_record;
-- 
1.9.1


From 990db506ee247e579a2d33a61c4c08dc52daa674 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 13 Nov 2014 11:56:36 +0100
Subject: [PATCH 45/46] sq find_fsp_lease load lease_version from database
 smb2_create

---
 source3/smbd/smb2_create.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/source3/smbd/smb2_create.c b/source3/smbd/smb2_create.c
index 4ff9090..482bb66 100644
--- a/source3/smbd/smb2_create.c
+++ b/source3/smbd/smb2_create.c
@@ -1213,6 +1213,11 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 					SMB2_LEASE_FLAG_BREAK_IN_PROGRESS;
 			}
 
+			lease_len = sizeof(buf);
+			if (lease.lease_version == 1) {
+				lease_len = 32;
+			}
+
 			if (!smb2_lease_push(&lease, buf, lease_len)) {
 				tevent_req_nterror(
 					req, NT_STATUS_INTERNAL_ERROR);
-- 
1.9.1


From 3a9ca0328059284c8c1e2619ab44acdee73bce62 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 13 Nov 2014 20:33:15 +0100
Subject: [PATCH 46/46] skip test_lease_breaking2

---
 source4/torture/smb2/lease.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c
index 221d45c..2b47611 100644
--- a/source4/torture/smb2/lease.c
+++ b/source4/torture/smb2/lease.c
@@ -2373,7 +2373,7 @@ struct torture_suite *torture_smb2_lease_init(void)
 	torture_suite_add_1smb2_test(suite, "oplock", test_lease_oplock);
 	torture_suite_add_1smb2_test(suite, "multibreak", test_lease_multibreak);
 	torture_suite_add_1smb2_test(suite, "breaking1", test_lease_breaking1);
-	torture_suite_add_1smb2_test(suite, "breaking2", test_lease_breaking2);
+	//torture_suite_add_1smb2_test(suite, "breaking2", test_lease_breaking2);
 	torture_suite_add_1smb2_test(suite, "complex1", test_lease_complex1);
 	torture_suite_add_1smb2_test(suite, "v2_request_parent",
 				     test_lease_v2_request_parent);
-- 
1.9.1

-------------- next part --------------
From 165212ac9a7bdac5c2d3722efa6bf176975eee59 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 13 Nov 2014 11:50:14 +0100
Subject: [PATCH 1/6] TODO: breaking_to_state... works...

---
 source3/locking/locking.c  |  60 +++++++++--
 source3/locking/proto.h    |   4 +-
 source3/smbd/globals.h     |   6 +-
 source3/smbd/open.c        |   8 ++
 source3/smbd/oplock.c      | 244 +++++++++++++++++++++++++++++++++------------
 source3/smbd/proto.h       |   5 +-
 source3/smbd/smb2_break.c  |  30 ++++--
 source3/smbd/smb2_create.c |   8 --
 source3/smbd/smb2_server.c |  17 ++--
 9 files changed, 276 insertions(+), 106 deletions(-)

diff --git a/source3/locking/locking.c b/source3/locking/locking.c
index 006d6d3..b3ab9ad 100644
--- a/source3/locking/locking.c
+++ b/source3/locking/locking.c
@@ -945,6 +945,7 @@ bool fsp_lease_broken(struct share_mode_lock *lck,
 {
 	struct share_mode_data *d = lck->data;
 	struct share_mode_entry *e;
+	struct share_mode_oplock *o;
 
 	if (!file_id_equal(&fsp->file_id, &lck_id)) {
 		return false;
@@ -960,27 +961,42 @@ bool fsp_lease_broken(struct share_mode_lock *lck,
 		return false;
 	}
 
+	o = &d->leases[e->lease_idx];
+
 	if (!smb2_lease_equal(&fsp->conn->sconn->client->connections->smb2.client.guid,
-			      key,
-			      &d->leases[e->lease_idx].client_guid,
-			      &d->leases[e->lease_idx].lease_key)) {
+			      key, &o->client_guid, &o->lease_key)) {
 		return false;
 	}
-	fsp->sent_oplock_break = NO_BREAK_SENT;
+
 	TALLOC_FREE(fsp->oplock_timeout);
-	fsp->lease->lease.lease_state = new_lease_state;
+
+	DEBUG(0,("%s: refresh lease state - line(%d)\n", __func__, __LINE__));
+	/* Ensure we're in sync with current lease state. */
+	fsp->lease->lease.lease_state = o->current_state;
+	fsp->lease->lease.lease_epoch = o->epoch;
+	if (o->breaking) {
+		DEBUG(0,("%s: refresh lease state - line(%d)\n", __func__, __LINE__));
+		fsp->lease->lease.lease_flags |= SMB2_LEASE_FLAG_BREAK_IN_PROGRESS;
+	} else {
+		DEBUG(0,("%s: refresh lease state - line(%d)\n", __func__, __LINE__));
+		fsp->lease->lease.lease_flags &= ~SMB2_LEASE_FLAG_BREAK_IN_PROGRESS;
+	}
+
 	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)
+			       uint32_t new_lease_state,
+			       struct share_mode_oplock **_l)
 {
 	struct share_mode_data *d = lck->data;
 	struct share_mode_oplock *l;
 	uint32_t i;
 
+	*_l = NULL;
+
 	for (i=0; i<d->num_leases; i++) {
 		if (smb2_lease_equal(&sconn->client->connections->smb2.client.guid,
 				     key,
@@ -996,18 +1012,44 @@ NTSTATUS downgrade_share_lease(struct smbd_server_connection *sconn,
 
 	l = &d->leases[i];
 
+	if (!l->breaking) {
+		DEBUG(0, ("Attempt to break from %d to %d - but we're not in breaking state\n",
+			   (int)l->current_state, (int)new_lease_state));
+		return NT_STATUS_INVALID_OPLOCK_PROTOCOL;
+	}
+
 	/*
 	 * 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));
+		DEBUG(0, ("Attempt to upgrade from %d to %d - expected %d\n",
+			   (int)l->current_state, (int)new_lease_state,
+			   (int)l->breaking_to_state));
 		return NT_STATUS_REQUEST_NOT_ACCEPTED;
 	}
 
-	l->current_state = new_lease_state;
+	if (l->current_state != new_lease_state) {
+		l->current_state = new_lease_state;
+		d->modified = true;
+	}
+
+	if ((new_lease_state & ~l->breaking_to_state) != 0) {
+		DEBUG(0, ("lease state %d not fully broken from %d to %d\n",
+			   (int)new_lease_state,
+			   (int)l->current_state,
+			   (int)l->breaking_to_state));
+		*_l = l;
+		return NT_STATUS_OPLOCK_BREAK_IN_PROGRESS;
+	}
+
+	//TODO: verify new_lease_state == l->breaking_to_state ???
+
+	DEBUG(0, ("breaking from %d to %d - expected %d\n",
+		   (int)l->current_state, (int)new_lease_state,
+		   (int)l->breaking_to_state));
+
 	l->breaking_to_state = 0;
 	l->breaking = 0;
 
diff --git a/source3/locking/proto.h b/source3/locking/proto.h
index 94e9b8f..c48cd89 100644
--- a/source3/locking/proto.h
+++ b/source3/locking/proto.h
@@ -179,10 +179,12 @@ 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_oplock;
 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);
+			       uint32_t new_lease_state,
+			       struct share_mode_oplock **_l);
 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/globals.h b/source3/smbd/globals.h
index f297ff7..0ce1982 100644
--- a/source3/smbd/globals.h
+++ b/source3/smbd/globals.h
@@ -251,8 +251,6 @@ NTSTATUS smbd_smb2_send_oplock_break(struct smbXsrv_connection *xconn,
 				     struct smbXsrv_open *op,
 				     uint8_t oplock_level);
 NTSTATUS smbd_smb2_send_lease_break(struct smbXsrv_connection *xconn,
-				    struct smbXsrv_session *session,
-				    struct smbXsrv_tcon *tcon,
 				    uint16_t new_epoch,
 				    uint32_t lease_flags,
 				    struct smb2_lease_key *lease_key,
@@ -306,7 +304,9 @@ void smbd_smb2_request_dispatch_immediate(struct tevent_context *ctx,
 struct deferred_open_record;
 
 /* SMB1 -> SMB2 glue. */
-void send_break_message_smb2(files_struct *fsp, uint32_t break_to);
+void send_break_message_smb2(files_struct *fsp,
+			     uint32_t break_from,
+			     uint32_t break_to);
 struct blocking_lock_record *get_pending_smb2req_blr(struct smbd_smb2_request *smb2req);
 bool push_blocking_lock_request_smb2( struct byte_range_lock *br_lck,
 				struct smb_request *req,
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index 804b6f0..33a2bf9 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1430,6 +1430,14 @@ static bool delay_for_oplock(files_struct *fsp,
 			if (share_mode_stale_pid(d, i)) {
 				continue;
 			}
+
+			//if (e->op_type == LEASE_OPLOCK) {
+			//	struct share_mode_oplock *o = NULL;
+			//	o = &d->leases[e->lease_idx];
+			//	if (o->breaking) {
+			//		continue;
+			//	}
+			//}
 			send_break_message(fsp->conn->sconn->msg_ctx, e,
 					   e_lease_type & ~SMB2_LEASE_HANDLE);
 			have_broken = true;
diff --git a/source3/smbd/oplock.c b/source3/smbd/oplock.c
index 4755ca6..240ff59 100644
--- a/source3/smbd/oplock.c
+++ b/source3/smbd/oplock.c
@@ -291,6 +291,40 @@ bool downgrade_oplock(files_struct *fsp)
 	return ret;
 }
 
+struct downgrade_lease_additional_state {
+	struct tevent_immediate *im;
+	struct smbXsrv_connection *xconn;
+	uint32_t lease_flags;
+	struct smb2_lease_key lease_key;
+	uint32_t current_lease_state;
+	uint32_t new_lease_state;
+	uint16_t new_epoch;
+};
+
+static void downgrade_lease_additional_trigger(struct tevent_context *ev,
+					       struct tevent_immediate *im,
+					       void *private_data)
+{
+	struct downgrade_lease_additional_state *state =
+		talloc_get_type_abort(private_data,
+		struct downgrade_lease_additional_state);
+	struct smbXsrv_connection *xconn = state->xconn;
+	NTSTATUS status;
+
+	status = smbd_smb2_send_lease_break(xconn,
+					    state->new_epoch,
+					    state->lease_flags,
+					    &state->lease_key,
+					    state->current_lease_state,
+					    state->new_lease_state);
+	TALLOC_FREE(state);
+	if (!NT_STATUS_IS_OK(status)) {
+		smbd_server_connection_terminate(xconn,
+						 nt_errstr(status));
+		return;
+	}
+}
+
 struct downgrade_lease_fsps_state {
 	struct file_id id;
 	struct share_mode_lock *lck;
@@ -312,22 +346,59 @@ static struct files_struct *downgrade_lease_fsps(struct files_struct *fsp,
 	return NULL;
 }
 
-NTSTATUS downgrade_lease(struct smbd_server_connection *sconn,
+NTSTATUS downgrade_lease(struct smbXsrv_connection *xconn,
 			 const struct file_id id,
-			 const struct smb2_lease_key *key,
+			 const struct smb2_lease *lease,
 			 uint32_t lease_state)
 {
+	struct smbd_server_connection *sconn = xconn->client->sconn;
+	const struct smb2_lease_key *key = &lease->lease_key;
 	struct share_mode_lock *lck;
+	struct share_mode_oplock *l = NULL;
 	NTSTATUS status;
 
-	DEBUG(10, ("%s: Downgrading %s to %x\n", __func__,
+	DEBUG(0, ("%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);
+	status = downgrade_share_lease(sconn, lck, key, lease_state, &l);
+
+	DEBUG(0, ("%s: Downgrading %s to %x => %s\n", __func__,
+		   file_id_string_tos(&id), (unsigned)lease_state, nt_errstr(status)));
+
+	if (NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_BREAK_IN_PROGRESS)) {
+		struct downgrade_lease_additional_state *state;
+
+		state = talloc_zero(xconn,
+				    struct downgrade_lease_additional_state);
+		if (state == NULL) {
+			TALLOC_FREE(lck);
+			return NT_STATUS_NO_MEMORY;
+		}
+
+		state->im = tevent_create_immediate(state);
+		if (state->im == NULL) {
+			TALLOC_FREE(state);
+			TALLOC_FREE(lck);
+			return NT_STATUS_NO_MEMORY;
+		}
+
+		state->xconn = xconn;
+		state->lease_flags = SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED;
+		state->lease_key = l->lease_key;
+		state->current_lease_state = l->current_state;
+		state->new_lease_state = l->breaking_to_state;
+		if (l->lease_version > 1) {
+			state->new_epoch = lease->lease_epoch;
+		}
+
+		tevent_schedule_immediate(state->im, xconn->ev_ctx,
+					  downgrade_lease_additional_trigger,
+					  state);
+	}
 
 	/*
 	 * This sucks. We have to reset fsp->sent_oplock_break on all fsps
@@ -346,6 +417,8 @@ NTSTATUS downgrade_lease(struct smbd_server_connection *sconn,
 	}
 
 	TALLOC_FREE(lck);
+	DEBUG(0, ("%s: Downgrading %s to %x => %s\n", __func__,
+		   file_id_string_tos(&id), (unsigned)lease_state, nt_errstr(status)));
 	return status;
 }
 
@@ -444,6 +517,11 @@ static void oplock_timeout_handler(struct tevent_context *ctx,
 {
 	files_struct *fsp = (files_struct *)private_data;
 
+	if (fsp->oplock_type == LEASE_OPLOCK) {
+		//TODO
+		SMB_ASSERT(false);
+	}
+
 	SMB_ASSERT(fsp->sent_oplock_break != NO_BREAK_SENT);
 
 	/* Remove the timed event handler. */
@@ -526,7 +604,9 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx,
 		struct smbd_server_connection);
 	struct server_id self = messaging_server_id(sconn->msg_ctx);
 	struct kernel_oplocks *koplocks = sconn->oplocks.kernel_ops;
+	uint16_t break_from;
 	uint16_t break_to;
+	bool break_needed = true;
 
 	if (data->data == NULL) {
 		DEBUG(0, ("Got NULL buffer\n"));
@@ -542,7 +622,7 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx,
 	message_to_share_mode_entry(&msg, (char *)data->data);
 	break_to = msg.op_type;
 
-	DEBUG(10, ("Got oplock break to %u message from pid %s: %s/%llu\n",
+	DEBUG(0, ("Got oplock break to %u message from pid %s: %s/%llu\n",
 		   (unsigned)break_to, server_id_str(talloc_tos(), &src),
 		   file_id_string_tos(&msg.id),
 		   (unsigned long long)msg.share_file_id));
@@ -556,13 +636,21 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx,
 		return;
 	}
 
-	if (fsp->sent_oplock_break != NO_BREAK_SENT) {
-		/*
-		 * Nothing to do anymore
-		 */
-		DEBUG(10, ("fsp->sent_oplock_break = %d\n",
-			   fsp->sent_oplock_break));
-		return;
+	if (fsp->oplock_timeout != NULL) {
+//		return;
+	}
+
+	break_from = fsp_lease_type(fsp);
+
+	if (fsp->oplock_type != LEASE_OPLOCK) {
+		if (fsp->sent_oplock_break != NO_BREAK_SENT) {
+			/*
+			 * Nothing to do anymore
+			 */
+			DEBUG(10, ("fsp->sent_oplock_break = %d\n",
+				   fsp->sent_oplock_break));
+			return;
+		}
 	}
 
 	if (!(global_client_caps & CAP_LEVEL_II_OPLOCKS)) {
@@ -581,30 +669,15 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx,
 		break_to &= ~SMB2_LEASE_READ;
 	}
 
-	DEBUG(10, ("msg.op_type=%u, break_to=%u\n",
-		   (unsigned)msg.op_type, (unsigned)break_to));
-
-	if (fsp->oplock_type == NO_OPLOCK) {
-		DEBUG(3, ("Already downgraded oplock to none on %s: %s\n",
-			  file_id_string_tos(&fsp->file_id),
-			  fsp_str_dbg(fsp)));
-		return;
-	}
-	if ((break_to & SMB2_LEASE_READ) &&
-	    (fsp->oplock_type == LEVEL_II_OPLOCK)) {
-		DEBUG(3, ("Already downgraded oplock to level2 on %s: %s\n",
-			  file_id_string_tos(&fsp->file_id),
-			  fsp_str_dbg(fsp)));
-		return;
-	}
-
 	if (fsp->oplock_type == LEASE_OPLOCK) {
 		struct share_mode_lock *lck;
 		int idx;
 
+		DEBUG(0,("%s: refresh lease state - begin\n", __func__));
 		lck = get_existing_share_mode_lock(
 			talloc_tos(), fsp->file_id);
 
+		DEBUG(0,("%s: refresh lease state - line(%d)\n", __func__, __LINE__));
 		idx = find_share_mode_oplock(
 			lck->data,
 			&fsp->conn->sconn->client->connections->
@@ -613,16 +686,78 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx,
 		if (idx != -1) {
 			struct share_mode_oplock *o;
 			o = &lck->data->leases[idx];
+
+		DEBUG(0,("%s: refresh lease state - line(%d)\n", __func__, __LINE__));
+			break_from = o->current_state;
+			break_to &= o->current_state;
+
+			if (o->breaking) {
+		DEBUG(0,("%s: refresh lease state - line(%d)\n", __func__, __LINE__));
+				break_to &= o->breaking_to_state;
+				if (o->breaking_to_state != break_to) {
+		DEBUG(0,("%s: refresh lease state - line(%d)\n", __func__, __LINE__));
+					o->breaking_to_state = break_to;
+					lck->data->modified = true;
+				}
+				break_needed = false;
+			} else if (o->current_state == break_to) {
+		DEBUG(0,("%s: refresh lease state - line(%d)\n", __func__, __LINE__));
+				break_needed = false;
+			} else if (o->current_state == SMB2_LEASE_READ) {
+		DEBUG(0,("%s: refresh lease state - line(%d)\n", __func__, __LINE__));
+				o->current_state = SMB2_LEASE_NONE;
+				lck->data->modified = true;
+			} else {
+		DEBUG(0,("%s: refresh lease state - line(%d)\n", __func__, __LINE__));
+				o->breaking = true;
+				o->breaking_to_state = break_to;
+				lck->data->modified = true;
+			}
+
+			/* Need to increment the epoch */
+			if (lck->data->modified) {
+		DEBUG(0,("%s: refresh lease state - line(%d)\n", __func__, __LINE__));
+				o->epoch += 1;
+			}
+
 			/* Ensure we're in sync with current lease state. */
 			fsp->lease->lease.lease_state = o->current_state;
-			/* Need to increment the epoch */
-			o->epoch += 1;
 			fsp->lease->lease.lease_epoch = o->epoch;
-			/* And remember to write it out again.. */
-			lck->data->modified = true;
+			if (o->breaking) {
+		DEBUG(0,("%s: refresh lease state - line(%d)\n", __func__, __LINE__));
+				fsp->lease->lease.lease_flags |= SMB2_LEASE_FLAG_BREAK_IN_PROGRESS;
+			} else {
+		DEBUG(0,("%s: refresh lease state - line(%d)\n", __func__, __LINE__));
+				fsp->lease->lease.lease_flags &= ~SMB2_LEASE_FLAG_BREAK_IN_PROGRESS;
+			}
 		}
 
+		DEBUG(0,("%s: refresh lease state - line(%d)\n", __func__, __LINE__));
 		TALLOC_FREE(lck);
+		DEBUG(0,("%s: refresh lease state - end\n", __func__));
+	}
+
+	if (!break_needed) {
+		DEBUG(0,("%s: skip break\n", __func__));
+		return;
+	}
+
+	if ((break_from == SMB2_LEASE_NONE) && !break_needed) {
+		DEBUG(3, ("Already downgraded oplock to none on %s: %s\n",
+			  file_id_string_tos(&fsp->file_id),
+			  fsp_str_dbg(fsp)));
+		return;
+	}
+
+	DEBUG(0, ("msg.op_type=%u, break_to=%u\n",
+		   (unsigned)msg.op_type, (unsigned)break_to));
+
+	if ((break_from == break_to) && !break_needed) {
+		DEBUG(3, ("Already downgraded oplock to %u on %s: %s\n",
+			  (unsigned)break_to,
+			  file_id_string_tos(&fsp->file_id),
+			  fsp_str_dbg(fsp)));
+		return;
 	}
 
 	/* Need to wait before sending a break
@@ -632,33 +767,31 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx,
 	}
 
 	if (sconn->using_smb2) {
-		send_break_message_smb2(fsp, break_to);
+		DEBUG(0,("%s: send_break_message_smb2 \n", __func__));
+		send_break_message_smb2(fsp, break_from, break_to);
 	} else {
 		send_break_message_smb1(fsp, (break_to & SMB2_LEASE_READ) ?
 					OPLOCKLEVEL_II : OPLOCKLEVEL_NONE);
 	}
 
-	if ((fsp_lease_type(fsp) == SMB2_LEASE_READ) &&
+	if ((break_from == SMB2_LEASE_READ) &&
 	    (break_to == SMB2_LEASE_NONE)) {
 		/*
 		 * This is an async break without a reply and thus no timeout
+		 *
+		 * leases are handled above.
 		 */
-		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 {
+		if (fsp->oplock_type != LEASE_OPLOCK) {
 			remove_oplock(fsp);
 		}
 		return;
 	}
-	fsp->sent_oplock_break = (break_to & SMB2_LEASE_READ) ?
-		LEVEL_II_BREAK_SENT:BREAK_TO_NONE_SENT;
+	if (fsp->oplock_type != LEASE_OPLOCK) {
+		fsp->sent_oplock_break = (break_to & SMB2_LEASE_READ) ?
+			LEVEL_II_BREAK_SENT:BREAK_TO_NONE_SENT;
+	}
 
+	DEBUG(0,("%s: add_oplock_timeout_handler \n", __func__));
 	add_oplock_timeout_handler(fsp);
 }
 
@@ -713,7 +846,7 @@ static void process_kernel_oplock_break(struct messaging_context *msg_ctx,
 	}
 
 	if (sconn->using_smb2) {
-		send_break_message_smb2(fsp, OPLOCKLEVEL_NONE);
+		send_break_message_smb2(fsp, 0, OPLOCKLEVEL_NONE);
 	} else {
 		send_break_message_smb1(fsp, OPLOCKLEVEL_NONE);
 	}
@@ -937,23 +1070,6 @@ done:
 	return;
 }
 
-bool break_in_progress(const files_struct *fsp)
-{
-	files_struct *new_fsp;
-
-	for (new_fsp = file_find_di_first(fsp->conn->sconn, fsp->file_id);
-			new_fsp != NULL;
-			new_fsp = file_find_di_next(new_fsp)) {
-		if (new_fsp == fsp) {
-			continue;
-		}
-		if (new_fsp->sent_oplock_break != NO_BREAK_SENT) {
-			return true;
-		}
-	}
-	return false;
-}
-
 void smbd_contend_level2_oplocks_begin(files_struct *fsp,
 				  enum level2_contention_type type)
 {
diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
index c8e404e..c4f8920 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -667,11 +667,10 @@ 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,
+NTSTATUS downgrade_lease(struct smbXsrv_connection *xconn,
 			 const struct file_id id,
-			 const struct smb2_lease_key *key,
+			 const struct smb2_lease *lease,
 			 uint32_t lease_state);
-bool break_in_progress(const files_struct *fsp);
 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/smb2_break.c b/source3/smbd/smb2_break.c
index 9ab118d..f9fce5a 100644
--- a/source3/smbd/smb2_break.c
+++ b/source3/smbd/smb2_break.c
@@ -354,8 +354,15 @@ static struct tevent_req *smbd_smb2_lease_break_send(
 		return tevent_req_post(req, ev);
 	}
 
-	status = downgrade_lease(smb2_req->sconn, fsp->file_id, &in_lease_key,
-				 in_lease_state);
+	status = downgrade_lease(smb2_req->xconn, fsp->file_id,
+				 &fsp->lease->lease, in_lease_state);
+	if (NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_BREAK_IN_PROGRESS)) {
+		/*
+		 * here we keep the fsp->oplock_timeout
+		 */
+		tevent_req_done(req);
+		return tevent_req_post(req, ev);
+	}
 	if (tevent_req_nterror(req, status)) {
 		DEBUG(10, ("downgrade_lease returned %s\n",
 			   nt_errstr(status)));
@@ -386,7 +393,9 @@ static NTSTATUS smbd_smb2_lease_break_recv(struct tevent_req *req,
  SMB2 OPLOCK_BREAK_NOTIFICATION.
 *********************************************************/
 
-void send_break_message_smb2(files_struct *fsp, uint32_t break_to)
+void send_break_message_smb2(files_struct *fsp,
+			     uint32_t break_from,
+			     uint32_t break_to)
 {
 	NTSTATUS status;
 	struct smbXsrv_connection *xconn = NULL;
@@ -425,11 +434,12 @@ void send_break_message_smb2(files_struct *fsp, uint32_t break_to)
 		(unsigned int)break_to ));
 
 	if (fsp->oplock_type == LEASE_OPLOCK) {
-		bool no_ack;
+		uint32_t break_flags = 0;
 		uint16_t new_epoch;
 
-		no_ack = ((fsp->lease->lease.lease_state == SMB2_LEASE_READ) &&
-			  (break_to == SMB2_LEASE_NONE));
+		if (fsp->lease->lease.lease_state != SMB2_LEASE_NONE) {
+			break_flags = SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED;
+		}
 
 		if (fsp->lease->lease.lease_version > 1) {
 			new_epoch = fsp->lease->lease.lease_epoch;
@@ -437,11 +447,9 @@ void send_break_message_smb2(files_struct *fsp, uint32_t break_to)
 			new_epoch = 0;
 		}
 
-		status = smbd_smb2_send_lease_break(
-			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);
+		status = smbd_smb2_send_lease_break(xconn, new_epoch, break_flags,
+						    &fsp->lease->lease.lease_key,
+						    break_from, break_to);
 	} else {
 		uint8_t smb2_oplock_level;
 		smb2_oplock_level = (break_to & SMB2_LEASE_READ) ?
diff --git a/source3/smbd/smb2_create.c b/source3/smbd/smb2_create.c
index 482bb66..91583fa 100644
--- a/source3/smbd/smb2_create.c
+++ b/source3/smbd/smb2_create.c
@@ -1204,14 +1204,6 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 			uint8_t buf[52];
 
 			lease = result->lease->lease;
-			lease.lease_flags &=
-				SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET;
-
-			/* Is there a break in progress ? */
-			if (break_in_progress(result)) {
-				lease.lease_flags |=
-					SMB2_LEASE_FLAG_BREAK_IN_PROGRESS;
-			}
 
 			lease_len = sizeof(buf);
 			if (lease.lease_version == 1) {
diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c
index 36ac0d7..16eed90 100644
--- a/source3/smbd/smb2_server.c
+++ b/source3/smbd/smb2_server.c
@@ -2760,14 +2760,19 @@ static NTSTATUS smbd_smb2_send_break(struct smbXsrv_connection *xconn,
 				     size_t body_len)
 {
 	struct smbd_smb2_send_break_state *state;
-	bool do_encryption = session->global->encryption_required;
+	bool do_encryption = false;
+	uint64_t session_wire_id = 0;
 	uint64_t nonce_high = 0;
 	uint64_t nonce_low = 0;
 	NTSTATUS status;
 	size_t statelen;
 
-	if (tcon->global->encryption_required) {
-		do_encryption = true;
+	if (session != NULL) {
+		session_wire_id = session->global->session_wire_id;
+		do_encryption = session->global->encryption_required;
+		if (tcon->global->encryption_required) {
+			do_encryption = true;
+		}
 	}
 
 	statelen = offsetof(struct smbd_smb2_send_break_state, body) +
@@ -2793,7 +2798,7 @@ static NTSTATUS smbd_smb2_send_break(struct smbXsrv_connection *xconn,
 	SIVAL(state->tf, SMB2_TF_PROTOCOL_ID, SMB2_TF_MAGIC);
 	SBVAL(state->tf, SMB2_TF_NONCE+0, nonce_low);
 	SBVAL(state->tf, SMB2_TF_NONCE+8, nonce_high);
-	SBVAL(state->tf, SMB2_TF_SESSION_ID, session->global->session_wire_id);
+	SBVAL(state->tf, SMB2_TF_SESSION_ID, session_wire_id);
 
 	SIVAL(state->hdr, 0,				SMB2_MAGIC);
 	SSVAL(state->hdr, SMB2_HDR_LENGTH,		SMB2_HDR_BODY);
@@ -2889,8 +2894,6 @@ NTSTATUS smbd_smb2_send_oplock_break(struct smbXsrv_connection *xconn,
 }
 
 NTSTATUS smbd_smb2_send_lease_break(struct smbXsrv_connection *xconn,
-				    struct smbXsrv_session *session,
-				    struct smbXsrv_tcon *tcon,
 				    uint16_t new_epoch,
 				    uint32_t lease_flags,
 				    struct smb2_lease_key *lease_key,
@@ -2910,7 +2913,7 @@ NTSTATUS smbd_smb2_send_lease_break(struct smbXsrv_connection *xconn,
 	SIVAL(body, 0x24, 0);		/* AccessMaskHint, MUST be 0 */
 	SIVAL(body, 0x28, 0);		/* ShareMaskHint, MUST be 0 */
 
-	return smbd_smb2_send_break(xconn, session, tcon, body, sizeof(body));
+	return smbd_smb2_send_break(xconn, NULL, NULL, body, sizeof(body));
 }
 
 static bool is_smb2_recvfile_write(struct smbd_smb2_request_read_state *state)
-- 
1.9.1


From 47ca1ad0bc82ab038f8f1026aeb794b4156e536f Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Fri, 14 Nov 2014 09:18:51 +0100
Subject: [PATCH 2/6] grant_fsp_lease don't upgrade when in breaking state

---
 source3/smbd/open.c | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index 33a2bf9..29bb463 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1660,6 +1660,11 @@ static NTSTATUS grant_fsp_lease(files_struct *fsp, struct share_mode_data *d,
 		 */
 		do_upgrade &= (granted == requested);
 
+		/*
+		 * only upgrade if we are not in breaking state
+		 */
+		do_upgrade &= !o->breaking;
+
 		DEBUG(10, ("existing=%"PRIu32", requested=%"PRIu32", "
 			   "granted=%"PRIu32", do_upgrade=%d\n",
 			   existing, requested, granted, (int)do_upgrade));
@@ -1667,9 +1672,16 @@ static NTSTATUS grant_fsp_lease(files_struct *fsp, struct share_mode_data *d,
 		if (do_upgrade) {
 			o->current_state = granted;
 			o->epoch += 1;
-			fsp->lease->lease.lease_epoch = o->epoch;
 		}
+
+		/* Ensure we're in sync with current lease state. */
 		fsp->lease->lease.lease_state = o->current_state;
+		fsp->lease->lease.lease_epoch = o->epoch;
+		if (o->breaking) {
+			fsp->lease->lease.lease_flags |= SMB2_LEASE_FLAG_BREAK_IN_PROGRESS;
+		} else {
+			fsp->lease->lease.lease_flags &= ~SMB2_LEASE_FLAG_BREAK_IN_PROGRESS;
+		}
 		return NT_STATUS_OK;
 	}
 
-- 
1.9.1


From 1c355eb1a8b653bdf0dbd4342422f53a035155ab Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Fri, 14 Nov 2014 03:18:32 +0100
Subject: [PATCH 3/6] SQ only copy the required fields to fsp->lease->lease

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

diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index 29bb463..a2a5de1 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1699,23 +1699,24 @@ static NTSTATUS grant_fsp_lease(files_struct *fsp, struct share_mode_data *d,
 	}
 	d->leases = tmp;
 
-	fsp->lease = talloc(fsp->conn->sconn, struct fsp_lease);
+	fsp->lease = talloc_zero(fsp->conn->sconn, struct fsp_lease);
 	if (fsp->lease == NULL) {
 		return NT_STATUS_INSUFFICIENT_RESOURCES;
 	}
 	fsp->lease->ref_count = 1;
-	fsp->lease->lease = *lease;
+	fsp->lease->lease.lease_version = lease->lease_version;
+	fsp->lease->lease.lease_key = lease->lease_key;
 	fsp->lease->lease.lease_state = granted;
-	fsp->lease->lease.lease_epoch += 1;
+	fsp->lease->lease.lease_epoch = 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 = fsp->lease->lease.lease_key,
+		.current_state = fsp->lease->lease.lease_state,
 		.lease_version = fsp->lease->lease.lease_version,
 		.epoch = fsp->lease->lease.lease_epoch,
-		.current_state = granted,
 	};
 
 	status = leases_db_add(client_guid, &lease->lease_key,
-- 
1.9.1


From 2c17e1ce00fdd93b4e959f35414c0adfd1d94d21 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Fri, 14 Nov 2014 03:10:16 +0100
Subject: [PATCH 4/6] TODO lease->lease_flags &=
 ~SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET not needed???

---
 libcli/smb/smb2_lease.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcli/smb/smb2_lease.c b/libcli/smb/smb2_lease.c
index 7705256..fc641ff 100644
--- a/libcli/smb/smb2_lease.c
+++ b/libcli/smb/smb2_lease.c
@@ -48,7 +48,6 @@ 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:
-- 
1.9.1


From 021abf204660b82484e76dad60f754d68826c049 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Fri, 14 Nov 2014 08:56:38 +0100
Subject: [PATCH 5/6] delay_for_oplock only read...

---
 source3/smbd/open.c | 116 +++++++++++++++++++++-------------------------------
 1 file changed, 47 insertions(+), 69 deletions(-)

diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index a2a5de1..5b13bf7 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1416,41 +1416,6 @@ static bool delay_for_oplock(files_struct *fsp,
 		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(fsp, d, e, lease)) {
-				continue;
-			}
-			if (share_mode_stale_pid(d, i)) {
-				continue;
-			}
-
-			//if (e->op_type == LEASE_OPLOCK) {
-			//	struct share_mode_oplock *o = NULL;
-			//	o = &d->leases[e->lease_idx];
-			//	if (o->breaking) {
-			//		continue;
-			//	}
-			//}
-			send_break_message(fsp->conn->sconn->msg_ctx, e,
-					   e_lease_type & ~SMB2_LEASE_HANDLE);
-			have_broken = true;
-		}
-	}
-
-	if (have_broken) {
-		return true;
-	}
-	if (have_sharing_violation) {
-		return false;
-	}
-
 	switch (create_disposition) {
 	case FILE_SUPERSEDE:
 	case FILE_OVERWRITE:
@@ -1464,56 +1429,69 @@ static bool delay_for_oplock(files_struct *fsp,
 
 	for (i=0; i<d->num_share_modes; i++) {
 		struct share_mode_entry *e = &d->share_modes[i];
+		struct share_mode_oplock *o = NULL;
 		uint32_t e_lease_type = get_lease_type(d, e);
+		uint32_t break_to;
+
+		if (e->op_type == LEASE_OPLOCK) {
+			o = &d->leases[e->lease_idx];
+		}
+
+		if (have_sharing_violation) {
+			break_to = e_lease_type & ~SMB2_LEASE_HANDLE;
+		} else {
+			break_to = e_lease_type & ~SMB2_LEASE_WRITE;
+		}
+
+		if (e->op_type != LEASE_OPLOCK) {
+			break_to &= ~(SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
+		}
 
 		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 (lease != NULL && o != NULL) {
+			bool ign;
 
-			if (share_mode_stale_pid(d, i)) {
-				return false;
+			ign = smb2_lease_equal(fsp_client_guid(fsp),
+					       &lease->lease_key,
+					       &o->client_guid,
+					       &o->lease_key);
+			if (ign) {
+				continue;
 			}
-			if ((e->op_type == LEASE_OPLOCK) &&
-			    (lease != NULL) &&
-			    smb2_lease_equal(fsp_client_guid(fsp),
-					     &lease->lease_key,
-					     &d->leases[e->lease_idx].client_guid,
-					     &d->leases[e->lease_idx].lease_key)) {
-				return false;
+		}
+
+		if ((e_lease_type & ~break_to) == 0) {
+			if (o == NULL) {
+				continue;
 			}
 
-			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;
+			if (o->breaking) {
+				have_broken = true;
 			}
+			continue;
+		}
 
-			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;
+		if (share_mode_stale_pid(d, i)) {
+			continue;
 		}
 
-		if (will_overwrite && (e_lease_type & SMB2_LEASE_READ)) {
-			if (share_mode_stale_pid(d, i)) {
-				continue;
+		if (will_overwrite) {
+			if (e->op_type == LEASE_OPLOCK) {
+				break_to &= ~SMB2_LEASE_HANDLE;
+			} else {
+				break_to &= ~SMB2_LEASE_READ;
 			}
-			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, ("breaking SMB2_LEASE_WRITE to %d\n",
+			   (int)break_to));
+		send_break_message(fsp->conn->sconn->msg_ctx, e,
+				   break_to);
+		have_broken = true;
+		continue;
 	}
 
 	return have_broken;
-- 
1.9.1


From 5936a0f5f9b0f33cbeb759e3f98a144a919f63f7 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Fri, 14 Nov 2014 09:24:14 +0100
Subject: [PATCH 6/6] Revert "skip test_lease_breaking2"

This reverts commit 3a9ca0328059284c8c1e2619ab44acdee73bce62.
---
 source4/torture/smb2/lease.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c
index 2b47611..221d45c 100644
--- a/source4/torture/smb2/lease.c
+++ b/source4/torture/smb2/lease.c
@@ -2373,7 +2373,7 @@ struct torture_suite *torture_smb2_lease_init(void)
 	torture_suite_add_1smb2_test(suite, "oplock", test_lease_oplock);
 	torture_suite_add_1smb2_test(suite, "multibreak", test_lease_multibreak);
 	torture_suite_add_1smb2_test(suite, "breaking1", test_lease_breaking1);
-	//torture_suite_add_1smb2_test(suite, "breaking2", test_lease_breaking2);
+	torture_suite_add_1smb2_test(suite, "breaking2", test_lease_breaking2);
 	torture_suite_add_1smb2_test(suite, "complex1", test_lease_complex1);
 	torture_suite_add_1smb2_test(suite, "v2_request_parent",
 				     test_lease_v2_request_parent);
-- 
1.9.1

-------------- next part --------------
From f905e89dc94ac08d314693a74655740915dd5d20 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Fri, 14 Nov 2014 09:50:32 -0800
Subject: [PATCH 1/4] s4: leases tests: Still need to ignore alloc_size.

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 source4/torture/smb2/lease.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c
index 221d45c..24c6544 100644
--- a/source4/torture/smb2/lease.c
+++ b/source4/torture/smb2/lease.c
@@ -46,7 +46,9 @@
 #define CHECK_CREATED(__io, __created, __attribute)			\
 	do {								\
 		CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
-		CHECK_VAL((__io)->out.alloc_size, 0);			\
+		if (!TARGET_IS_SAMBA3(tctx)) {                          \
+			CHECK_VAL((__io)->out.alloc_size, 0);           \
+		}                                                       \
 		CHECK_VAL((__io)->out.size, 0);				\
 		CHECK_VAL((__io)->out.file_attr, (__attribute));	\
 		CHECK_VAL((__io)->out.reserved2, 0);			\
-- 
1.9.1


From 252bb97f0da1be531c4da1a1b09235ab46d4c775 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Fri, 14 Nov 2014 12:03:16 -0800
Subject: [PATCH 2/4] s3: leases - correctly cope with a client not responding
 to a lease break request.

Just downgrade to the to_break value anyway.

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 source3/locking/locking.c |  2 +-
 source3/smbd/oplock.c     | 60 +++++++++++++++++++++++++++++++++++++++--------
 2 files changed, 51 insertions(+), 11 deletions(-)

diff --git a/source3/locking/locking.c b/source3/locking/locking.c
index b3ab9ad..a559c5f 100644
--- a/source3/locking/locking.c
+++ b/source3/locking/locking.c
@@ -1015,7 +1015,7 @@ NTSTATUS downgrade_share_lease(struct smbd_server_connection *sconn,
 	if (!l->breaking) {
 		DEBUG(0, ("Attempt to break from %d to %d - but we're not in breaking state\n",
 			   (int)l->current_state, (int)new_lease_state));
-		return NT_STATUS_INVALID_OPLOCK_PROTOCOL;
+		return NT_STATUS_UNSUCCESSFUL;
 	}
 
 	/*
diff --git a/source3/smbd/oplock.c b/source3/smbd/oplock.c
index 240ff59..5c0d592 100644
--- a/source3/smbd/oplock.c
+++ b/source3/smbd/oplock.c
@@ -510,6 +510,32 @@ static files_struct *initial_break_processing(
 	return fsp;
 }
 
+struct lease_timeout_state {
+	files_struct *fsp;
+	uint16_t break_to;
+};
+
+static void lease_timeout_handler(struct tevent_context *ctx,
+				   struct tevent_timer *te,
+				   struct timeval now,
+				   void *private_data)
+{
+	struct lease_timeout_state *lts =
+		(struct lease_timeout_state *)private_data;
+
+	DEBUG(0, ("lease break failed for file %s -- allowing anyway\n",
+		  fsp_str_dbg(lts->fsp)));
+	(void)downgrade_lease(lts->fsp->conn->sconn->client->connections,
+			lts->fsp->file_id,
+			&lts->fsp->lease->lease,
+			lts->break_to);
+	/*
+	 * Remove the timed event handler.
+	 * Must do this last as lts is hung off it.
+	 */
+	TALLOC_FREE(lts->fsp->oplock_timeout);
+}
+
 static void oplock_timeout_handler(struct tevent_context *ctx,
 				   struct tevent_timer *te,
 				   struct timeval now,
@@ -517,11 +543,6 @@ static void oplock_timeout_handler(struct tevent_context *ctx,
 {
 	files_struct *fsp = (files_struct *)private_data;
 
-	if (fsp->oplock_type == LEASE_OPLOCK) {
-		//TODO
-		SMB_ASSERT(false);
-	}
-
 	SMB_ASSERT(fsp->sent_oplock_break != NO_BREAK_SENT);
 
 	/* Remove the timed event handler. */
@@ -535,7 +556,7 @@ static void oplock_timeout_handler(struct tevent_context *ctx,
  Add a timeout handler waiting for the client reply.
 *******************************************************************/
 
-static void add_oplock_timeout_handler(files_struct *fsp)
+static void add_oplock_timeout_handler(files_struct *fsp, uint16_t break_to)
 {
 	struct smbd_server_connection *sconn = fsp->conn->sconn;
 	struct kernel_oplocks *koplocks = sconn->oplocks.kernel_ops;
@@ -554,10 +575,29 @@ static void add_oplock_timeout_handler(files_struct *fsp)
 			  "around\n"));
 	}
 
-	fsp->oplock_timeout =
-		tevent_add_timer(fsp->conn->sconn->ev_ctx, fsp,
+	if (fsp->oplock_type == LEASE_OPLOCK) {
+		struct lease_timeout_state *lts = talloc_zero(fsp,
+						struct lease_timeout_state);
+		if (lts == NULL) {
+			return;
+		}
+		lts->fsp = fsp;
+		lts->break_to = break_to;
+		fsp->oplock_timeout =
+			tevent_add_timer(fsp->conn->sconn->ev_ctx, fsp,
+				 timeval_current_ofs(OPLOCK_BREAK_TIMEOUT, 0),
+				 lease_timeout_handler, lts);
+		if (fsp->oplock_timeout == NULL) {
+			talloc_free(lts);
+		} else {
+			talloc_move(fsp->oplock_timeout, &lts);
+		}
+	} else {
+		fsp->oplock_timeout =
+			tevent_add_timer(fsp->conn->sconn->ev_ctx, fsp,
 				 timeval_current_ofs(OPLOCK_BREAK_TIMEOUT, 0),
 				 oplock_timeout_handler, fsp);
+	}
 
 	if (fsp->oplock_timeout == NULL) {
 		DEBUG(0, ("Could not add oplock timeout handler\n"));
@@ -792,7 +832,7 @@ static void process_oplock_break_message(struct messaging_context *msg_ctx,
 	}
 
 	DEBUG(0,("%s: add_oplock_timeout_handler \n", __func__));
-	add_oplock_timeout_handler(fsp);
+	add_oplock_timeout_handler(fsp, break_to);
 }
 
 /*******************************************************************
@@ -853,7 +893,7 @@ static void process_kernel_oplock_break(struct messaging_context *msg_ctx,
 
 	fsp->sent_oplock_break = BREAK_TO_NONE_SENT;
 
-	add_oplock_timeout_handler(fsp);
+	add_oplock_timeout_handler(fsp, OPLOCKLEVEL_NONE);
 }
 
 struct break_to_none_state {
-- 
1.9.1


From 62692fd11151cbe566d4ad71f7268b44dbc82510 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Fri, 14 Nov 2014 10:24:40 -0800
Subject: [PATCH 3/4] s3: leases - torture test for timeout of responding to
 lease break request.

Passes against W2K12.

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

diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c
index 24c6544..fa50949 100644
--- a/source4/torture/smb2/lease.c
+++ b/source4/torture/smb2/lease.c
@@ -2358,6 +2358,97 @@ static bool test_lease_v2_complex1(struct torture_context *tctx,
 	return ret;
 }
 
+static bool test_lease_timeout(struct torture_context *tctx,
+                               struct smb2_tree *tree)
+{
+	TALLOC_CTX *mem_ctx = talloc_new(tctx);
+	struct smb2_create io;
+	struct smb2_lease ls1;
+	struct smb2_lease ls2;
+	struct smb2_handle h, hnew;
+	NTSTATUS status;
+	const char *fname = "lease_timeout.dat";
+	bool ret = true;
+	struct smb2_lease_break_ack ack = {};
+	struct smb2_request *req2 = NULL;
+	struct smb2_write w;
+	uint32_t caps;
+
+	caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
+	if (!(caps & SMB2_CAP_LEASING)) {
+		torture_skip(tctx, "leases are not supported");
+	}
+
+	smb2_util_unlink(tree, fname);
+
+	/* Grab a RWH lease. */
+	smb2_lease_create(&io, &ls1, false, fname, LEASE1, smb2_util_lease_state("RWH"));
+	status = smb2_create(tree, mem_ctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE(&io, "RWH", true, LEASE1, 0);
+	h = io.out.file.handle;
+
+	tree->session->transport->lease.handler	= torture_lease_handler;
+	tree->session->transport->lease.private_data = tree;
+	tree->session->transport->oplock.handler = torture_oplock_handler;
+	tree->session->transport->oplock.private_data = tree;
+
+	/*
+	 * Just don't ack the lease break.
+	 */
+	ZERO_STRUCT(break_info);
+	break_info.lease_skip_ack = true;
+
+	/* Break with a RWH request. */
+	smb2_lease_create(&io, &ls2, false, fname, LEASE2, smb2_util_lease_state("RWH"));
+	req2 = smb2_create_send(tree, &io);
+	torture_assert(tctx, req2 != NULL, "smb2_create_send");
+	torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending");
+
+	CHECK_BREAK_INFO("RWH", "RH", LEASE1);
+
+	/* Copy the break request. */
+	ack.in.lease.lease_key =
+		break_info.lease_break.current_lease.lease_key;
+	ack.in.lease.lease_state =
+		break_info.lease_break.new_lease_state;
+
+	/* Now wait for the timeout and get the reply. */
+	status = smb2_create_recv(req2, tctx, &io);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE(&io, "RH", true, LEASE2, 0);
+	hnew = io.out.file.handle;
+
+	/* Ack the break after the timeout... */
+	status = smb2_lease_break_ack(tree, &ack);
+	CHECK_STATUS(status, NT_STATUS_UNSUCCESSFUL);
+
+	/* Write on the original handle and make sure it's still valid. */
+	ZERO_STRUCT(break_info);
+	ZERO_STRUCT(w);
+	w.in.file.handle = h;
+	w.in.offset      = 0;
+	w.in.data        = data_blob_talloc(mem_ctx, NULL, 4096);
+	memset(w.in.data.data, 'o', w.in.data.length);
+	status = smb2_write(tree, &w);
+	CHECK_STATUS(status, NT_STATUS_OK);
+
+	CHECK_BREAK_INFO("RH", "", LEASE2);
+
+ done:
+	smb2_util_close(tree, h);
+	smb2_util_close(tree, hnew);
+
+	smb2_util_unlink(tree, fname);
+
+	talloc_free(mem_ctx);
+
+	return ret;
+}
+
+
 struct torture_suite *torture_smb2_lease_init(void)
 {
 	struct torture_suite *suite =
@@ -2384,6 +2475,7 @@ struct torture_suite *torture_smb2_lease_init(void)
 	torture_suite_add_1smb2_test(suite, "v2_epoch2", test_lease_v2_epoch2);
 	torture_suite_add_1smb2_test(suite, "v2_epoch3", test_lease_v2_epoch3);
 	torture_suite_add_1smb2_test(suite, "v2_complex1", test_lease_v2_complex1);
+	torture_suite_add_1smb2_test(suite, "timeout", test_lease_timeout);
 
 	suite->description = talloc_strdup(suite, "SMB2-LEASE tests");
 
-- 
1.9.1


From 296b9eb6a3cba4d926b233a2aec9210e6a67f8e8 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Fri, 14 Nov 2014 20:17:40 -0800
Subject: [PATCH 4/4] s3: leases - fix raw.oplock and smb2.oplock tests.

For existing non-lease entries, remove SMB2_LEASE_HANDLE *only* from
break_to, not SMB2_LEASE_WRITE as well.

Signed-off-by: Jeremy Allison <jra at samba.org>
---
 source3/smbd/open.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index 5b13bf7..b62daeb 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -1444,7 +1444,7 @@ static bool delay_for_oplock(files_struct *fsp,
 		}
 
 		if (e->op_type != LEASE_OPLOCK) {
-			break_to &= ~(SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
+			break_to &= ~SMB2_LEASE_HANDLE;
 		}
 
 		DEBUG(10, ("entry %u: e_lease_type %u, will_overwrite: %u\n",
-- 
1.9.1



More information about the samba-technical mailing list