[SCM] Samba Shared Repository - branch master updated

Ralph Böhme slow at samba.org
Thu Oct 31 12:48:02 UTC 2024


The branch, master has been updated
       via  efbbe8d6f80 smbd: fix breaking leases on rename
       via  bc2d8798196 smbd: force sync rename with lease break
       via  a5635791cfd smbd: return correct error for compound related requests that went async
       via  3890ac2fafc smbtorture: test rename with other opens on the file
       via  42e739ab62c smbtorture: add a bunch of tests for async rename and async interim responses
       via  4932b433ff2 smbtorture: rename CHECK_VALUE() to CHECK_VAL() in smb2/compound.c
      from  4f3005f851a netcmd: More explicit warning when python-gpg is missing

https://git.samba.org/?p=samba.git;a=shortlog;h=master


- Log -----------------------------------------------------------------
commit efbbe8d6f80ceb6107f20486623eee949409c0ff
Author: Ralph Boehme <slow at samba.org>
Date:   Thu Oct 10 19:29:09 2024 +0200

    smbd: fix breaking leases on rename
    
    We must also break leases on other opens if the open of the rename doesn't have
    a lease itself. The existing test test_lease_v2_rename() that was added
    alongside the deferred rename server code didn't cover this case.
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=15697
    
    Signed-off-by: Ralph Boehme <slow at samba.org>
    Reviewed-by: Stefan Metzmacher <metze at samba.org>
    
    Autobuild-User(master): Ralph Böhme <slow at samba.org>
    Autobuild-Date(master): Thu Oct 31 12:47:24 UTC 2024 on atb-devel-224

commit bc2d87981967bc65155ba09eb5b3e3f913bec50e
Author: Ralph Boehme <slow at samba.org>
Date:   Thu Oct 10 19:25:30 2024 +0200

    smbd: force sync rename with lease break
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=15697
    
    Signed-off-by: Ralph Boehme <slow at samba.org>
    Reviewed-by: Stefan Metzmacher <metze at samba.org>

commit a5635791cfdb10f64bf2bf7c72c58f7591249a0d
Author: Ralph Boehme <slow at samba.org>
Date:   Mon Aug 26 10:48:34 2024 +0200

    smbd: return correct error for compound related requests that went async
    
    For a compound related request chain of eg CREATE+NOTIFY+GETINFO, the NOTIFY
    will typically go async. When this is noted in smbd_smb2_request_pending_queue()
    the pending async tevent_req is cancelled which means we return
    NT_STATUS_CANCELLED to the client while Windows returns
    NT_STATUS_INTERNAL_ERROR.
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=15697
    
    Signed-off-by: Ralph Boehme <slow at samba.org>
    Reviewed-by: Stefan Metzmacher <metze at samba.org>

commit 3890ac2fafc5e17919fa39542440a05ef72a3fa5
Author: Ralph Boehme <slow at samba.org>
Date:   Sat Sep 21 01:28:07 2024 +0200

    smbtorture: test rename with other opens on the file
    
    Windows allows this. Samba also already implements this correctly.
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=15697
    
    Signed-off-by: Ralph Boehme <slow at samba.org>
    Reviewed-by: Stefan Metzmacher <metze at samba.org>

commit 42e739ab62cb573d72215737027cf3c7f1fcd212
Author: Ralph Boehme <slow at samba.org>
Date:   Thu Oct 17 17:45:26 2024 +0200

    smbtorture: add a bunch of tests for async rename and async interim responses
    
    All tests pass against Windows 2022, we have some bugs.
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=15697
    
    Signed-off-by: Ralph Boehme <slow at samba.org>
    Reviewed-by: Stefan Metzmacher <metze at samba.org>

commit 4932b433ff2f1c4e603073624a5d22140acfb2ed
Author: Ralph Boehme <slow at samba.org>
Date:   Thu Oct 17 17:44:13 2024 +0200

    smbtorture: rename CHECK_VALUE() to CHECK_VAL() in smb2/compound.c
    
    Prepares for using macros from lease_break_handler.h which makes use of
    CHECK_VAL() while relying on a definition of CHECK_VAL() in the .c file.
    
    While at it, add a goto done which is always a good thing to get clear failures
    from tests.
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=15697
    
    Signed-off-by: Ralph Boehme <slow at samba.org>
    Reviewed-by: Stefan Metzmacher <metze at samba.org>

-----------------------------------------------------------------------

Summary of changes:
 selftest/knownfail              |   2 -
 source3/smbd/smb2_server.c      |  10 +
 source3/smbd/smb2_setinfo.c     |  10 +-
 source4/torture/smb2/compound.c | 905 +++++++++++++++++++++++++++++++++++++++-
 source4/torture/smb2/rename.c   |  72 ++++
 5 files changed, 984 insertions(+), 15 deletions(-)


Changeset truncated at 500 lines:

diff --git a/selftest/knownfail b/selftest/knownfail
index 31e70a1a9d3..5f64e4edad0 100644
--- a/selftest/knownfail
+++ b/selftest/knownfail
@@ -215,8 +215,6 @@
 ^samba3.smb2.getinfo.fsinfo # quotas don't work yet
 ^samba3.smb2.setinfo.setinfo
 ^samba3.smb2.session.*reauth5 # some special anonymous checks?
-^samba3.smb2.compound.interim2 # wrong return code (STATUS_CANCELLED)
-^samba3.smb2.compound.aio.interim2 # wrong return code (STATUS_CANCELLED)
 ^samba3.smb2.lock.*replay_broken_windows # This tests the windows behaviour
 ^samba3.smb2.lease.unlink # we currently do not downgrade RH lease to R after unlink
 ^samba4.smb2.ioctl.compress_notsup.*\(ad_dc_ntvfs\)
diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c
index b37829e8c4f..287d9844e84 100644
--- a/source3/smbd/smb2_server.c
+++ b/source3/smbd/smb2_server.c
@@ -4076,6 +4076,16 @@ NTSTATUS smbd_smb2_request_error_ex(struct smbd_smb2_request *req,
 		}
 	}
 
+	if (req->compound_related &&
+	    NT_STATUS_EQUAL(status, NT_STATUS_CANCELLED))
+	{
+		/*
+		 * A compound request went async but was cancelled as it was not
+		 * one of the allowed async compound requests.
+		 */
+		status = NT_STATUS_INTERNAL_ERROR;
+	}
+
 	body.data = outhdr + SMB2_HDR_BODY;
 	body.length = 8;
 	SSVAL(body.data, 0, 9);
diff --git a/source3/smbd/smb2_setinfo.c b/source3/smbd/smb2_setinfo.c
index dd0ba880fd1..9f04d020c4f 100644
--- a/source3/smbd/smb2_setinfo.c
+++ b/source3/smbd/smb2_setinfo.c
@@ -120,7 +120,11 @@ NTSTATUS smbd_smb2_request_process_setinfo(struct smbd_smb2_request *req)
 	}
 	tevent_req_set_callback(subreq, smbd_smb2_request_setinfo_done, req);
 
-	return smbd_smb2_request_pending_queue(req, subreq, 500);
+	/*
+	 * Windows never sends async interim responses if a rename triggers a
+	 * lease break. See test smb2.lease.compound_rename_middle.
+	 */
+	return smbd_smb2_request_pending_queue(req, subreq, 0);
 }
 
 static void smbd_smb2_request_setinfo_done(struct tevent_req *subreq)
@@ -238,10 +242,6 @@ static struct tevent_req *delay_rename_for_lease_break(struct tevent_req *req,
 	struct timeval timeout;
 	bool ok;
 
-	if (fsp->oplock_type != LEASE_OPLOCK) {
-		return NULL;
-	}
-
 	ok = share_mode_forall_leases(
 		lck, delay_rename_lease_break_fn, &state);
 	if (!ok) {
diff --git a/source4/torture/smb2/compound.c b/source4/torture/smb2/compound.c
index 175069d54ee..622283a7291 100644
--- a/source4/torture/smb2/compound.c
+++ b/source4/torture/smb2/compound.c
@@ -28,6 +28,7 @@
 #include "libcli/security/security.h"
 #include "librpc/gen_ndr/ndr_security.h"
 #include "../libcli/smb/smbXcli_base.h"
+#include "lease_break_handler.h"
 
 #define CHECK_STATUS(status, correct) do { \
 	if (!NT_STATUS_EQUAL(status, correct)) { \
@@ -37,14 +38,59 @@
 		goto done; \
 	}} while (0)
 
-#define CHECK_VALUE(v, correct) do { \
+#define CHECK_VAL(v, correct) do { \
 	if ((v) != (correct)) { \
 		torture_result(tctx, TORTURE_FAIL, \
 		    "(%s) Incorrect value %s=%d - should be %d\n", \
 		    __location__, #v, (int)v, (int)correct); \
 		ret = false; \
+		goto done; \
 	}} while (0)
 
+#define CHECK_LEASE(__io, __state, __oplevel, __key, __flags)		\
+	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)); \
+			CHECK_VAL((__io)->out.lease_response.lease_key.data[1], ~(__key)); \
+			CHECK_VAL((__io)->out.lease_response.lease_state, smb2_util_lease_state(__state)); \
+		} else {						\
+			CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); \
+			CHECK_VAL((__io)->out.lease_response.lease_key.data[0], 0); \
+			CHECK_VAL((__io)->out.lease_response.lease_key.data[1], 0); \
+			CHECK_VAL((__io)->out.lease_response.lease_state, 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)
+
+#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)); \
+			CHECK_VAL((__io)->out.lease_response_v2.lease_key.data[1], ~(__key)); \
+			CHECK_VAL((__io)->out.lease_response_v2.lease_state, smb2_util_lease_state(__state)); \
+		} else {						\
+			CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); \
+			CHECK_VAL((__io)->out.lease_response_v2.lease_key.data[0], 0); \
+			CHECK_VAL((__io)->out.lease_response_v2.lease_key.data[1], 0); \
+			CHECK_VAL((__io)->out.lease_response_v2.lease_state, 0); \
+		}							\
+									\
+		CHECK_VAL((__io)->out.lease_response_v2.lease_flags, __flags); \
+		if (__flags & SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET) { \
+			CHECK_VAL((__io)->out.lease_response_v2.parent_lease_key.data[0], (__parent)); \
+			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)
+
 #define WAIT_FOR_ASYNC_RESPONSE(req) \
 	while (!req->cancel.can_cancel && req->state <= SMB2_REQUEST_RECV) { \
 		if (tevent_loop_once(tctx->ev) != 0) { \
@@ -52,6 +98,9 @@
 		} \
 	}
 
+static const uint64_t LEASE1 = 0xBADC0FFEE0DDF00Dull;
+static const uint64_t LEASE2 = 0xDEADBEEFFEEDBEADull;
+
 static struct {
 	struct smb2_handle handle;
 	uint8_t level;
@@ -1191,7 +1240,7 @@ static bool test_compound_padding(struct torture_context *tctx,
 	 * size must be 24: 16 byte read response header plus 3
 	 * requested bytes padded to an 8 byte boundary.
 	 */
-	CHECK_VALUE(req[1]->in.body_size, 24);
+	CHECK_VAL(req[1]->in.body_size, 24);
 
 	status = smb2_read_recv(req[1], tree, &r);
 	CHECK_STATUS(status, NT_STATUS_OK);
@@ -1262,7 +1311,7 @@ static bool test_compound_padding(struct torture_context *tctx,
 	 * size must be 24: 16 byte read response header plus 3
 	 * requested bytes padded to an 8 byte boundary.
 	 */
-	CHECK_VALUE(req[1]->in.body_size, 24);
+	CHECK_VAL(req[1]->in.body_size, 24);
 
 	status = smb2_read_recv(req[1], tree, &r);
 	CHECK_STATUS(status, NT_STATUS_OK);
@@ -1303,8 +1352,8 @@ static bool test_compound_padding(struct torture_context *tctx,
 	 * size must be 24: 16 byte read response header plus 3
 	 * requested bytes padded to an 8 byte boundary.
 	 */
-	CHECK_VALUE(req[0]->in.body_size, 24);
-	CHECK_VALUE(req[1]->in.body_size, 24);
+	CHECK_VAL(req[0]->in.body_size, 24);
+	CHECK_VAL(req[1]->in.body_size, 24);
 
 	status = smb2_read_recv(req[0], tree, &r);
 	CHECK_STATUS(status, NT_STATUS_OK);
@@ -1336,7 +1385,7 @@ static bool test_compound_padding(struct torture_context *tctx,
 	 * size must be 19: 16 byte read response header plus 3
 	 * requested bytes without padding.
 	 */
-	CHECK_VALUE(req[0]->in.body_size, 19);
+	CHECK_VAL(req[0]->in.body_size, 19);
 
 	status = smb2_read_recv(req[0], tree, &r);
 	CHECK_STATUS(status, NT_STATUS_OK);
@@ -1929,6 +1978,111 @@ done:
     return ret;
 }
 
+/*
+ * Send a compound related series of CREATE+CLOSE+CREATE+NOTIFY and check
+ * CREATE+CLOSE+CREATE responses come in a separate compound response before the
+ * STATUS_PENDING for the NOTIFY.
+ */
+static bool test_compound_interim3(struct torture_context *tctx,
+				   struct smb2_tree *tree)
+{
+	const char *dname = "test_compound_interim3";
+	struct smb2_handle hd = {};
+	struct smb2_create cr = {};
+	struct smb2_handle h1 = {};
+	struct smb2_notify nt = {};
+	struct smb2_request *req[6] = {};
+	struct smb2_close cl = {};
+	NTSTATUS status;
+	int rc;
+	bool ret = true;
+
+	smb2_deltree(tree, dname);
+	smb2_transport_compound_start(tree->session->transport, 4);
+
+	hd.data[0] = UINT64_MAX;
+	hd.data[1] = UINT64_MAX;
+
+	cr.in.desired_access	= SEC_RIGHTS_FILE_ALL;
+	cr.in.create_options	= NTCREATEX_OPTIONS_DIRECTORY;
+	cr.in.file_attributes	= FILE_ATTRIBUTE_DIRECTORY;
+	cr.in.share_access	= NTCREATEX_SHARE_ACCESS_READ |
+		NTCREATEX_SHARE_ACCESS_WRITE |
+		NTCREATEX_SHARE_ACCESS_DELETE;
+	cr.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
+	cr.in.fname		= dname;
+
+	nt.in.recursive          = true;
+	nt.in.buffer_size        = 0x1000;
+	nt.in.file.handle        = hd;
+	nt.in.completion_filter  = FILE_NOTIFY_CHANGE_NAME;
+	nt.in.unknown            = 0x00000000;
+
+	req[0] = smb2_create_send(tree, &cr);
+	torture_assert_not_null_goto(tctx, req[0], ret, done,
+				     "smb2_create_send failed\n");
+
+	smb2_transport_compound_set_related(tree->session->transport, true);
+
+	cl.in.file.handle = hd;
+
+	req[1] = smb2_close_send(tree, &cl);
+	torture_assert_not_null_goto(tctx, req[1], ret, done,
+				     "smb2_close_send failed\n");
+
+	req[2] = smb2_create_send(tree, &cr);
+	torture_assert_not_null_goto(tctx, req[2], ret, done,
+				     "smb2_create_send failed\n");
+
+	req[3] = smb2_notify_send(tree, &nt);
+	torture_assert_not_null_goto(tctx, req[3], ret, done,
+				     "smb2_create_send failed\n");
+
+	while (req[2]->state < SMB2_REQUEST_DONE) {
+		rc = tevent_loop_once(tctx->ev);
+		torture_assert_goto(tctx, rc == 0, ret, done,
+				    "tevent_loop_once failed\n");
+	}
+
+	torture_assert_goto(tctx, req[0]->state == SMB2_REQUEST_DONE, ret, done,
+			    "state not SMB2_REQUEST_DONE");
+	torture_assert_goto(tctx, req[1]->state == SMB2_REQUEST_DONE, ret, done,
+			    "state not SMB2_REQUEST_DONE");
+	torture_assert_goto(tctx, req[2]->state == SMB2_REQUEST_DONE, ret, done,
+			    "state not SMB2_REQUEST_DONE");
+	torture_assert_goto(tctx, req[3]->state == SMB2_REQUEST_RECV, ret, done,
+			    "state not SMB2_REQUEST_RECV");
+
+	WAIT_FOR_ASYNC_RESPONSE(req[3]);
+	torture_assert_goto(tctx, req[3]->state == SMB2_REQUEST_RECV, ret, done,
+			    "state not SMB2_REQUEST_RECV");
+	torture_assert_goto(tctx, req[3]->cancel.can_cancel, ret, done, "pending");
+
+	status = smb2_create_recv(req[0], tree, &cr);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+					"smb2_setinfo_recv failed\n");
+
+	status = smb2_close_recv(req[1], &cl);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+					"smb2_setinfo_recv failed\n");
+
+	status = smb2_create_recv(req[2], tree, &cr);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+					"smb2_create_recv failed\n");
+	h1 = cr.out.file.handle;
+
+	smb2_cancel(req[3]);
+	status = smb2_notify_recv(req[3], tree, &nt);
+	torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_CANCELLED,
+					   ret, done,
+					   "smb2_notify_recv failed\n");
+
+done:
+	smb2_util_close(tree, h1);
+	smb2_deltree(tree, dname);
+	return ret;
+}
+
 /* Test compound related finds */
 static bool test_compound_find_related(struct torture_context *tctx,
 				       struct smb2_tree *tree)
@@ -2392,7 +2546,7 @@ static bool test_compound_async_write_write(struct torture_context *tctx,
 		 * as it's the last element of a compound.
 		 */
 		WAIT_FOR_ASYNC_RESPONSE(req[1]);
-		CHECK_VALUE(req[1]->cancel.can_cancel, true);
+		CHECK_VAL(req[1]->cancel.can_cancel, true);
 		/*
 		 * Now pick up the real return.
 		 */
@@ -2496,7 +2650,7 @@ static bool test_compound_async_read_read(struct torture_context *tctx,
 		 * as it's the last element of a compound.
 		 */
 		WAIT_FOR_ASYNC_RESPONSE(req[1]);
-		CHECK_VALUE(req[1]->cancel.can_cancel, true);
+		CHECK_VAL(req[1]->cancel.can_cancel, true);
 		/*
 		 * Now pick up the real return.
 		 */
@@ -2523,6 +2677,728 @@ static bool test_compound_async_read_read(struct torture_context *tctx,
 	return ret;
 }
 
+/*
+ * Checks a lease break by a create triggers an pending async response.
+ */
+static bool test_create_lease_break_async(struct torture_context *tctx,
+					  struct smb2_tree *tree1,
+					  struct smb2_tree *tree2)
+{
+	struct smb2_request *req = NULL;
+	struct smb2_create c1 = {};
+	struct smb2_create c2 = {};
+	struct smb2_lease ls1 = {};
+	struct smb2_lease ls2 = {};
+	struct smb2_handle h1 = {};
+	struct smb2_handle h2 = {};
+	struct smb2_lease_break_ack ack = {};
+	const char *fname_src = "test_create_lease_break_async.dat";
+	uint32_t caps;
+	NTSTATUS status;
+	bool ret = true;
+
+	caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
+	if (!(caps & SMB2_CAP_LEASING)) {
+		torture_skip(tctx, "leases are not supported");
+	}
+
+	smb2_util_unlink(tree1, fname_src);
+
+	tree1->session->transport->lease.handler = torture_lease_handler;
+	tree1->session->transport->lease.private_data = tree1;
+	torture_reset_lease_break_info(tctx, &lease_break_info);
+	lease_break_info.lease_skip_ack = true;
+
+	/* First open with a RWH lease. */
+	smb2_lease_create(&c1,
+			  &ls1,
+			  false,
+			  fname_src,
+			  LEASE1,
+			  smb2_util_lease_state("RWH"));
+
+	status = smb2_create(tree1, tree1, &c1);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+					"smb2_create failed\n");
+	CHECK_LEASE(&c1, "RWH", true, LEASE1, 0);
+	h1 = c1.out.file.handle;
+
+	/* Second open, triggers lease break to "RH" */
+
+	smb2_lease_create(&c2,
+			  &ls2,
+			  false,
+			  fname_src,
+			  LEASE2,
+			  smb2_util_lease_state("RH"));
+
+	req = smb2_create_send(tree2, &c2);
+	torture_assert_not_null_goto(tctx, req, ret, done,
+				     "smb2_create_send failed\n");
+
+	/*
+	 * Check we got the lease break, but defer the ack.
+	 */
+	CHECK_BREAK_INFO("RWH", "RH", LEASE1);
+
+	ack.in.lease.lease_key =
+		lease_break_info.lease_break.current_lease.lease_key;
+	ack.in.lease.lease_state =
+		lease_break_info.lease_break.new_lease_state;
+	torture_reset_lease_break_info(tctx, &lease_break_info);
+
+	/* Wait for STATUS_PENDING response */
+	WAIT_FOR_ASYNC_RESPONSE(req);
+	torture_assert_goto(tctx, req->cancel.can_cancel, ret, done, "pending");
+
+	status = smb2_lease_break_ack(tree1, &ack);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+					"smb2_lease_break_ack failed\n");
+	CHECK_LEASE_BREAK_ACK(&ack, "RH", LEASE1);
+
+
+	status = smb2_create_recv(req, tree2, &c2);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+					"smb2_create_recv failed\n");
+	h2 = c2.out.file.handle;
+
+done:
+	if (!smb2_util_handle_empty(h1)) {
+		smb2_util_close(tree1, h1);
+	}
+	if (!smb2_util_handle_empty(h2)) {
+		smb2_util_close(tree2, h2);
+	}
+
+	smb2_util_unlink(tree1, fname_src);
+
+	return ret;
+}
+
+/*
+ * Basic test compound related CREATE+GETINFO+CLOSE where
+ * the CREATE triggers a lease break. Verifies CREATE sees
+ * an async interim response.
+ */
+static bool test_compound_getinfo_middle(struct torture_context *tctx,
+					 struct smb2_tree *tree1,
+					 struct smb2_tree *tree2)
+{
+	struct smb2_create c1 = {};
+	struct smb2_create c2 = {};
+	struct smb2_lease ls1 = {};
+	struct smb2_handle h1 = {};
+	struct smb2_request *req[3] = {};
+	union smb_fileinfo info = {};
+	struct smb2_getinfo rinfo = {};
+	struct smb2_lease_break_ack ack = {};
+	struct smb2_close cl = {};
+	const char *fname_src = "test_compound_getinfo_middle.dat";
+	uint32_t caps;
+	NTSTATUS status;
+	bool ret = true;
+
+	caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
+	if (!(caps & SMB2_CAP_LEASING)) {
+		torture_skip(tctx, "leases are not supported");
+	}
+
+	smb2_util_unlink(tree1, fname_src);
+
+	tree1->session->transport->lease.handler = torture_lease_handler;
+	tree1->session->transport->lease.private_data = tree1;
+	torture_reset_lease_break_info(tctx, &lease_break_info);
+	lease_break_info.lease_skip_ack = true;
+
+	/* First open with a RWH lease. */
+	smb2_lease_create(&c1,
+			  &ls1,
+			  false,
+			  fname_src,
+			  LEASE1,
+			  smb2_util_lease_state("RWH"));
+
+	status = smb2_create(tree1, tree1, &c1);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+					"smb2_create failed\n");
+	CHECK_LEASE(&c1, "RWH", true, LEASE1, 0);
+	h1 = c1.out.file.handle;
+
+	/* Second open, triggers a lease break */
+
+	smb2_transport_compound_start(tree2->session->transport, 3);
+
+	smb2_lease_create(&c2,
+			  NULL,
+			  false,
+			  fname_src,
+			  0,
+			  smb2_util_lease_state(""));
+	req[0] = smb2_create_send(tree2, &c2);
+	torture_assert_not_null_goto(tctx, req[0], ret, done,
+				     "smb2_create_send failed\n");
+
+	smb2_transport_compound_set_related(tree2->session->transport, true);
+
+	ZERO_STRUCT(info);
+	info.generic.level = RAW_FILEINFO_BASIC_INFORMATION;
+	info.generic.in.file.handle.data[0] = UINT64_MAX;
+	info.generic.in.file.handle.data[0] = UINT64_MAX;
+	req[1] = smb2_getinfo_file_send(tree2, &info);
+	torture_assert(tctx, req[1] != NULL, "smb2_setinfo_file_send");
+
+	cl.in.file.handle.data[0] = UINT64_MAX;
+	cl.in.file.handle.data[1] = UINT64_MAX;
+
+	req[2] = smb2_close_send(tree2, &cl);
+	torture_assert(tctx, req[2] != NULL, "smb2_close_send");
+
+	/*
+	 * Check we got the lease break, but defer the ack.
+	 */
+	CHECK_BREAK_INFO("RWH", "RH", LEASE1);
+
+	ack.in.lease.lease_key =
+		lease_break_info.lease_break.current_lease.lease_key;


-- 
Samba Shared Repository



More information about the samba-cvs mailing list