Latest leases patchset - getting there !

Stefan (metze) Metzmacher metze at samba.org
Wed Nov 12 17:17:43 MST 2014


Hi Jeremy,

>>>> here's ontop13.diff.txt with some more fixes
>>>> and new tests which demonstrate a problem
>>>> of outdated fsp->lease information if a lease is shared
>>>> between two smbd processes.
>>>
>>> Here is a patch on top of ontop13.diff.txt which
>>> fixes us for smb2.lease.complex1.
>>
>> OK, here's a patchset that includes this previous
>> patch and fixes both issues. It should apply
>> cleanly on top of ontop13.diff.txt.
>>
>> The realization was that we need to internally
>> treat all leases as v2, and update the epoch
>> correctly no matter whether the client was
>> using v1 or v2 lease requests.
>>
>> If the client requested a v1 lease just null
>> out the epoch value before marshalling and
>> sending back in the create reply.
>>
>> Please review and maybe squash these into
>> the existing leases patch.
> 
> Slightly updated version of this patchset.
> Only the middle (second) patch is changed.
> 
> Changes from metze-wip-15.patchset were:
> 
> 1). Inside find_fsp_lease(), when allocating
> a new smb2_lease struct, always set the
> lease_version field to 2, as we are always
> updating the epoch. Correct version will
> be set when marshalling the lease reply.
> 
> 2). Removed the change to smb2_create.c.
> Existing marshalling code in smb2_lease_push()
> already does the correct versioning based
> on length.
> 
> Please squash into existing patchset !

I've integrated this with a few more tests we fail (sorry...:-)

ontop17.diff.txt replaces the older ontop13.diff.txt
and also includes your changes.

I'll try to add a samba only regression test for the homes share case
next.

BTW: I think the leasedb would be the correct place to store if
the current lease is v1 or v2 (see the v2_epoch[2|3] tests).

metze
-------------- next part --------------
From fcce48a320df7220c2ad9760a569ad8b5d9d32de 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 01/24] 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 e87aa86d4c9167a316e6e920a8454381f7ceff28 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 02/24] 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 7fbc0cf4f3d79b083435bbdec0f9bc817cbc74c4 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 03/24] 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 68d477c825e982d33d5391ece90e6fef048fce8c 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 04/24] 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 d263d4932ef80c99c4f1984078b5a58487a561c0 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 05/24] 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 3705b75c62cd372ea4a0c50333b5d4968775fb5b 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 06/24] 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 5eb92f9f03c0ab5e3b1d3bd5129561f3b3875dd7 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 07/24] 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 61291950dd0d120d214a8a5450ec360d303122ff 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 08/24] 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 08eb7f0dd5ea843b7b30bdcfddbd012d32d9a087 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 09/24] 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 f8324e819a4a968ba5f518bc2fba3f398fe86959 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 10/24] 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 549dbaab4bca2f33499070fb98277d4f37559dbc 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 11/24] 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 7f84e32b185aa48a934df57c4dec92d19eba65ba 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 12/24] 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 87bb7f777253e6b09a13895c92b53c05a9558f25 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 13/24] 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 a337fd49f705a96879e15c02ff8bb66b1d02b94b 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 14/24] 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 a0cd462c561442e1a827a2c037eedf67fba9d38e 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 15/24] 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 984c54bd92ca44ce017b799546a7e4bdcccaa5b6 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 16/24] 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 03a3ecddcf28b6fc6bb0164b6398db65a77dd374 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 17/24] 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 32971be82558325a2c748237203640ce54f2cc41 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 18/24] 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 650612b836a98334afd7b0701a9d8caf21c17314 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 19/24] 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 11131f8818e3b1af71d881e58419bb9771da1d9e 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 20/24] 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 50f6c2488b0933c1b8cca1be279021780a4d6e02 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 21/24] 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 | 116 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 116 insertions(+)

diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c
index b9df9ba..405a96e 100644
--- a/source4/torture/smb2/lease.c
+++ b/source4/torture/smb2/lease.c
@@ -1778,6 +1778,121 @@ 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, tevent_req_is_in_progress(req2->subreq),
+		       "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, tevent_req_is_in_progress(req2->subreq),
+		       "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 +2180,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 52c603d6e7bfbc591380daa014cbad30845e2aec 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 22/24] 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 | 199 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 199 insertions(+)

diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c
index 405a96e..c3dca46 100644
--- a/source4/torture/smb2/lease.c
+++ b/source4/torture/smb2/lease.c
@@ -1893,6 +1893,204 @@ 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, tevent_req_is_in_progress(req2->subreq),
+		       "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, tevent_req_is_in_progress(req3->subreq),
+		       "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, tevent_req_is_in_progress(req2->subreq),
+		       "req2 pending");
+	torture_assert(tctx, tevent_req_is_in_progress(req3->subreq),
+		       "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)
 {
@@ -2181,6 +2379,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 0f6f8161d8438cb9e24b5fbfe9ea3b2cbd8f4715 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 23/24] NEEDED? s4:torture:smb2: ignore the allocation size in
 the create return against samba3

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

diff --git a/source4/torture/smb2/durable_open.c b/source4/torture/smb2/durable_open.c
index c3d63d1..7e1e6a2 100644
--- a/source4/torture/smb2/durable_open.c
+++ b/source4/torture/smb2/durable_open.c
@@ -25,6 +25,7 @@
 #include "libcli/smb2/smb2_calls.h"
 #include "../libcli/smb/smbXcli_base.h"
 #include "torture/torture.h"
+#include "torture/util.h"
 #include "torture/smb2/proto.h"
 #include "../libcli/smb/smbXcli_base.h"
 
@@ -53,7 +54,9 @@
 #define CHECK_CREATED(__io, __created, __attribute)			\
 	do {								\
 		CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
-		CHECK_VAL((__io)->out.alloc_size, 0);			\
+		if (!TARGET_IS_SAMBA3(tctx)) { \
+			CHECK_VAL((__io)->out.alloc_size, 0);		\
+		} \
 		CHECK_VAL((__io)->out.size, 0);				\
 		CHECK_VAL((__io)->out.file_attr, (__attribute));	\
 		CHECK_VAL((__io)->out.reserved2, 0);			\
diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c
index c3dca46..c56ac8e 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 3fe80a8afec412bf1d2f398157589343eab32e0b Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Wed, 12 Nov 2014 09:19:19 +0100
Subject: [PATCH 24/24] Revert "NEEDED? s4:torture:smb2: ignore the allocation
 size in the create return against samba3"

This reverts commit 9b90f0864485ddead49956c48ef3a1c1aedbd122.
---
 source4/torture/smb2/durable_open.c | 5 +----
 source4/torture/smb2/lease.c        | 4 +---
 2 files changed, 2 insertions(+), 7 deletions(-)

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

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


More information about the samba-technical mailing list