The Samba-Bugzilla – Attachment 18490 Details for
Bug 15697
Compound rename from Mac clients can fail with NT_STATUS_INTERNAL_ERROR if the file has a lease
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Patch for 4.20 and 4.21 cherry-picked from master
bug15697-v420,v421.patch (text/plain), 44.74 KB, created by
Ralph Böhme
on 2024-11-04 10:53:40 UTC
(
hide
)
Description:
Patch for 4.20 and 4.21 cherry-picked from master
Filename:
MIME Type:
Creator:
Ralph Böhme
Created:
2024-11-04 10:53:40 UTC
Size:
44.74 KB
patch
obsolete
>From 15e9e9a5cfe11e5647679b54f41fdd44f4a8fa91 Mon Sep 17 00:00:00 2001 >From: Ralph Boehme <slow@samba.org> >Date: Thu, 17 Oct 2024 17:44:13 +0200 >Subject: [PATCH 1/6] 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@samba.org> >Reviewed-by: Stefan Metzmacher <metze@samba.org> >(cherry picked from commit 4932b433ff2f1c4e603073624a5d22140acfb2ed) >--- > source4/torture/smb2/compound.c | 17 +++++++++-------- > 1 file changed, 9 insertions(+), 8 deletions(-) > >diff --git a/source4/torture/smb2/compound.c b/source4/torture/smb2/compound.c >index 175069d54ee5..91e185d45c91 100644 >--- a/source4/torture/smb2/compound.c >+++ b/source4/torture/smb2/compound.c >@@ -37,12 +37,13 @@ > 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 WAIT_FOR_ASYNC_RESPONSE(req) \ >@@ -1191,7 +1192,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 +1263,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 +1304,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 +1337,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); >@@ -2392,7 +2393,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 +2497,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. > */ >-- >2.47.0 > > >From 3027b0f17cae3716b00d6fa9d617d58a461c3553 Mon Sep 17 00:00:00 2001 >From: Ralph Boehme <slow@samba.org> >Date: Thu, 17 Oct 2024 17:45:26 +0200 >Subject: [PATCH 2/6] 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@samba.org> >Reviewed-by: Stefan Metzmacher <metze@samba.org> >(cherry picked from commit 42e739ab62cb573d72215737027cf3c7f1fcd212) >--- > .../knownfail.d/samba3.smb2.compound_async | 4 + > source4/torture/smb2/compound.c | 888 ++++++++++++++++++ > 2 files changed, 892 insertions(+) > create mode 100644 selftest/knownfail.d/samba3.smb2.compound_async > >diff --git a/selftest/knownfail.d/samba3.smb2.compound_async b/selftest/knownfail.d/samba3.smb2.compound_async >new file mode 100644 >index 000000000000..20de45d5061f >--- /dev/null >+++ b/selftest/knownfail.d/samba3.smb2.compound_async >@@ -0,0 +1,4 @@ >+^samba3.smb2.compound_async.rename_non_compound_no_async\(fileserver\) >+^samba3.smb2.compound_async.rename_same_srcdst_non_compound_no_async\(fileserver\) >+^samba3.smb2.compound_async.rename_last\(fileserver\) >+^samba3.smb2.compound_async.rename_middle\(fileserver\) >diff --git a/source4/torture/smb2/compound.c b/source4/torture/smb2/compound.c >index 91e185d45c91..622283a72911 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)) { \ >@@ -46,6 +47,50 @@ > 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) { \ >@@ -53,6 +98,9 @@ > } \ > } > >+static const uint64_t LEASE1 = 0xBADC0FFEE0DDF00Dull; >+static const uint64_t LEASE2 = 0xDEADBEEFFEEDBEADull; >+ > static struct { > struct smb2_handle handle; > uint8_t level; >@@ -1930,6 +1978,111 @@ static bool test_compound_interim2(struct torture_context *tctx, > 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) >@@ -2524,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; >+ ack.in.lease.lease_state = >+ lease_break_info.lease_break.new_lease_state; >+ torture_reset_lease_break_info(tctx, &lease_break_info); >+ >+ /* Wait for async response */ >+ WAIT_FOR_ASYNC_RESPONSE(req[0]); >+ >+ torture_assert_goto(tctx, req[0]->state == SMB2_REQUEST_RECV, ret, done, >+ "smb2_create finished"); >+ torture_assert_goto(tctx, req[1]->state == SMB2_REQUEST_RECV, ret, done, >+ "smb2_getinfo finished"); >+ torture_assert_goto(tctx, req[2]->state == SMB2_REQUEST_RECV, ret, done, >+ "smb2_close finished"); >+ torture_assert_goto(tctx, req[0]->cancel.can_cancel, ret, done, "pending"); >+ torture_assert_goto(tctx, !req[1]->cancel.can_cancel, ret, done, "pending"); >+ torture_assert_goto(tctx, !req[2]->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[0], tree2, &c2); >+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done, >+ "smb2_create_recv failed\n"); >+ >+ status = smb2_getinfo_recv(req[1], tree2, &rinfo); >+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done, >+ "smb2_getinfo_recv failed\n"); >+ >+ status = smb2_close_recv(req[2], &cl); >+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done, >+ "smb2_close_recv failed\n"); >+ >+done: >+ if (!smb2_util_handle_empty(h1)) { >+ smb2_util_close(tree1, h1); >+ } >+ smb2_util_unlink(tree1, fname_src); >+ >+ return ret; >+} >+ >+/* >+ * Checks a lease break by a rename where src and dst name are the same does not >+ * trigger a pending async response, but does trigger a h-lease break. >+ */ >+static bool test_rename_same_srcdst_non_compound_no_async( >+ 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_lease ls2 = {}; >+ struct smb2_handle h1 = {}; >+ struct smb2_handle h2 = {}; >+ struct smb2_request *req = NULL; >+ struct smb2_lease_break_ack ack = {}; >+ union smb_setfileinfo sinfo = {}; >+ const char *fname_src = "test_rename_non_compound_no_async.dat"; >+ const char *fname_dst = "test_rename_non_compound_no_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); >+ smb2_util_unlink(tree1, fname_dst); >+ >+ 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 RH lease. */ >+ smb2_lease_create(&c1, >+ &ls1, >+ false, >+ fname_src, >+ LEASE1, >+ smb2_util_lease_state("RH")); >+ >+ status = smb2_create(tree1, tree1, &c1); >+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done, >+ "smb2_create failed\n"); >+ CHECK_LEASE(&c1, "RH", true, LEASE1, 0); >+ h1 = c1.out.file.handle; >+ >+ /* Second open, also with a RH lease, this will do the rename */ >+ >+ smb2_lease_create(&c2, >+ &ls2, >+ false, >+ fname_src, >+ LEASE2, >+ smb2_util_lease_state("RH")); >+ status = smb2_create(tree2, tree2, &c2); >+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done, >+ "smb2_create failed\n"); >+ CHECK_LEASE(&c2, "RH", true, LEASE2, 0); >+ h2 = c2.out.file.handle; >+ >+ /* Break with a rename. */ >+ sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; >+ sinfo.rename_information.in.file.handle = h2; >+ sinfo.rename_information.in.new_name = fname_dst; >+ req = smb2_setinfo_file_send(tree2, &sinfo); >+ torture_assert(tctx, req != NULL, "smb2_setinfo_file_send"); >+ >+ /* >+ * Check we got the lease break, but defer the ack. >+ */ >+ CHECK_BREAK_INFO("RH", "R", 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); >+ >+ /* Give the server enough time to possibly send a pending response */ >+ smb_msleep(1000); >+ >+ 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, "R", LEASE1); >+ >+ /* >+ * Sending the lease break ACK would have also read the >+ * NT_STATUS_PENDING interim response if any, but a Windows server >+ * doesn't send one, check this. This is in contract to a lease break >+ * triggered by an SMB2-CREATE. >+ */ >+ torture_assert_goto(tctx, !req->cancel.can_cancel, ret, done, "pending"); >+ >+ /* Get the rename reply. */ >+ status = smb2_setinfo_recv(req); >+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done, >+ "smb2_setinfo_recv failed\n"); >+ >+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); >+ smb2_util_unlink(tree1, fname_dst); >+ >+ return ret; >+} >+ >+/* >+ * Checks a lease break by a rename does not trigger a pending async response. >+ */ >+static bool test_rename_non_compound_no_async(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_lease ls2 = {}; >+ struct smb2_handle h1 = {}; >+ struct smb2_handle h2 = {}; >+ struct smb2_request *req = NULL; >+ struct smb2_lease_break_ack ack = {}; >+ union smb_setfileinfo sinfo = {}; >+ const char *fname_src = "test_rename_non_compound_no_async_src.dat"; >+ const char *fname_dst = "test_rename_non_compound_no_async_dst.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); >+ smb2_util_unlink(tree1, fname_dst); >+ >+ 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 RH lease. */ >+ smb2_lease_create(&c1, >+ &ls1, >+ false, >+ fname_src, >+ LEASE1, >+ smb2_util_lease_state("RH")); >+ >+ status = smb2_create(tree1, tree1, &c1); >+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done, >+ "smb2_create failed\n"); >+ CHECK_LEASE(&c1, "RH", true, LEASE1, 0); >+ h1 = c1.out.file.handle; >+ >+ /* Second open, also with a RH lease, this will to the rename */ >+ >+ smb2_lease_create(&c2, >+ &ls2, >+ false, >+ fname_src, >+ LEASE2, >+ smb2_util_lease_state("RH")); >+ status = smb2_create(tree2, tree2, &c2); >+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done, >+ "smb2_create failed\n"); >+ CHECK_LEASE(&c2, "RH", true, LEASE2, 0); >+ h2 = c2.out.file.handle; >+ >+ /* Break with a rename. */ >+ sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; >+ sinfo.rename_information.in.file.handle = h2; >+ sinfo.rename_information.in.new_name = fname_dst; >+ req = smb2_setinfo_file_send(tree2, &sinfo); >+ torture_assert(tctx, req != NULL, "smb2_setinfo_file_send"); >+ >+ /* >+ * Check we got the lease break, but defer the ack. >+ */ >+ CHECK_BREAK_INFO("RH", "R", 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); >+ >+ /* Give the server enough time to possibly send a pending response */ >+ smb_msleep(1000); >+ >+ 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, "R", LEASE1); >+ >+ /* >+ * Sending the lease break ACK would have also read the >+ * NT_STATUS_PENDING interim response if any, but a Windows server >+ * doesn't send one, check this. This is in contract to a lease break >+ * triggered by an SMB2-CREATE. >+ */ >+ torture_assert_goto(tctx, !req->cancel.can_cancel, ret, done, "pending"); >+ >+ /* Get the rename reply. */ >+ status = smb2_setinfo_recv(req); >+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done, >+ "smb2_setinfo_recv failed\n"); >+ >+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); >+ smb2_util_unlink(tree1, fname_dst); >+ >+ return ret; >+} >+ >+/* >+ * Test a compound SMB2-CREATE+SMB2-SETINFO(rename) works and doesn't trigger a >+ * pending async response. >+ */ >+static bool test_compound_rename_last(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_lease ls2 = {}; >+ struct smb2_handle h1 = {}; >+ struct smb2_handle h2 = {}; >+ struct smb2_request *req[2] = {}; >+ union smb_setfileinfo sinfo = {}; >+ struct smb2_lease_break_ack ack = {}; >+ const char *fname_src = "test_compound_rename_last_src.dat"; >+ const char *fname_dst = "test_compound_rename_last_dst.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); >+ smb2_util_unlink(tree1, fname_dst); >+ >+ 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 RH lease. */ >+ smb2_lease_create(&c1, >+ &ls1, >+ false, >+ fname_src, >+ LEASE1, >+ smb2_util_lease_state("RH")); >+ >+ status = smb2_create(tree1, tree1, &c1); >+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done, >+ "smb2_create failed\n"); >+ CHECK_LEASE(&c1, "RH", true, LEASE1, 0); >+ h1 = c1.out.file.handle; >+ >+ /* Second open, also with a RH lease, this will to the rename */ >+ >+ smb2_transport_compound_start(tree2->session->transport, 2); >+ >+ smb2_lease_create(&c2, >+ &ls2, >+ false, >+ fname_src, >+ LEASE2, >+ 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); >+ >+ /* Break with a rename. */ >+ sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; >+ sinfo.rename_information.in.file.handle.data[0] = UINT64_MAX; >+ sinfo.rename_information.in.file.handle.data[1] = UINT64_MAX; >+ sinfo.rename_information.in.new_name = fname_dst; >+ req[1] = smb2_setinfo_file_send(tree2, &sinfo); >+ torture_assert(tctx, req[1] != NULL, "smb2_setinfo_file_send"); >+ >+ /* >+ * Check we got the lease break, but defer the ack. >+ */ >+ CHECK_BREAK_INFO("RH", "R", 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); >+ >+ /* Give the server enough time to possibly send a pending response */ >+ smb_msleep(1000); >+ >+ 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, "R", LEASE1); >+ >+ /* >+ * Sending the lease break ACK would have also read the >+ * NT_STATUS_PENDING interim response if any, but a Windows server >+ * doesn't send one, check this. This is in contract to a lease break >+ * triggered by an SMB2-CREATE. >+ */ >+ torture_assert_goto(tctx, req[0]->state == SMB2_REQUEST_RECV, ret, done, >+ "state not SMB2_REQUEST_RECV"); >+ torture_assert_goto(tctx, req[1]->state == SMB2_REQUEST_RECV, ret, done, >+ "state not SMB2_REQUEST_RECV"); >+ torture_assert_goto(tctx, !req[1]->cancel.can_cancel, ret, done, "pending"); >+ >+ status = smb2_create_recv(req[0], tree2, &c2); >+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done, >+ "smb2_setinfo_recv failed\n"); >+ h2 = c2.out.file.handle; >+ >+ /* Get the rename reply. */ >+ status = smb2_setinfo_recv(req[1]); >+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done, >+ "smb2_setinfo_recv failed\n"); >+ >+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); >+ smb2_util_unlink(tree1, fname_dst); >+ >+ return ret; >+} >+ >+/* >+ * Compound related CREATE + SETINFO(rename) + CLOSE, rename triggers a lease >+ * break. Verify we don't get an async interim response for the SETINFO and all >+ * responses are received in a single compound response. >+ */ >+static bool test_compound_rename_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_setfileinfo sinfo = {}; >+ struct smb2_lease_break_ack ack = {}; >+ struct smb2_close cl = {}; >+ const char *fname_src = "test_compound_rename_middle_src.dat"; >+ const char *fname_dst = "test_compound_rename_middle_dst.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); >+ smb2_util_unlink(tree1, fname_dst); >+ >+ 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 RH lease. */ >+ smb2_lease_create(&c1, >+ &ls1, >+ false, >+ fname_src, >+ LEASE1, >+ smb2_util_lease_state("RH")); >+ >+ status = smb2_create(tree1, tree1, &c1); >+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done, >+ "smb2_create failed\n"); >+ CHECK_LEASE(&c1, "RH", true, LEASE1, 0); >+ h1 = c1.out.file.handle; >+ >+ /* Second open, this will to the rename */ >+ >+ 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); >+ >+ /* Break with a rename. */ >+ sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; >+ sinfo.rename_information.in.file.handle.data[0] = UINT64_MAX; >+ sinfo.rename_information.in.file.handle.data[1] = UINT64_MAX; >+ sinfo.rename_information.in.new_name = fname_dst; >+ req[1] = smb2_setinfo_file_send(tree2, &sinfo); >+ 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"); >+ >+ /* Give the server enough time to possibly send a pending response */ >+ smb_msleep(1000); >+ >+ /* >+ * Check we got the lease break, but defer the ack. >+ */ >+ CHECK_BREAK_INFO("RH", "R", 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); >+ >+ torture_assert_goto(tctx, !req[0]->cancel.can_cancel, ret, done, "pending"); >+ torture_assert_goto(tctx, !req[1]->cancel.can_cancel, ret, done, "pending"); >+ torture_assert_goto(tctx, !req[2]->cancel.can_cancel, ret, done, "pending"); >+ torture_assert_goto(tctx, req[0]->state == SMB2_REQUEST_RECV, ret, done, >+ "state not SMB2_REQUEST_RECV"); >+ torture_assert_goto(tctx, req[1]->state == SMB2_REQUEST_RECV, ret, done, >+ "state not SMB2_REQUEST_RECV"); >+ torture_assert_goto(tctx, req[2]->state == SMB2_REQUEST_RECV, ret, done, >+ "state not SMB2_REQUEST_RECV"); >+ >+ 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, "R", LEASE1); >+ >+ >+ status = smb2_create_recv(req[0], tree2, &c2); >+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done, >+ "smb2_setinfo_recv failed\n"); >+ >+ /* Get the rename reply. */ >+ status = smb2_setinfo_recv(req[1]); >+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done, >+ "smb2_setinfo_recv failed\n"); >+ >+ status = smb2_close_recv(req[2], &cl); >+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done, >+ "smb2_setinfo_recv failed\n"); >+ >+done: >+ if (!smb2_util_handle_empty(h1)) { >+ smb2_util_close(tree1, h1); >+ } >+ >+ smb2_util_unlink(tree1, fname_src); >+ smb2_util_unlink(tree1, fname_dst); >+ >+ return ret; >+} > > struct torture_suite *torture_smb2_compound_init(TALLOC_CTX *ctx) > { >@@ -2553,6 +3428,7 @@ struct torture_suite *torture_smb2_compound_init(TALLOC_CTX *ctx) > suite, "invalid4", test_compound_invalid4); > torture_suite_add_1smb2_test(suite, "interim1", test_compound_interim1); > torture_suite_add_1smb2_test(suite, "interim2", test_compound_interim2); >+ torture_suite_add_1smb2_test(suite, "interim3", test_compound_interim3); > torture_suite_add_1smb2_test(suite, "compound-break", test_compound_break); > torture_suite_add_1smb2_test(suite, "compound-padding", test_compound_padding); > torture_suite_add_1smb2_test(suite, "create-write-close", >@@ -2589,6 +3465,18 @@ struct torture_suite *torture_smb2_compound_async_init(TALLOC_CTX *ctx) > test_compound_async_write_write); > torture_suite_add_1smb2_test(suite, "read_read", > test_compound_async_read_read); >+ torture_suite_add_2smb2_test(suite, "create_lease_break_async", >+ test_create_lease_break_async); >+ torture_suite_add_2smb2_test(suite, "getinfo_middle", >+ test_compound_getinfo_middle); >+ torture_suite_add_2smb2_test(suite, "rename_same_srcdst_non_compound_no_async", >+ test_rename_same_srcdst_non_compound_no_async); >+ torture_suite_add_2smb2_test(suite, "rename_non_compound_no_async", >+ test_rename_non_compound_no_async); >+ torture_suite_add_2smb2_test(suite, "rename_last", >+ test_compound_rename_last); >+ torture_suite_add_2smb2_test(suite, "rename_middle", >+ test_compound_rename_middle); > > suite->description = talloc_strdup(suite, "SMB2-COMPOUND-ASYNC tests"); > >-- >2.47.0 > > >From 9f044363054dc4d655ee27f9af91e5641d03539b Mon Sep 17 00:00:00 2001 >From: Ralph Boehme <slow@samba.org> >Date: Sat, 21 Sep 2024 01:28:07 +0200 >Subject: [PATCH 3/6] 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@samba.org> >Reviewed-by: Stefan Metzmacher <metze@samba.org> >(cherry picked from commit 3890ac2fafc5e17919fa39542440a05ef72a3fa5) >--- > source4/torture/smb2/rename.c | 72 +++++++++++++++++++++++++++++++++++ > 1 file changed, 72 insertions(+) > >diff --git a/source4/torture/smb2/rename.c b/source4/torture/smb2/rename.c >index 12636c403f05..31e30fa19b49 100644 >--- a/source4/torture/smb2/rename.c >+++ b/source4/torture/smb2/rename.c >@@ -1693,6 +1693,74 @@ static bool test_smb2_close_full_information(struct torture_context *torture, > return ret; > } > >+static bool torture_smb2_rename_open(struct torture_context *torture, >+ struct smb2_tree *tree1, >+ struct smb2_tree *tree2) >+{ >+ struct smb2_create c1 = {}; >+ struct smb2_create c2 = {}; >+ union smb_setfileinfo sinfo = {}; >+ struct smb2_handle h1 = {}; >+ struct smb2_handle h2 = {}; >+ NTSTATUS status; >+ bool ret = true; >+ >+ smb2_deltree(tree1, BASEDIR); >+ smb2_util_mkdir(tree1, BASEDIR); >+ >+ /* Create testfile */ >+ >+ c1.in.desired_access = SEC_STD_DELETE; >+ c1.in.create_options = NTCREATEX_OPTIONS_NON_DIRECTORY_FILE; >+ c1.in.file_attributes = FILE_ATTRIBUTE_NORMAL; >+ c1.in.share_access = NTCREATEX_SHARE_ACCESS_READ | >+ NTCREATEX_SHARE_ACCESS_WRITE | NTCREATEX_SHARE_ACCESS_DELETE; >+ c1.in.create_disposition = NTCREATEX_DISP_CREATE; >+ c1.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; >+ c1.in.fname = BASEDIR "\\file.txt"; >+ >+ status = smb2_create(tree1, torture, &c1); >+ torture_assert_ntstatus_ok_goto(torture, status, ret, done, >+ "smb2_create failed\n"); >+ h1 = c1.out.file.handle; >+ >+ /* 2nd open on testfile */ >+ >+ c2.in.desired_access = SEC_FILE_READ_DATA; >+ c2.in.create_options = NTCREATEX_OPTIONS_NON_DIRECTORY_FILE; >+ c2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; >+ c2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | >+ NTCREATEX_SHARE_ACCESS_WRITE | NTCREATEX_SHARE_ACCESS_DELETE; >+ c2.in.create_disposition = NTCREATEX_DISP_OPEN; >+ c2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; >+ c2.in.fname = BASEDIR "\\file.txt"; >+ >+ status = smb2_create(tree2, torture, &c2); >+ torture_assert_ntstatus_ok_goto(torture, status, ret, done, >+ "smb2_create failed\n"); >+ h2 = c2.out.file.handle; >+ >+ torture_comment(torture, "Renaming test file\n"); >+ >+ sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; >+ sinfo.rename_information.in.file.handle = h1; >+ sinfo.rename_information.in.new_name = >+ BASEDIR "\\newname.txt"; >+ status = smb2_setinfo_file(tree1, &sinfo); >+ torture_assert_ntstatus_ok_goto(torture, status, ret, done, >+ "Rename failed\n"); >+ >+done: >+ if (!smb2_util_handle_empty(h1)) { >+ smb2_util_close(tree1, h1); >+ } >+ if (!smb2_util_handle_empty(h2)) { >+ smb2_util_close(tree2, h2); >+ } >+ smb2_deltree(tree1, BASEDIR); >+ return ret; >+} >+ > /* > basic testing of SMB2 rename > */ >@@ -1745,6 +1813,10 @@ struct torture_suite *torture_smb2_rename_init(TALLOC_CTX *ctx) > "close-full-information", > test_smb2_close_full_information); > >+ torture_suite_add_2smb2_test(suite, >+ "rename-open", >+ torture_smb2_rename_open); >+ > suite->description = talloc_strdup(suite, "smb2.rename tests"); > > return suite; >-- >2.47.0 > > >From 1ea04e6461547592a04b03d6101276532ca75701 Mon Sep 17 00:00:00 2001 >From: Ralph Boehme <slow@samba.org> >Date: Mon, 26 Aug 2024 10:48:34 +0200 >Subject: [PATCH 4/6] 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@samba.org> >Reviewed-by: Stefan Metzmacher <metze@samba.org> >(cherry picked from commit a5635791cfdb10f64bf2bf7c72c58f7591249a0d) >--- > selftest/knownfail | 2 -- > source3/smbd/smb2_server.c | 10 ++++++++++ > 2 files changed, 10 insertions(+), 2 deletions(-) > >diff --git a/selftest/knownfail b/selftest/knownfail >index 31e70a1a9d34..5f64e4edad05 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 b37829e8c4f8..287d9844e842 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); >-- >2.47.0 > > >From aee2977996ed6c6a326ab9686bf5deafeb2ed655 Mon Sep 17 00:00:00 2001 >From: Ralph Boehme <slow@samba.org> >Date: Thu, 10 Oct 2024 19:25:30 +0200 >Subject: [PATCH 5/6] smbd: force sync rename with lease break > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15697 > >Signed-off-by: Ralph Boehme <slow@samba.org> >Reviewed-by: Stefan Metzmacher <metze@samba.org> >(cherry picked from commit bc2d87981967bc65155ba09eb5b3e3f913bec50e) >--- > selftest/knownfail.d/samba3.smb2.compound_async | 3 --- > source3/smbd/smb2_setinfo.c | 6 +++++- > 2 files changed, 5 insertions(+), 4 deletions(-) > >diff --git a/selftest/knownfail.d/samba3.smb2.compound_async b/selftest/knownfail.d/samba3.smb2.compound_async >index 20de45d5061f..2d956ba45b08 100644 >--- a/selftest/knownfail.d/samba3.smb2.compound_async >+++ b/selftest/knownfail.d/samba3.smb2.compound_async >@@ -1,4 +1 @@ >-^samba3.smb2.compound_async.rename_non_compound_no_async\(fileserver\) >-^samba3.smb2.compound_async.rename_same_srcdst_non_compound_no_async\(fileserver\) >-^samba3.smb2.compound_async.rename_last\(fileserver\) > ^samba3.smb2.compound_async.rename_middle\(fileserver\) >diff --git a/source3/smbd/smb2_setinfo.c b/source3/smbd/smb2_setinfo.c >index dd0ba880fd1d..b43dedfec18e 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) >-- >2.47.0 > > >From d1cd6c213cfb74e1d46414befbce4dc3026b8c60 Mon Sep 17 00:00:00 2001 >From: Ralph Boehme <slow@samba.org> >Date: Thu, 10 Oct 2024 19:29:09 +0200 >Subject: [PATCH 6/6] smbd: fix breaking leases on rename >MIME-Version: 1.0 >Content-Type: text/plain; charset=UTF-8 >Content-Transfer-Encoding: 8bit > >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@samba.org> >Reviewed-by: Stefan Metzmacher <metze@samba.org> > >Autobuild-User(master): Ralph Böhme <slow@samba.org> >Autobuild-Date(master): Thu Oct 31 12:47:24 UTC 2024 on atb-devel-224 > >(cherry picked from commit efbbe8d6f80ceb6107f20486623eee949409c0ff) >--- > selftest/knownfail.d/samba3.smb2.compound_async | 1 - > source3/smbd/smb2_setinfo.c | 4 ---- > 2 files changed, 5 deletions(-) > delete mode 100644 selftest/knownfail.d/samba3.smb2.compound_async > >diff --git a/selftest/knownfail.d/samba3.smb2.compound_async b/selftest/knownfail.d/samba3.smb2.compound_async >deleted file mode 100644 >index 2d956ba45b08..000000000000 >--- a/selftest/knownfail.d/samba3.smb2.compound_async >+++ /dev/null >@@ -1 +0,0 @@ >-^samba3.smb2.compound_async.rename_middle\(fileserver\) >diff --git a/source3/smbd/smb2_setinfo.c b/source3/smbd/smb2_setinfo.c >index b43dedfec18e..9f04d020c4fb 100644 >--- a/source3/smbd/smb2_setinfo.c >+++ b/source3/smbd/smb2_setinfo.c >@@ -242,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) { >-- >2.47.0 >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Raw
Flags:
metze
:
review+
Actions:
View
Attachments on
bug 15697
: 18490