The Samba-Bugzilla – Attachment 18464 Details for
Bug 15649
Durable handle is not granted when a previous OPEN exists with NoOplock
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
Patches for v4-21-test
bfixes-tmp421.txt (text/plain), 124.78 KB, created by
Stefan Metzmacher
on 2024-10-10 14:07:38 UTC
(
hide
)
Description:
Patches for v4-21-test
Filename:
MIME Type:
Creator:
Stefan Metzmacher
Created:
2024-10-10 14:07:38 UTC
Size:
124.78 KB
patch
obsolete
>From 77a25a2c4315082ead87f9dcb553431a53a92f9d Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >Date: Fri, 30 Aug 2024 14:22:24 +0200 >Subject: [PATCH 01/10] s4:torture/smb2: improve error handling in > durable_open.c > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15649 >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15651 > >Signed-off-by: Stefan Metzmacher <metze@samba.org> >Reviewed-by: Ralph Boehme <slow@samba.org> >(cherry picked from commit e65e1326a0214a7dfff75ea1e528e82c8fc64517) >--- > source4/torture/smb2/durable_open.c | 39 ++++++++--------------------- > 1 file changed, 11 insertions(+), 28 deletions(-) > >diff --git a/source4/torture/smb2/durable_open.c b/source4/torture/smb2/durable_open.c >index cd07b330484e..8299995beb99 100644 >--- a/source4/torture/smb2/durable_open.c >+++ b/source4/torture/smb2/durable_open.c >@@ -28,34 +28,17 @@ > #include "torture/smb2/proto.h" > #include "../libcli/smb/smbXcli_base.h" > >-#define CHECK_VAL(v, correct) do { \ >- if ((v) != (correct)) { \ >- torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%llx - should be 0x%llx\n", \ >- __location__, #v, (unsigned long long)v, (unsigned long long)correct); \ >- ret = false; \ >- }} while (0) >- >-#define CHECK_NOT_VAL(v, incorrect) do { \ >- if ((v) == (incorrect)) { \ >- torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%llx - should not be 0x%llx\n", \ >- __location__, #v, (unsigned long long)v, (unsigned long long)incorrect); \ >- ret = false; \ >- }} while (0) >- >-#define CHECK_NOT_NULL(p) do { \ >- if ((p) == NULL) { \ >- torture_result(tctx, TORTURE_FAIL, "(%s): %s is NULL but it should not be.\n", \ >- __location__, #p); \ >- ret = false; \ >- }} while (0) >- >-#define CHECK_STATUS(status, correct) do { \ >- if (!NT_STATUS_EQUAL(status, correct)) { \ >- torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \ >- nt_errstr(status), nt_errstr(correct)); \ >- ret = false; \ >- goto done; \ >- }} while (0) >+#define CHECK_VAL(v, correct) \ >+ torture_assert_u64_equal_goto(tctx, v, correct, ret, done, __location__) >+ >+#define CHECK_NOT_VAL(v, incorrect) \ >+ torture_assert_u64_not_equal_goto(tctx, v, incorrect, ret, done, __location__) >+ >+#define CHECK_NOT_NULL(p) \ >+ torture_assert_not_null_goto(tctx, p, ret, done, __location__) >+ >+#define CHECK_STATUS(status, correct) \ >+ torture_assert_ntstatus_equal_goto(tctx, status, correct, ret, done, __location__) > > #define CHECK_CREATED(__io, __created, __attribute) \ > do { \ >-- >2.34.1 > > >From c18211fc69fd79fff6e37e42b4fc5cc8c91ec1b6 Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >Date: Fri, 30 Aug 2024 14:22:24 +0200 >Subject: [PATCH 02/10] s4:torture/smb2: improve error handling in > durable_v2_open.c > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15649 >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15651 > >Signed-off-by: Stefan Metzmacher <metze@samba.org> >Reviewed-by: Ralph Boehme <slow@samba.org> >(cherry picked from commit 9b2417c2f04857709c25e3665cd783a68edf0cf2) >--- > source4/torture/smb2/durable_v2_open.c | 19 +++++-------------- > 1 file changed, 5 insertions(+), 14 deletions(-) > >diff --git a/source4/torture/smb2/durable_v2_open.c b/source4/torture/smb2/durable_v2_open.c >index 7447dd287a48..7eed4327b52e 100644 >--- a/source4/torture/smb2/durable_v2_open.c >+++ b/source4/torture/smb2/durable_v2_open.c >@@ -27,20 +27,11 @@ > #include "torture/smb2/proto.h" > #include "librpc/ndr/libndr.h" > >-#define CHECK_VAL(v, correct) do { \ >- if ((v) != (correct)) { \ >- torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%x - should be 0x%x\n", \ >- __location__, #v, (int)v, (int)correct); \ >- ret = false; \ >- }} while (0) >- >-#define CHECK_STATUS(status, correct) do { \ >- if (!NT_STATUS_EQUAL(status, correct)) { \ >- torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \ >- nt_errstr(status), nt_errstr(correct)); \ >- ret = false; \ >- goto done; \ >- }} while (0) >+#define CHECK_VAL(v, correct) \ >+ torture_assert_u64_equal_goto(tctx, v, correct, ret, done, __location__) >+ >+#define CHECK_STATUS(status, correct) \ >+ torture_assert_ntstatus_equal_goto(tctx, status, correct, ret, done, __location__) > > #define CHECK_CREATED(__io, __created, __attribute) \ > do { \ >-- >2.34.1 > > >From d15888ae7efb22b8934b0dcc662202d5738e77d7 Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >Date: Fri, 30 Aug 2024 17:38:02 +0200 >Subject: [PATCH 03/10] s4:torture/smb2: add smb2.durable-open.lock-noW-lease > >This demonstrates that a W lease is required for a >durable handle to be durable when it has byte range locks. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15649 >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15651 > >Signed-off-by: Stefan Metzmacher <metze@samba.org> >Reviewed-by: Ralph Boehme <slow@samba.org> >(cherry picked from commit 1cc1586d84a65046ab7804f17297c6964bb76c23) >--- > selftest/knownfail.d/smb2.durable.lock | 1 + > source4/torture/smb2/durable_open.c | 97 +++++++++++++++++++++++++- > 2 files changed, 97 insertions(+), 1 deletion(-) > create mode 100644 selftest/knownfail.d/smb2.durable.lock > >diff --git a/selftest/knownfail.d/smb2.durable.lock b/selftest/knownfail.d/smb2.durable.lock >new file mode 100644 >index 000000000000..3e3bd80ba463 >--- /dev/null >+++ b/selftest/knownfail.d/smb2.durable.lock >@@ -0,0 +1 @@ >+^samba3.smb2.durable-open.lock-noW-lease >diff --git a/source4/torture/smb2/durable_open.c b/source4/torture/smb2/durable_open.c >index 8299995beb99..b730fab3c29f 100644 >--- a/source4/torture/smb2/durable_open.c >+++ b/source4/torture/smb2/durable_open.c >@@ -2151,7 +2151,7 @@ static bool test_durable_open_lock_oplock(struct torture_context *tctx, > } > > /* >- Open, take BRL, disconnect, reconnect. >+ Open(RWH), take BRL, disconnect, reconnect. > */ > static bool test_durable_open_lock_lease(struct torture_context *tctx, > struct smb2_tree *tree) >@@ -2249,6 +2249,100 @@ static bool test_durable_open_lock_lease(struct torture_context *tctx, > return ret; > } > >+/* >+ Open(RH), take BRL, disconnect, fails reconnect without W LEASE >+*/ >+static bool test_durable_open_lock_noW_lease(struct torture_context *tctx, >+ struct smb2_tree *tree) >+{ >+ TALLOC_CTX *mem_ctx = talloc_new(tctx); >+ struct smb2_create io; >+ struct smb2_lease ls; >+ struct smb2_handle h = {{0}}; >+ struct smb2_lock lck; >+ struct smb2_lock_element el[2]; >+ NTSTATUS status; >+ char fname[256]; >+ bool ret = true; >+ uint64_t lease; >+ uint32_t caps; >+ struct smbcli_options options; >+ >+ options = tree->session->transport->options; >+ >+ caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); >+ if (!(caps & SMB2_CAP_LEASING)) { >+ torture_skip(tctx, "leases are not supported"); >+ } >+ >+ /* >+ * Choose a random name and random lease in case the state is left a >+ * little funky. >+ */ >+ lease = random(); >+ snprintf(fname, 256, "durable_open_lease_noW_lock_%s.dat", generate_random_str(tctx, 8)); >+ >+ /* Clean slate */ >+ smb2_util_unlink(tree, fname); >+ >+ /* Create with lease */ >+ >+ smb2_lease_create(&io, &ls, false /* dir */, fname, lease, >+ smb2_util_lease_state("RH")); >+ io.in.durable_open = true; >+ >+ 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_VAL(io.out.durable_open, true); >+ CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); >+ CHECK_VAL(io.out.lease_response.lease_key.data[0], lease); >+ CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease); >+ CHECK_VAL(io.out.lease_response.lease_state, >+ SMB2_LEASE_READ|SMB2_LEASE_HANDLE); >+ >+ ZERO_STRUCT(lck); >+ ZERO_STRUCT(el); >+ lck.in.locks = el; >+ lck.in.lock_count = 0x0001; >+ lck.in.lock_sequence = 0x00000000; >+ lck.in.file.handle = h; >+ el[0].offset = 0; >+ el[0].length = 1; >+ el[0].reserved = 0x00000000; >+ el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE; >+ status = smb2_lock(tree, &lck); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ >+ /* Disconnect/Reconnect. */ >+ talloc_free(tree); >+ tree = NULL; >+ >+ if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) { >+ torture_warning(tctx, "couldn't reconnect, bailing\n"); >+ ret = false; >+ goto done; >+ } >+ >+ ZERO_STRUCT(io); >+ io.in.fname = fname; >+ io.in.durable_handle = &h; >+ io.in.lease_request = &ls; >+ >+ status = smb2_create(tree, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); >+ h = io.out.file.handle; >+ >+ done: >+ smb2_util_close(tree, h); >+ smb2_util_unlink(tree, fname); >+ talloc_free(tree); >+ >+ return ret; >+} >+ > /** > * Open with a RH lease, disconnect, open in another tree, reconnect. > * >@@ -2823,6 +2917,7 @@ struct torture_suite *torture_smb2_durable_open_init(TALLOC_CTX *ctx) > torture_suite_add_2smb2_test(suite, "lease", test_durable_open_lease); > torture_suite_add_1smb2_test(suite, "lock-oplock", test_durable_open_lock_oplock); > torture_suite_add_1smb2_test(suite, "lock-lease", test_durable_open_lock_lease); >+ torture_suite_add_1smb2_test(suite, "lock-noW-lease", test_durable_open_lock_noW_lease); > torture_suite_add_2smb2_test(suite, "open2-lease", > test_durable_open_open2_lease); > torture_suite_add_2smb2_test(suite, "open2-oplock", >-- >2.34.1 > > >From 1326b85f0d271c43527bfc859fff595856f3b1ee Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >Date: Fri, 30 Aug 2024 17:38:02 +0200 >Subject: [PATCH 04/10] s4:torture/smb2: add > smb2.durable-v2-open.lock-{oplock,lease,noW-lease} > >This demonstrates that a W lease is required for a >durable handle to be durable when it has byte range locks. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15649 >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15651 > >Signed-off-by: Stefan Metzmacher <metze@samba.org> >Reviewed-by: Ralph Boehme <slow@samba.org> >(cherry picked from commit 8884d617310b47375e38c0386433c5e183703454) >--- > selftest/knownfail.d/smb2.durable.lock | 1 + > source4/torture/smb2/durable_v2_open.c | 336 +++++++++++++++++++++++++ > 2 files changed, 337 insertions(+) > >diff --git a/selftest/knownfail.d/smb2.durable.lock b/selftest/knownfail.d/smb2.durable.lock >index 3e3bd80ba463..16273fb4ad45 100644 >--- a/selftest/knownfail.d/smb2.durable.lock >+++ b/selftest/knownfail.d/smb2.durable.lock >@@ -1 +1,2 @@ > ^samba3.smb2.durable-open.lock-noW-lease >+^samba3.smb2.durable-v2-open.lock-noW-lease >diff --git a/source4/torture/smb2/durable_v2_open.c b/source4/torture/smb2/durable_v2_open.c >index 7eed4327b52e..685ef80c0cc2 100644 >--- a/source4/torture/smb2/durable_v2_open.c >+++ b/source4/torture/smb2/durable_v2_open.c >@@ -41,6 +41,30 @@ > CHECK_VAL((__io)->out.reserved2, 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) >+ > static struct { > int count; > struct smb2_close cl; >@@ -1731,6 +1755,315 @@ done: > return ret; > } > >+/* >+ Open(BATCH), take BRL, disconnect, reconnect. >+*/ >+static bool test_durable_v2_open_lock_oplock(struct torture_context *tctx, >+ struct smb2_tree *tree) >+{ >+ TALLOC_CTX *mem_ctx = talloc_new(tctx); >+ struct smb2_create io; >+ struct GUID create_guid = GUID_random(); >+ struct smb2_handle h = {{0}}; >+ struct smb2_lock lck; >+ struct smb2_lock_element el[2]; >+ NTSTATUS status; >+ char fname[256]; >+ bool ret = true; >+ struct smbcli_options options; >+ >+ options = tree->session->transport->options; >+ >+ snprintf(fname, 256, "durable_v2_open_lock_oplock_%s.dat", generate_random_str(tctx, 8)); >+ >+ /* Clean slate */ >+ smb2_util_unlink(tree, fname); >+ >+ /* Create with lease */ >+ >+ smb2_oplock_create_share(&io, fname, >+ smb2_util_share_access(""), >+ smb2_util_oplock_level("b")); >+ io.in.durable_open = false; >+ io.in.durable_open_v2 = true; >+ io.in.persistent_open = false; >+ io.in.create_guid = create_guid; >+ io.in.timeout = UINT32_MAX; >+ >+ 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_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, true); >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b")); >+ >+ ZERO_STRUCT(lck); >+ ZERO_STRUCT(el); >+ lck.in.locks = el; >+ lck.in.lock_count = 0x0001; >+ lck.in.lock_sequence = 0x00000000; >+ lck.in.file.handle = h; >+ el[0].offset = 0; >+ el[0].length = 1; >+ el[0].reserved = 0x00000000; >+ el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE; >+ status = smb2_lock(tree, &lck); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ >+ /* Disconnect/Reconnect. */ >+ talloc_free(tree); >+ tree = NULL; >+ >+ if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) { >+ torture_warning(tctx, "couldn't reconnect, bailing\n"); >+ ret = false; >+ goto done; >+ } >+ >+ ZERO_STRUCT(io); >+ io.in.fname = fname; >+ io.in.durable_open_v2 = false; >+ io.in.durable_handle_v2 = &h; >+ io.in.create_guid = create_guid; >+ >+ status = smb2_create(tree, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ h = io.out.file.handle; >+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */ >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b")); >+ >+ lck.in.file.handle = h; >+ el[0].flags = SMB2_LOCK_FLAG_UNLOCK; >+ status = smb2_lock(tree, &lck); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ >+ done: >+ smb2_util_close(tree, h); >+ smb2_util_unlink(tree, fname); >+ talloc_free(tree); >+ >+ return ret; >+} >+ >+/* >+ Open(RWH), take BRL, disconnect, reconnect. >+*/ >+static bool test_durable_v2_open_lock_lease(struct torture_context *tctx, >+ struct smb2_tree *tree) >+{ >+ TALLOC_CTX *mem_ctx = talloc_new(tctx); >+ struct smb2_create io; >+ struct smb2_lease ls; >+ struct GUID create_guid = GUID_random(); >+ struct smb2_handle h = {{0}}; >+ struct smb2_lock lck; >+ struct smb2_lock_element el[2]; >+ NTSTATUS status; >+ char fname[256]; >+ bool ret = true; >+ uint64_t lease; >+ uint32_t caps; >+ struct smbcli_options options; >+ >+ options = tree->session->transport->options; >+ >+ caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); >+ if (!(caps & SMB2_CAP_LEASING)) { >+ torture_skip(tctx, "leases are not supported"); >+ } >+ >+ /* >+ * Choose a random name and random lease in case the state is left a >+ * little funky. >+ */ >+ lease = random(); >+ snprintf(fname, 256, "durable_v2_open_lock_lease_%s.dat", generate_random_str(tctx, 8)); >+ >+ /* Clean slate */ >+ smb2_util_unlink(tree, fname); >+ >+ /* Create with lease */ >+ >+ smb2_lease_v2_create(&io, &ls, false /* dir */, fname, >+ lease, 0, /* parent lease key */ >+ smb2_util_lease_state("RWH"), 0 /* lease epoch */); >+ io.in.durable_open = false; >+ io.in.durable_open_v2 = true; >+ io.in.persistent_open = false; >+ io.in.create_guid = create_guid; >+ io.in.timeout = UINT32_MAX; >+ >+ 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_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, true); >+ CHECK_VAL(io.out.persistent_open, false); >+ ls.lease_epoch += 1; >+ CHECK_LEASE_V2(&io, "RWH", true, lease, >+ 0, 0, ls.lease_epoch); >+ >+ ZERO_STRUCT(lck); >+ ZERO_STRUCT(el); >+ lck.in.locks = el; >+ lck.in.lock_count = 0x0001; >+ lck.in.lock_sequence = 0x00000000; >+ lck.in.file.handle = h; >+ el[0].offset = 0; >+ el[0].length = 1; >+ el[0].reserved = 0x00000000; >+ el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE; >+ status = smb2_lock(tree, &lck); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ >+ /* Disconnect/Reconnect. */ >+ talloc_free(tree); >+ tree = NULL; >+ >+ if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) { >+ torture_warning(tctx, "couldn't reconnect, bailing\n"); >+ ret = false; >+ goto done; >+ } >+ >+ ZERO_STRUCT(io); >+ io.in.fname = fname; >+ io.in.durable_open_v2 = false; >+ io.in.durable_handle_v2 = &h; >+ io.in.create_guid = create_guid; >+ io.in.lease_request_v2 = &ls; >+ io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE; >+ >+ status = smb2_create(tree, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ h = io.out.file.handle; >+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */ >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_LEASE_V2(&io, "RWH", true, lease, >+ 0, 0, ls.lease_epoch); >+ >+ lck.in.file.handle = h; >+ el[0].flags = SMB2_LOCK_FLAG_UNLOCK; >+ status = smb2_lock(tree, &lck); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ >+ done: >+ smb2_util_close(tree, h); >+ smb2_util_unlink(tree, fname); >+ talloc_free(tree); >+ >+ return ret; >+} >+ >+/* >+ Open(RH), take BRL, disconnect, fails reconnect without W LEASE >+*/ >+static bool test_durable_v2_open_lock_noW_lease(struct torture_context *tctx, >+ struct smb2_tree *tree) >+{ >+ TALLOC_CTX *mem_ctx = talloc_new(tctx); >+ struct smb2_create io; >+ struct smb2_lease ls; >+ struct GUID create_guid = GUID_random(); >+ struct smb2_handle h = {{0}}; >+ struct smb2_lock lck; >+ struct smb2_lock_element el[2]; >+ NTSTATUS status; >+ char fname[256]; >+ bool ret = true; >+ uint64_t lease; >+ uint32_t caps; >+ struct smbcli_options options; >+ >+ options = tree->session->transport->options; >+ >+ caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); >+ if (!(caps & SMB2_CAP_LEASING)) { >+ torture_skip(tctx, "leases are not supported"); >+ } >+ >+ /* >+ * Choose a random name and random lease in case the state is left a >+ * little funky. >+ */ >+ lease = random(); >+ snprintf(fname, 256, "durable_v2_open_lock_noW_lease_%s.dat", generate_random_str(tctx, 8)); >+ >+ /* Clean slate */ >+ smb2_util_unlink(tree, fname); >+ >+ /* Create with lease */ >+ >+ smb2_lease_v2_create(&io, &ls, false /* dir */, fname, >+ lease, 0, /* parent lease key */ >+ smb2_util_lease_state("RH"), 0 /* lease epoch */); >+ io.in.durable_open = false; >+ io.in.durable_open_v2 = true; >+ io.in.persistent_open = false; >+ io.in.create_guid = create_guid; >+ io.in.timeout = UINT32_MAX; >+ >+ 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_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, true); >+ CHECK_VAL(io.out.persistent_open, false); >+ ls.lease_epoch += 1; >+ CHECK_LEASE_V2(&io, "RH", true, lease, >+ 0, 0, ls.lease_epoch); >+ >+ ZERO_STRUCT(lck); >+ ZERO_STRUCT(el); >+ lck.in.locks = el; >+ lck.in.lock_count = 0x0001; >+ lck.in.lock_sequence = 0x00000000; >+ lck.in.file.handle = h; >+ el[0].offset = 0; >+ el[0].length = 1; >+ el[0].reserved = 0x00000000; >+ el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE; >+ status = smb2_lock(tree, &lck); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ >+ /* Disconnect/Reconnect. */ >+ talloc_free(tree); >+ tree = NULL; >+ >+ if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) { >+ torture_warning(tctx, "couldn't reconnect, bailing\n"); >+ ret = false; >+ goto done; >+ } >+ >+ ZERO_STRUCT(io); >+ io.in.fname = fname; >+ io.in.durable_open_v2 = false; >+ io.in.durable_handle_v2 = &h; >+ io.in.create_guid = create_guid; >+ io.in.lease_request_v2 = &ls; >+ io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE; >+ >+ status = smb2_create(tree, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); >+ >+ done: >+ smb2_util_close(tree, h); >+ smb2_util_unlink(tree, fname); >+ talloc_free(tree); >+ >+ return ret; >+} >+ > /** > * Test durable request / reconnect with AppInstanceId > */ >@@ -2157,6 +2490,9 @@ struct torture_suite *torture_smb2_durable_v2_open_init(TALLOC_CTX *ctx) > torture_suite_add_1smb2_test(suite, "reopen2-lease", test_durable_v2_open_reopen2_lease); > torture_suite_add_1smb2_test(suite, "reopen2-lease-v2", test_durable_v2_open_reopen2_lease_v2); > torture_suite_add_1smb2_test(suite, "durable-v2-setinfo", test_durable_v2_setinfo); >+ torture_suite_add_1smb2_test(suite, "lock-oplock", test_durable_v2_open_lock_oplock); >+ torture_suite_add_1smb2_test(suite, "lock-lease", test_durable_v2_open_lock_lease); >+ torture_suite_add_1smb2_test(suite, "lock-noW-lease", test_durable_v2_open_lock_noW_lease); > torture_suite_add_2smb2_test(suite, "app-instance", test_durable_v2_open_app_instance); > torture_suite_add_1smb2_test(suite, "persistent-open-oplock", test_persistent_open_oplock); > torture_suite_add_1smb2_test(suite, "persistent-open-lease", test_persistent_open_lease); >-- >2.34.1 > > >From f58d4e0ac031ee1e1cf9485dbe2ac3ab35176172 Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >Date: Fri, 30 Aug 2024 18:10:16 +0200 >Subject: [PATCH 05/10] s3:smbd: only store durable handles with byte range > locks when having WRITE lease > >This simplifies the reconnect assumptions, when we want to allow >more than one durable handle on a file for multiple clients with >READ+HANDLE leases. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15649 >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15651 > >Signed-off-by: Stefan Metzmacher <metze@samba.org> >Reviewed-by: Ralph Boehme <slow@samba.org> >(cherry picked from commit 0893ae88180137d44f17196234f657d362543ff5) >--- > selftest/knownfail.d/smb2.durable.lock | 2 -- > source3/smbd/durable.c | 6 ++++++ > 2 files changed, 6 insertions(+), 2 deletions(-) > delete mode 100644 selftest/knownfail.d/smb2.durable.lock > >diff --git a/selftest/knownfail.d/smb2.durable.lock b/selftest/knownfail.d/smb2.durable.lock >deleted file mode 100644 >index 16273fb4ad45..000000000000 >--- a/selftest/knownfail.d/smb2.durable.lock >+++ /dev/null >@@ -1,2 +0,0 @@ >-^samba3.smb2.durable-open.lock-noW-lease >-^samba3.smb2.durable-v2-open.lock-noW-lease >diff --git a/source3/smbd/durable.c b/source3/smbd/durable.c >index dfb87dd3775c..2325e69cccc6 100644 >--- a/source3/smbd/durable.c >+++ b/source3/smbd/durable.c >@@ -173,6 +173,12 @@ NTSTATUS vfs_default_durable_disconnect(struct files_struct *fsp, > return NT_STATUS_NOT_SUPPORTED; > } > >+ if (fsp->current_lock_count != 0 && >+ (fsp_lease_type(fsp) & SMB2_LEASE_WRITE) == 0) >+ { >+ return NT_STATUS_NOT_SUPPORTED; >+ } >+ > /* > * For now let it be simple and do not keep > * delete on close files durable open >-- >2.34.1 > > >From 3e65c9d482151cc01aeff3f2933210eeae4e6e25 Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >Date: Wed, 28 Aug 2024 16:48:27 +0200 >Subject: [PATCH 06/10] s4:torture/smb2: add > smb2.durable-v2-open.{[non]stat[RH]-and,two-same,two-different}-lease > >These show that it's possible to have durable handles in addition >of stat opens, as well as multiple durable opens with RH leases. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15649 >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15651 > >Signed-off-by: Stefan Metzmacher <metze@samba.org> >Reviewed-by: Ralph Boehme <slow@samba.org> >(cherry picked from commit 77c7741f39a0a9789bede7c4722bd3f35d4af3fd) >--- > .../knownfail.d/smb2.durable-v2-open.bug15649 | 2 + > .../knownfail.d/smb2.durable-v2-open.bug15651 | 3 + > source4/torture/smb2/durable_v2_open.c | 784 ++++++++++++++++++ > 3 files changed, 789 insertions(+) > create mode 100644 selftest/knownfail.d/smb2.durable-v2-open.bug15649 > create mode 100644 selftest/knownfail.d/smb2.durable-v2-open.bug15651 > >diff --git a/selftest/knownfail.d/smb2.durable-v2-open.bug15649 b/selftest/knownfail.d/smb2.durable-v2-open.bug15649 >new file mode 100644 >index 000000000000..748b6c3150ec >--- /dev/null >+++ b/selftest/knownfail.d/smb2.durable-v2-open.bug15649 >@@ -0,0 +1,2 @@ >+^samba3.smb2.durable-v2-open.stat-and-lease >+^samba3.smb2.durable-v2-open.nonstat-and-lease >diff --git a/selftest/knownfail.d/smb2.durable-v2-open.bug15651 b/selftest/knownfail.d/smb2.durable-v2-open.bug15651 >new file mode 100644 >index 000000000000..1bb0a70d9a0d >--- /dev/null >+++ b/selftest/knownfail.d/smb2.durable-v2-open.bug15651 >@@ -0,0 +1,3 @@ >+^samba3.smb2.durable-v2-open.statRH-and-lease >+^samba3.smb2.durable-v2-open.two-same-lease >+^samba3.smb2.durable-v2-open.two-different-lease >diff --git a/source4/torture/smb2/durable_v2_open.c b/source4/torture/smb2/durable_v2_open.c >index 685ef80c0cc2..e86b1955092a 100644 >--- a/source4/torture/smb2/durable_v2_open.c >+++ b/source4/torture/smb2/durable_v2_open.c >@@ -26,6 +26,7 @@ > #include "torture/torture.h" > #include "torture/smb2/proto.h" > #include "librpc/ndr/libndr.h" >+#include "lease_break_handler.h" > > #define CHECK_VAL(v, correct) \ > torture_assert_u64_equal_goto(tctx, v, correct, ret, done, __location__) >@@ -2064,6 +2065,784 @@ static bool test_durable_v2_open_lock_noW_lease(struct torture_context *tctx, > return ret; > } > >+/** >+ * 1. stat open (without lease) => h1 >+ * 2. durable open with RWH => h2 >+ * 3. disconnect >+ * 4. reconnect >+ * 5. durable reconnect RWH => h2 >+ */ >+static bool test_durable_v2_open_stat_and_lease(struct torture_context *tctx, >+ struct smb2_tree *tree1) >+{ >+ TALLOC_CTX *mem_ctx = talloc_new(tctx); >+ NTSTATUS status; >+ char fname[256]; >+ struct smb2_handle dh; >+ struct smb2_handle _h1; >+ struct smb2_handle *h1 = NULL; >+ struct smb2_handle _h2; >+ struct smb2_handle *h2 = NULL; >+ struct smb2_create io; >+ struct GUID create_guid2 = GUID_random(); >+ struct smb2_lease ls; >+ uint64_t lease_key; >+ bool ret = true; >+ struct smbcli_options options1; >+ uint32_t caps; >+ >+ caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn); >+ if (!(caps & SMB2_CAP_LEASING)) { >+ torture_skip(tctx, "leases are not supported"); >+ } >+ >+ options1 = tree1->session->transport->options; >+ >+ smb2_deltree(tree1, __func__); >+ status = torture_smb2_testdir(tree1, __func__, &dh); >+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done, >+ "torture_smb2_testdir failed\n"); >+ smb2_util_close(tree1, dh); >+ >+ /* Choose a random name in case the state is left a little funky. */ >+ snprintf(fname, 256, "%s\\file_%s.dat", >+ __func__, generate_random_str(tctx, 8)); >+ >+ smb2_util_unlink(tree1, fname); >+ >+ smb2_generic_create(&io, NULL, false /* dir */, fname, >+ FILE_OPEN_IF, 0, 0, 0); >+ io.in.desired_access = SEC_FILE_READ_ATTRIBUTE; >+ io.in.desired_access |= SEC_FILE_WRITE_ATTRIBUTE; >+ io.in.desired_access |= SEC_STD_SYNCHRONIZE; >+ >+ status = smb2_create(tree1, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h1 = io.out.file.handle; >+ h1 = &_h1; >+ CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, false); >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); >+ >+ lease_key = random(); >+ smb2_lease_v2_create(&io, &ls, false /* dir */, fname, >+ lease_key, 0, /* parent lease key */ >+ smb2_util_lease_state("RWH"), 0 /* lease epoch */); >+ io.in.durable_open = false; >+ io.in.durable_open_v2 = true; >+ io.in.persistent_open = false; >+ io.in.create_guid = create_guid2; >+ io.in.timeout = UINT32_MAX; >+ >+ status = smb2_create(tree1, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h2 = io.out.file.handle; >+ h2 = &_h2; >+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, true); >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_VAL(io.out.timeout, 300*1000); >+ ls.lease_epoch += 1; >+ CHECK_LEASE_V2(&io, "RWH", true, lease_key, >+ 0, 0, ls.lease_epoch); >+ >+ /* disconnect, reconnect and then do durable reopen */ >+ TALLOC_FREE(tree1); >+ h1 = NULL; >+ >+ if (!torture_smb2_connection_ext(tctx, 0, &options1, &tree1)) { >+ torture_warning(tctx, "couldn't reconnect, bailing\n"); >+ ret = false; >+ goto done; >+ } >+ >+ ZERO_STRUCT(io); >+ io.in.fname = fname; >+ io.in.durable_open_v2 = false; >+ io.in.durable_handle_v2 = h2; >+ io.in.create_guid = create_guid2; >+ io.in.lease_request_v2 = &ls; >+ io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE; >+ >+ status = smb2_create(tree1, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h2 = io.out.file.handle; >+ h2 = &_h2; >+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */ >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_LEASE_V2(&io, "RWH", true, lease_key, >+ 0, 0, ls.lease_epoch); >+ >+ status = smb2_util_close(tree1, *h2); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ h2 = NULL; >+ >+done: >+ if (tree1 != NULL) { >+ smb2_keepalive(tree1->session->transport); >+ } >+ >+ if (tree1 != NULL && h1 != NULL) { >+ smb2_util_close(tree1, *h1); >+ } >+ if (tree1 != NULL && h2 != NULL) { >+ smb2_util_close(tree1, *h2); >+ } >+ >+ if (tree1 != NULL) { >+ smb2_util_unlink(tree1, fname); >+ smb2_deltree(tree1, __func__); >+ >+ TALLOC_FREE(tree1); >+ } >+ >+ talloc_free(mem_ctx); >+ >+ return ret; >+} >+ >+/** >+ * 1. non stat open (without a lease) => h1 >+ * 2. durable open with RWH => h2 => RH >+ * 3. disconnect >+ * 4. reconnect >+ * 5. durable reconnect RH => h2 >+ */ >+static bool test_durable_v2_open_nonstat_and_lease(struct torture_context *tctx, >+ struct smb2_tree *tree1) >+{ >+ TALLOC_CTX *mem_ctx = talloc_new(tctx); >+ NTSTATUS status; >+ char fname[256]; >+ struct smb2_handle dh; >+ struct smb2_handle _h1; >+ struct smb2_handle *h1 = NULL; >+ struct smb2_handle _h2; >+ struct smb2_handle *h2 = NULL; >+ struct smb2_create io; >+ struct GUID create_guid2 = GUID_random(); >+ struct smb2_lease ls; >+ uint64_t lease_key; >+ bool ret = true; >+ struct smbcli_options options1; >+ uint32_t caps; >+ >+ caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn); >+ if (!(caps & SMB2_CAP_LEASING)) { >+ torture_skip(tctx, "leases are not supported"); >+ } >+ >+ options1 = tree1->session->transport->options; >+ >+ smb2_deltree(tree1, __func__); >+ status = torture_smb2_testdir(tree1, __func__, &dh); >+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done, >+ "torture_smb2_testdir failed\n"); >+ smb2_util_close(tree1, dh); >+ >+ /* Choose a random name in case the state is left a little funky. */ >+ snprintf(fname, 256, "%s\\file_%s.dat", >+ __func__, generate_random_str(tctx, 8)); >+ >+ smb2_util_unlink(tree1, fname); >+ >+ smb2_generic_create(&io, NULL, false /* dir */, fname, >+ FILE_OPEN_IF, 0, 0, 0); >+ io.in.desired_access = SEC_FILE_READ_ATTRIBUTE; >+ io.in.desired_access |= SEC_FILE_WRITE_ATTRIBUTE; >+ io.in.desired_access |= SEC_STD_SYNCHRONIZE; >+ /* >+ * SEC_STD_READ_CONTROL means we no longer >+ * have a stat open that would allow a RWH lease >+ */ >+ io.in.desired_access |= SEC_STD_READ_CONTROL; >+ >+ status = smb2_create(tree1, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h1 = io.out.file.handle; >+ h1 = &_h1; >+ CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, false); >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); >+ >+ lease_key = random(); >+ smb2_lease_v2_create(&io, &ls, false /* dir */, fname, >+ lease_key, 0, /* parent lease key */ >+ smb2_util_lease_state("RWH"), 0 /* lease epoch */); >+ io.in.durable_open = false; >+ io.in.durable_open_v2 = true; >+ io.in.persistent_open = false; >+ io.in.create_guid = create_guid2; >+ io.in.timeout = UINT32_MAX; >+ >+ status = smb2_create(tree1, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h2 = io.out.file.handle; >+ h2 = &_h2; >+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, true); >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_VAL(io.out.timeout, 300*1000); >+ ls.lease_epoch += 1; >+ CHECK_LEASE_V2(&io, "RH", true, lease_key, >+ 0, 0, ls.lease_epoch); >+ >+ /* disconnect, reconnect and then do durable reopen */ >+ TALLOC_FREE(tree1); >+ h1 = NULL; >+ >+ if (!torture_smb2_connection_ext(tctx, 0, &options1, &tree1)) { >+ torture_warning(tctx, "couldn't reconnect, bailing\n"); >+ ret = false; >+ goto done; >+ } >+ >+ ZERO_STRUCT(io); >+ io.in.fname = fname; >+ io.in.durable_open_v2 = false; >+ io.in.durable_handle_v2 = h2; >+ io.in.create_guid = create_guid2; >+ io.in.lease_request_v2 = &ls; >+ io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE; >+ >+ status = smb2_create(tree1, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h2 = io.out.file.handle; >+ h2 = &_h2; >+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */ >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_LEASE_V2(&io, "RH", true, lease_key, >+ 0, 0, ls.lease_epoch); >+ >+ status = smb2_util_close(tree1, *h2); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ h2 = NULL; >+ >+done: >+ if (tree1 != NULL) { >+ smb2_keepalive(tree1->session->transport); >+ } >+ >+ if (tree1 != NULL && h1 != NULL) { >+ smb2_util_close(tree1, *h1); >+ } >+ if (tree1 != NULL && h2 != NULL) { >+ smb2_util_close(tree1, *h2); >+ } >+ >+ if (tree1 != NULL) { >+ smb2_util_unlink(tree1, fname); >+ smb2_deltree(tree1, __func__); >+ >+ TALLOC_FREE(tree1); >+ } >+ >+ talloc_free(mem_ctx); >+ >+ return ret; >+} >+ >+/** >+ * 1. stat open with RH lease => h1 >+ * 2. durable open with RWH => h2 => RH >+ * 3. disconnect >+ * 4. reconnect >+ * 5. durable reconnect RH => h2 >+ */ >+static bool test_durable_v2_open_statRH_and_lease(struct torture_context *tctx, >+ struct smb2_tree *tree1) >+{ >+ TALLOC_CTX *mem_ctx = talloc_new(tctx); >+ NTSTATUS status; >+ char fname[256]; >+ struct smb2_handle dh; >+ struct smb2_handle _h1; >+ struct smb2_handle *h1 = NULL; >+ struct smb2_handle _h2; >+ struct smb2_handle *h2 = NULL; >+ struct smb2_create io; >+ struct GUID create_guid2 = GUID_random(); >+ struct smb2_lease ls; >+ uint64_t lease_key; >+ bool ret = true; >+ struct smbcli_options options1; >+ uint32_t caps; >+ >+ caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn); >+ if (!(caps & SMB2_CAP_LEASING)) { >+ torture_skip(tctx, "leases are not supported"); >+ } >+ >+ options1 = tree1->session->transport->options; >+ >+ smb2_deltree(tree1, __func__); >+ status = torture_smb2_testdir(tree1, __func__, &dh); >+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done, >+ "torture_smb2_testdir failed\n"); >+ smb2_util_close(tree1, dh); >+ >+ /* Choose a random name in case the state is left a little funky. */ >+ snprintf(fname, 256, "%s\\file_%s.dat", >+ __func__, generate_random_str(tctx, 8)); >+ >+ smb2_util_unlink(tree1, fname); >+ >+ smb2_generic_create(&io, NULL, false /* dir */, fname, >+ FILE_OPEN_IF, 0, 0, 0); >+ lease_key = random(); >+ smb2_lease_v2_create(&io, &ls, false /* dir */, fname, >+ lease_key, 0, /* parent lease key */ >+ smb2_util_lease_state("RH"), 0 /* lease epoch */); >+ io.in.desired_access = SEC_FILE_READ_ATTRIBUTE; >+ >+ status = smb2_create(tree1, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h1 = io.out.file.handle; >+ h1 = &_h1; >+ CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, false); >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); >+ ls.lease_epoch += 1; >+ CHECK_LEASE_V2(&io, "RH", true, lease_key, >+ 0, 0, ls.lease_epoch); >+ >+ lease_key = random(); >+ smb2_lease_v2_create(&io, &ls, false /* dir */, fname, >+ lease_key, 0, /* parent lease key */ >+ smb2_util_lease_state("RWH"), 0 /* lease epoch */); >+ io.in.durable_open = false; >+ io.in.durable_open_v2 = true; >+ io.in.persistent_open = false; >+ io.in.create_guid = create_guid2; >+ io.in.timeout = UINT32_MAX; >+ >+ status = smb2_create(tree1, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h2 = io.out.file.handle; >+ h2 = &_h2; >+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, true); >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_VAL(io.out.timeout, 300*1000); >+ ls.lease_epoch += 1; >+ CHECK_LEASE_V2(&io, "RH", true, lease_key, >+ 0, 0, ls.lease_epoch); >+ >+ /* disconnect, reconnect and then do durable reopen */ >+ TALLOC_FREE(tree1); >+ h1 = NULL; >+ >+ if (!torture_smb2_connection_ext(tctx, 0, &options1, &tree1)) { >+ torture_warning(tctx, "couldn't reconnect, bailing\n"); >+ ret = false; >+ goto done; >+ } >+ >+ ZERO_STRUCT(io); >+ io.in.fname = fname; >+ io.in.durable_open_v2 = false; >+ io.in.durable_handle_v2 = h2; >+ io.in.create_guid = create_guid2; >+ io.in.lease_request_v2 = &ls; >+ io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE; >+ >+ status = smb2_create(tree1, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h2 = io.out.file.handle; >+ h2 = &_h2; >+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */ >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_LEASE_V2(&io, "RH", true, lease_key, >+ 0, 0, ls.lease_epoch); >+ >+ status = smb2_util_close(tree1, *h2); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ h2 = NULL; >+ >+done: >+ if (tree1 != NULL) { >+ smb2_keepalive(tree1->session->transport); >+ } >+ >+ if (tree1 != NULL && h1 != NULL) { >+ smb2_util_close(tree1, *h1); >+ } >+ if (tree1 != NULL && h2 != NULL) { >+ smb2_util_close(tree1, *h2); >+ } >+ >+ if (tree1 != NULL) { >+ smb2_util_unlink(tree1, fname); >+ smb2_deltree(tree1, __func__); >+ >+ TALLOC_FREE(tree1); >+ } >+ >+ talloc_free(mem_ctx); >+ >+ return ret; >+} >+ >+/** >+ * 1. durable open with L1(RWH) => h1 >+ * 2. durable open with L1(RWH) => h2 >+ * 3. disconnect >+ * 4. reconnect >+ * 5. durable reconnect L1(RWH) => h1 >+ * 6. durable reconnect L1(RWH) => h2 >+ */ >+static bool test_durable_v2_open_two_same_lease(struct torture_context *tctx, >+ struct smb2_tree *tree1) >+{ >+ TALLOC_CTX *mem_ctx = talloc_new(tctx); >+ NTSTATUS status; >+ char fname[256]; >+ struct smb2_handle dh; >+ struct smb2_handle _h1; >+ struct smb2_handle *h1 = NULL; >+ struct smb2_handle _h2; >+ struct smb2_handle *h2 = NULL; >+ struct smb2_create io; >+ struct GUID create_guid1 = GUID_random(); >+ struct GUID create_guid2 = GUID_random(); >+ struct smb2_lease ls; >+ uint64_t lease_key; >+ bool ret = true; >+ struct smbcli_options options1; >+ uint32_t caps; >+ >+ caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn); >+ if (!(caps & SMB2_CAP_LEASING)) { >+ torture_skip(tctx, "leases are not supported"); >+ } >+ >+ options1 = tree1->session->transport->options; >+ >+ smb2_deltree(tree1, __func__); >+ status = torture_smb2_testdir(tree1, __func__, &dh); >+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done, >+ "torture_smb2_testdir failed\n"); >+ smb2_util_close(tree1, dh); >+ >+ /* Choose a random name in case the state is left a little funky. */ >+ snprintf(fname, 256, "%s\\file_%s.dat", >+ __func__, generate_random_str(tctx, 8)); >+ >+ smb2_util_unlink(tree1, fname); >+ >+ lease_key = random(); >+ smb2_lease_v2_create(&io, &ls, false /* dir */, fname, >+ lease_key, 0, /* parent lease key */ >+ smb2_util_lease_state("RWH"), 0 /* lease epoch */); >+ io.in.durable_open = false; >+ io.in.durable_open_v2 = true; >+ io.in.persistent_open = false; >+ io.in.create_guid = create_guid1; >+ io.in.timeout = UINT32_MAX; >+ >+ status = smb2_create(tree1, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h1 = io.out.file.handle; >+ h1 = &_h1; >+ CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, true); >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_VAL(io.out.timeout, 300*1000); >+ ls.lease_epoch += 1; >+ CHECK_LEASE_V2(&io, "RWH", true, lease_key, >+ 0, 0, ls.lease_epoch); >+ >+ smb2_lease_v2_create(&io, &ls, false /* dir */, fname, >+ lease_key, 0, /* parent lease key */ >+ smb2_util_lease_state("RWH"), 0 /* lease epoch */); >+ io.in.durable_open = false; >+ io.in.durable_open_v2 = true; >+ io.in.persistent_open = false; >+ io.in.create_guid = create_guid2; >+ io.in.timeout = UINT32_MAX; >+ >+ status = smb2_create(tree1, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h2 = io.out.file.handle; >+ h2 = &_h2; >+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, true); >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_VAL(io.out.timeout, 300*1000); >+ ls.lease_epoch += 1; >+ CHECK_LEASE_V2(&io, "RWH", true, lease_key, >+ 0, 0, ls.lease_epoch); >+ >+ /* disconnect, reconnect and then do durable reopen */ >+ TALLOC_FREE(tree1); >+ >+ if (!torture_smb2_connection_ext(tctx, 0, &options1, &tree1)) { >+ torture_warning(tctx, "couldn't reconnect, bailing\n"); >+ ret = false; >+ goto done; >+ } >+ >+ ZERO_STRUCT(io); >+ io.in.fname = fname; >+ io.in.durable_open_v2 = false; >+ io.in.durable_handle_v2 = h1; >+ io.in.create_guid = create_guid1; >+ io.in.lease_request_v2 = &ls; >+ io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE; >+ >+ status = smb2_create(tree1, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h1 = io.out.file.handle; >+ h1 = &_h1; >+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */ >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_LEASE_V2(&io, "RWH", true, lease_key, >+ 0, 0, ls.lease_epoch); >+ >+ ZERO_STRUCT(io); >+ io.in.fname = fname; >+ io.in.durable_open_v2 = false; >+ io.in.durable_handle_v2 = h2; >+ io.in.create_guid = create_guid2; >+ io.in.lease_request_v2 = &ls; >+ io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE; >+ >+ status = smb2_create(tree1, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h2 = io.out.file.handle; >+ h2 = &_h2; >+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */ >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_LEASE_V2(&io, "RWH", true, lease_key, >+ 0, 0, ls.lease_epoch); >+ >+ status = smb2_util_close(tree1, *h1); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ h1 = NULL; >+ >+ status = smb2_util_close(tree1, *h2); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ h2 = NULL; >+ >+done: >+ if (tree1 != NULL) { >+ smb2_keepalive(tree1->session->transport); >+ } >+ >+ if (tree1 != NULL && h1 != NULL) { >+ smb2_util_close(tree1, *h1); >+ } >+ if (tree1 != NULL && h2 != NULL) { >+ smb2_util_close(tree1, *h2); >+ } >+ >+ if (tree1 != NULL) { >+ smb2_util_unlink(tree1, fname); >+ smb2_deltree(tree1, __func__); >+ >+ TALLOC_FREE(tree1); >+ } >+ >+ talloc_free(mem_ctx); >+ >+ return ret; >+} >+ >+/** >+ * 1. durable open with L1(RH) => h1 >+ * 2. durable open with L2(RH) => h2 >+ * 3. disconnect >+ * 4. reconnect >+ * 5. durable reconnect L1(RH) => h1 >+ * 6. durable reconnect L2(RH) => h2 >+ */ >+static bool test_durable_v2_open_two_different_leases(struct torture_context *tctx, >+ struct smb2_tree *tree1) >+{ >+ TALLOC_CTX *mem_ctx = talloc_new(tctx); >+ NTSTATUS status; >+ char fname[256]; >+ struct smb2_handle dh; >+ struct smb2_handle _h1; >+ struct smb2_handle *h1 = NULL; >+ struct smb2_handle _h2; >+ struct smb2_handle *h2 = NULL; >+ struct smb2_create io; >+ struct GUID create_guid1 = GUID_random(); >+ struct GUID create_guid2 = GUID_random(); >+ struct smb2_lease ls1; >+ uint64_t lease_key1; >+ struct smb2_lease ls2; >+ uint64_t lease_key2; >+ bool ret = true; >+ struct smbcli_options options1; >+ uint32_t caps; >+ >+ caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn); >+ if (!(caps & SMB2_CAP_LEASING)) { >+ torture_skip(tctx, "leases are not supported"); >+ } >+ >+ options1 = tree1->session->transport->options; >+ >+ smb2_deltree(tree1, __func__); >+ status = torture_smb2_testdir(tree1, __func__, &dh); >+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done, >+ "torture_smb2_testdir failed\n"); >+ smb2_util_close(tree1, dh); >+ >+ /* Choose a random name in case the state is left a little funky. */ >+ snprintf(fname, 256, "%s\\file_%s.dat", >+ __func__, generate_random_str(tctx, 8)); >+ >+ smb2_util_unlink(tree1, fname); >+ >+ lease_key1 = random(); >+ smb2_lease_v2_create(&io, &ls1, false /* dir */, fname, >+ lease_key1, 0, /* parent lease key */ >+ smb2_util_lease_state("RH"), 0 /* lease epoch */); >+ io.in.durable_open = false; >+ io.in.durable_open_v2 = true; >+ io.in.persistent_open = false; >+ io.in.create_guid = create_guid1; >+ io.in.timeout = UINT32_MAX; >+ >+ status = smb2_create(tree1, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h1 = io.out.file.handle; >+ h1 = &_h1; >+ CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, true); >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_VAL(io.out.timeout, 300*1000); >+ ls1.lease_epoch += 1; >+ CHECK_LEASE_V2(&io, "RH", true, lease_key1, >+ 0, 0, ls1.lease_epoch); >+ >+ lease_key2 = random(); >+ smb2_lease_v2_create(&io, &ls2, false /* dir */, fname, >+ lease_key2, 0, /* parent lease key */ >+ smb2_util_lease_state("RH"), 0 /* lease epoch */); >+ io.in.durable_open = false; >+ io.in.durable_open_v2 = true; >+ io.in.persistent_open = false; >+ io.in.create_guid = create_guid2; >+ io.in.timeout = UINT32_MAX; >+ >+ status = smb2_create(tree1, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h2 = io.out.file.handle; >+ h2 = &_h2; >+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, true); >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_VAL(io.out.timeout, 300*1000); >+ ls2.lease_epoch += 1; >+ CHECK_LEASE_V2(&io, "RH", true, lease_key2, >+ 0, 0, ls2.lease_epoch); >+ >+ /* disconnect, reconnect and then do durable reopen */ >+ TALLOC_FREE(tree1); >+ >+ if (!torture_smb2_connection_ext(tctx, 0, &options1, &tree1)) { >+ torture_warning(tctx, "couldn't reconnect, bailing\n"); >+ ret = false; >+ goto done; >+ } >+ >+ ZERO_STRUCT(io); >+ io.in.fname = fname; >+ io.in.durable_open_v2 = false; >+ io.in.durable_handle_v2 = h1; >+ io.in.create_guid = create_guid1; >+ io.in.lease_request_v2 = &ls1; >+ io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE; >+ >+ status = smb2_create(tree1, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h1 = io.out.file.handle; >+ h1 = &_h1; >+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */ >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_LEASE_V2(&io, "RH", true, lease_key1, >+ 0, 0, ls1.lease_epoch); >+ >+ ZERO_STRUCT(io); >+ io.in.fname = fname; >+ io.in.durable_open_v2 = false; >+ io.in.durable_handle_v2 = h2; >+ io.in.create_guid = create_guid2; >+ io.in.lease_request_v2 = &ls2; >+ io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE; >+ >+ status = smb2_create(tree1, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h2 = io.out.file.handle; >+ h2 = &_h2; >+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */ >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_LEASE_V2(&io, "RH", true, lease_key2, >+ 0, 0, ls2.lease_epoch); >+ >+ status = smb2_util_close(tree1, *h1); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ h1 = NULL; >+ >+ status = smb2_util_close(tree1, *h2); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ h2 = NULL; >+ >+done: >+ if (tree1 != NULL) { >+ smb2_keepalive(tree1->session->transport); >+ } >+ >+ if (tree1 != NULL && h1 != NULL) { >+ smb2_util_close(tree1, *h1); >+ } >+ if (tree1 != NULL && h2 != NULL) { >+ smb2_util_close(tree1, *h2); >+ } >+ >+ if (tree1 != NULL) { >+ smb2_util_unlink(tree1, fname); >+ smb2_deltree(tree1, __func__); >+ >+ TALLOC_FREE(tree1); >+ } >+ >+ talloc_free(mem_ctx); >+ >+ return ret; >+} >+ > /** > * Test durable request / reconnect with AppInstanceId > */ >@@ -2493,6 +3272,11 @@ struct torture_suite *torture_smb2_durable_v2_open_init(TALLOC_CTX *ctx) > torture_suite_add_1smb2_test(suite, "lock-oplock", test_durable_v2_open_lock_oplock); > torture_suite_add_1smb2_test(suite, "lock-lease", test_durable_v2_open_lock_lease); > torture_suite_add_1smb2_test(suite, "lock-noW-lease", test_durable_v2_open_lock_noW_lease); >+ torture_suite_add_1smb2_test(suite, "stat-and-lease", test_durable_v2_open_stat_and_lease); >+ torture_suite_add_1smb2_test(suite, "nonstat-and-lease", test_durable_v2_open_nonstat_and_lease); >+ torture_suite_add_1smb2_test(suite, "statRH-and-lease", test_durable_v2_open_statRH_and_lease); >+ torture_suite_add_1smb2_test(suite, "two-same-lease", test_durable_v2_open_two_same_lease); >+ torture_suite_add_1smb2_test(suite, "two-different-lease", test_durable_v2_open_two_different_leases); > torture_suite_add_2smb2_test(suite, "app-instance", test_durable_v2_open_app_instance); > torture_suite_add_1smb2_test(suite, "persistent-open-oplock", test_persistent_open_oplock); > torture_suite_add_1smb2_test(suite, "persistent-open-lease", test_persistent_open_lease); >-- >2.34.1 > > >From a6149e4797f3ca0527eec9f38ec881990140f54d Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >Date: Wed, 4 Sep 2024 18:18:43 +0200 >Subject: [PATCH 07/10] s4:torture/smb2: add > smb2.durable-v2-open.{keep,purge}-disconnected-* tests > >These demonstrate which durables handles are kept and which are purged >because of various opens, writes or renames. > >smb2.durable-v2-open.keep-disconnected-rh-with-stat-open >smb2.durable-v2-open.keep-disconnected-rh-with-rh-open >smb2.durable-v2-open.keep-disconnected-rh-with-rwh-open >smb2.durable-v2-open.keep-disconnected-rwh-with-stat-open > >smb2.durable-v2-open.purge-disconnected-rwh-with-rwh-open >smb2.durable-v2-open.purge-disconnected-rwh-with-rh-open >smb2.durable-v2-open.purge-disconnected-rh-with-share-none-open >smb2.durable-v2-open.purge-disconnected-rh-with-write >smb2.durable-v2-open.purge-disconnected-rh-with-rename > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15649 >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15651 >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15708 > >Signed-off-by: Stefan Metzmacher <metze@samba.org> >Reviewed-by: Ralph Boehme <slow@samba.org> >(cherry picked from commit 9e98cd5c7a180521026b0d73a330bdaf2c8af73a) >--- > .../knownfail.d/smb2.durable-v2-open.bug15651 | 2 + > .../knownfail.d/smb2.durable-v2-open.bug15708 | 7 + > source4/torture/smb2/durable_v2_open.c | 1851 +++++++++++++++++ > 3 files changed, 1860 insertions(+) > create mode 100644 selftest/knownfail.d/smb2.durable-v2-open.bug15708 > >diff --git a/selftest/knownfail.d/smb2.durable-v2-open.bug15651 b/selftest/knownfail.d/smb2.durable-v2-open.bug15651 >index 1bb0a70d9a0d..1702a3a65809 100644 >--- a/selftest/knownfail.d/smb2.durable-v2-open.bug15651 >+++ b/selftest/knownfail.d/smb2.durable-v2-open.bug15651 >@@ -1,3 +1,5 @@ > ^samba3.smb2.durable-v2-open.statRH-and-lease > ^samba3.smb2.durable-v2-open.two-same-lease > ^samba3.smb2.durable-v2-open.two-different-lease >+^samba3.smb2.durable-v2-open.keep-disconnected-rh-with-stat-open >+^samba3.smb2.durable-v2-open.keep-disconnected-rwh-with-stat-open >diff --git a/selftest/knownfail.d/smb2.durable-v2-open.bug15708 b/selftest/knownfail.d/smb2.durable-v2-open.bug15708 >new file mode 100644 >index 000000000000..3a6380c6d651 >--- /dev/null >+++ b/selftest/knownfail.d/smb2.durable-v2-open.bug15708 >@@ -0,0 +1,7 @@ >+# >+# https://bugzilla.samba.org/show_bug.cgi?id=15708 is not fixed >+# yet, it requires some complex changes within handle_share_mode_lease() >+# merging logic of open_mode_check() and delay_for_oplock()... >+# >+^samba3.smb2.durable-v2-open.keep-disconnected-rh-with-rh-open >+^samba3.smb2.durable-v2-open.keep-disconnected-rh-with-rwh-open >diff --git a/source4/torture/smb2/durable_v2_open.c b/source4/torture/smb2/durable_v2_open.c >index e86b1955092a..104796e76ad6 100644 >--- a/source4/torture/smb2/durable_v2_open.c >+++ b/source4/torture/smb2/durable_v2_open.c >@@ -2843,6 +2843,1848 @@ done: > return ret; > } > >+/** >+ * 1. durable open with L1A(RH) on tree1 => h1a >+ * 1. durable open with L1B(RH) on tree1 => h1b >+ * 2. disconnect tree1 >+ * 3. stat open on tree2 => h2 >+ * 4. reconnect tree1 >+ * 5. durable reconnect L1A(RH) => h1a >+ * 6. durable reconnect L1B(RH) => h1a >+ */ >+static bool test_durable_v2_open_keep_disconnected_rh_with_stat_open(struct torture_context *tctx, >+ struct smb2_tree *tree1, >+ struct smb2_tree *tree2) >+{ >+ TALLOC_CTX *mem_ctx = talloc_new(tctx); >+ NTSTATUS status; >+ char fname[256]; >+ struct smb2_handle dh; >+ struct smb2_handle _h1a; >+ struct smb2_handle *h1a = NULL; >+ struct smb2_handle _h1b; >+ struct smb2_handle *h1b = NULL; >+ struct smb2_handle _h2; >+ struct smb2_handle *h2 = NULL; >+ struct smb2_create io; >+ struct GUID create_guid1a = GUID_random(); >+ struct GUID create_guid1b = GUID_random(); >+ struct smb2_lease ls1a; >+ uint64_t lease_key1a; >+ struct smb2_lease ls1b; >+ uint64_t lease_key1b; >+ bool ret = true; >+ struct smbcli_options options1; >+ uint32_t caps; >+ >+ caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn); >+ if (!(caps & SMB2_CAP_LEASING)) { >+ torture_skip(tctx, "leases are not supported"); >+ } >+ >+ options1 = tree1->session->transport->options; >+ >+ tree1->session->transport->lease.handler = torture_lease_handler; >+ tree1->session->transport->lease.private_data = tree1; >+ >+ tree2->session->transport->lease.handler = torture_lease_handler; >+ tree2->session->transport->lease.private_data = tree2; >+ >+ torture_reset_lease_break_info(tctx, &lease_break_info); >+ >+ smb2_deltree(tree1, __func__); >+ status = torture_smb2_testdir(tree1, __func__, &dh); >+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done, >+ "torture_smb2_testdir failed\n"); >+ smb2_util_close(tree1, dh); >+ >+ /* Choose a random name in case the state is left a little funky. */ >+ snprintf(fname, 256, "%s\\file_%s.dat", >+ __func__, generate_random_str(tctx, 8)); >+ >+ smb2_util_unlink(tree1, fname); >+ >+ lease_key1a = random(); >+ smb2_lease_v2_create(&io, &ls1a, false /* dir */, fname, >+ lease_key1a, 0, /* parent lease key */ >+ smb2_util_lease_state("RH"), 0 /* lease epoch */); >+ io.in.durable_open = false; >+ io.in.durable_open_v2 = true; >+ io.in.persistent_open = false; >+ io.in.create_guid = create_guid1a; >+ io.in.timeout = UINT32_MAX; >+ >+ status = smb2_create(tree1, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h1a = io.out.file.handle; >+ h1a = &_h1a; >+ CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, true); >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_VAL(io.out.timeout, 300*1000); >+ ls1a.lease_epoch += 1; >+ CHECK_LEASE_V2(&io, "RH", true, lease_key1a, >+ 0, 0, ls1a.lease_epoch); >+ >+ lease_key1b = random(); >+ smb2_lease_v2_create(&io, &ls1b, false /* dir */, fname, >+ lease_key1b, 0, /* parent lease key */ >+ smb2_util_lease_state("RH"), 0 /* lease epoch */); >+ io.in.durable_open = false; >+ io.in.durable_open_v2 = true; >+ io.in.persistent_open = false; >+ io.in.create_guid = create_guid1b; >+ io.in.timeout = UINT32_MAX; >+ >+ status = smb2_create(tree1, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h1b = io.out.file.handle; >+ h1b = &_h1b; >+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, true); >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_VAL(io.out.timeout, 300*1000); >+ ls1b.lease_epoch += 1; >+ CHECK_LEASE_V2(&io, "RH", true, lease_key1b, >+ 0, 0, ls1b.lease_epoch); >+ >+ CHECK_NO_BREAK(tctx); >+ >+ /* disconnect, reconnect and then do durable reopen */ >+ TALLOC_FREE(tree1); >+ >+ CHECK_NO_BREAK(tctx); >+ >+ smb2_generic_create(&io, NULL, false /* dir */, fname, >+ FILE_OPEN_IF, 0, 0, 0); >+ io.in.desired_access = SEC_FILE_READ_ATTRIBUTE; >+ status = smb2_create(tree2, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h2 = io.out.file.handle; >+ h2 = &_h2; >+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, false); >+ CHECK_VAL(io.out.persistent_open, false); >+ >+ CHECK_NO_BREAK(tctx); >+ >+ if (!torture_smb2_connection_ext(tctx, 0, &options1, &tree1)) { >+ torture_warning(tctx, "couldn't reconnect, bailing\n"); >+ ret = false; >+ goto done; >+ } >+ >+ ZERO_STRUCT(io); >+ io.in.fname = fname; >+ io.in.durable_open_v2 = false; >+ io.in.durable_handle_v2 = h1a; >+ io.in.create_guid = create_guid1a; >+ io.in.lease_request_v2 = &ls1a; >+ io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE; >+ >+ status = smb2_create(tree1, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h1a = io.out.file.handle; >+ h1a = &_h1a; >+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */ >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_LEASE_V2(&io, "RH", true, lease_key1a, >+ 0, 0, ls1a.lease_epoch); >+ >+ ZERO_STRUCT(io); >+ io.in.fname = fname; >+ io.in.durable_open_v2 = false; >+ io.in.durable_handle_v2 = h1b; >+ io.in.create_guid = create_guid1b; >+ io.in.lease_request_v2 = &ls1b; >+ io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE; >+ >+ status = smb2_create(tree1, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h1b = io.out.file.handle; >+ h1b = &_h1b; >+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */ >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_LEASE_V2(&io, "RH", true, lease_key1b, >+ 0, 0, ls1b.lease_epoch); >+ >+ status = smb2_util_close(tree1, *h1a); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ h1a = NULL; >+ >+ status = smb2_util_close(tree1, *h1b); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ h1b = NULL; >+ >+ status = smb2_util_close(tree2, *h2); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ h2 = NULL; >+ >+ CHECK_NO_BREAK(tctx); >+ >+done: >+ if (tree1 != NULL) { >+ smb2_keepalive(tree1->session->transport); >+ } >+ if (tree2 != NULL) { >+ smb2_keepalive(tree2->session->transport); >+ } >+ if (tree1 != NULL && h1a != NULL) { >+ smb2_util_close(tree1, *h1a); >+ } >+ if (tree1 != NULL && h1b != NULL) { >+ smb2_util_close(tree1, *h1b); >+ } >+ if (tree2 != NULL && h2 != NULL) { >+ smb2_util_close(tree2, *h2); >+ } >+ >+ if (tree1 != NULL) { >+ smb2_util_unlink(tree1, fname); >+ smb2_deltree(tree1, __func__); >+ >+ TALLOC_FREE(tree1); >+ } >+ >+ TALLOC_FREE(tree2); >+ >+ talloc_free(mem_ctx); >+ >+ return ret; >+} >+ >+/** >+ * 1. durable open with L1A(RH) on tree1 => h1a >+ * 1. durable open with L1B(RH) on tree1 => h1b >+ * 2. disconnect tree1 >+ * 3. durable open with L2(RH) on tree2 => h2 >+ * 4. reconnect tree1 >+ * 5. durable reconnect L1A(RH) => h1a >+ * 6. durable reconnect L1B(RH) => h1a >+ */ >+static bool test_durable_v2_open_keep_disconnected_rh_with_rh_open(struct torture_context *tctx, >+ struct smb2_tree *tree1, >+ struct smb2_tree *tree2) >+{ >+ TALLOC_CTX *mem_ctx = talloc_new(tctx); >+ NTSTATUS status; >+ char fname[256]; >+ struct smb2_handle dh; >+ struct smb2_handle _h1a; >+ struct smb2_handle *h1a = NULL; >+ struct smb2_handle _h1b; >+ struct smb2_handle *h1b = NULL; >+ struct smb2_handle _h2; >+ struct smb2_handle *h2 = NULL; >+ struct smb2_create io; >+ struct GUID create_guid1a = GUID_random(); >+ struct GUID create_guid1b = GUID_random(); >+ struct GUID create_guid2 = GUID_random(); >+ struct smb2_lease ls1a; >+ uint64_t lease_key1a; >+ struct smb2_lease ls1b; >+ uint64_t lease_key1b; >+ struct smb2_lease ls2; >+ uint64_t lease_key2; >+ bool ret = true; >+ struct smbcli_options options1; >+ uint32_t caps; >+ >+ caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn); >+ if (!(caps & SMB2_CAP_LEASING)) { >+ torture_skip(tctx, "leases are not supported"); >+ } >+ >+ options1 = tree1->session->transport->options; >+ >+ tree1->session->transport->lease.handler = torture_lease_handler; >+ tree1->session->transport->lease.private_data = tree1; >+ >+ tree2->session->transport->lease.handler = torture_lease_handler; >+ tree2->session->transport->lease.private_data = tree2; >+ >+ torture_reset_lease_break_info(tctx, &lease_break_info); >+ >+ smb2_deltree(tree1, __func__); >+ status = torture_smb2_testdir(tree1, __func__, &dh); >+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done, >+ "torture_smb2_testdir failed\n"); >+ smb2_util_close(tree1, dh); >+ >+ /* Choose a random name in case the state is left a little funky. */ >+ snprintf(fname, 256, "%s\\file_%s.dat", >+ __func__, generate_random_str(tctx, 8)); >+ >+ smb2_util_unlink(tree1, fname); >+ >+ lease_key1a = random(); >+ smb2_lease_v2_create(&io, &ls1a, false /* dir */, fname, >+ lease_key1a, 0, /* parent lease key */ >+ smb2_util_lease_state("RH"), 0 /* lease epoch */); >+ io.in.durable_open = false; >+ io.in.durable_open_v2 = true; >+ io.in.persistent_open = false; >+ io.in.create_guid = create_guid1a; >+ io.in.timeout = UINT32_MAX; >+ >+ status = smb2_create(tree1, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h1a = io.out.file.handle; >+ h1a = &_h1a; >+ CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, true); >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_VAL(io.out.timeout, 300*1000); >+ ls1a.lease_epoch += 1; >+ CHECK_LEASE_V2(&io, "RH", true, lease_key1a, >+ 0, 0, ls1a.lease_epoch); >+ >+ lease_key1b = random(); >+ smb2_lease_v2_create(&io, &ls1b, false /* dir */, fname, >+ lease_key1b, 0, /* parent lease key */ >+ smb2_util_lease_state("RH"), 0 /* lease epoch */); >+ io.in.durable_open = false; >+ io.in.durable_open_v2 = true; >+ io.in.persistent_open = false; >+ io.in.create_guid = create_guid1b; >+ io.in.timeout = UINT32_MAX; >+ >+ status = smb2_create(tree1, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h1b = io.out.file.handle; >+ h1b = &_h1b; >+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, true); >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_VAL(io.out.timeout, 300*1000); >+ ls1b.lease_epoch += 1; >+ CHECK_LEASE_V2(&io, "RH", true, lease_key1b, >+ 0, 0, ls1b.lease_epoch); >+ >+ CHECK_NO_BREAK(tctx); >+ >+ /* disconnect, reconnect and then do durable reopen */ >+ TALLOC_FREE(tree1); >+ >+ CHECK_NO_BREAK(tctx); >+ >+ lease_key2 = random(); >+ smb2_lease_v2_create(&io, &ls2, false /* dir */, fname, >+ lease_key2, 0, /* parent lease key */ >+ smb2_util_lease_state("RH"), 0 /* lease epoch */); >+ io.in.durable_open = false; >+ io.in.durable_open_v2 = true; >+ io.in.persistent_open = false; >+ io.in.create_guid = create_guid2; >+ io.in.timeout = UINT32_MAX; >+ >+ status = smb2_create(tree2, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h2 = io.out.file.handle; >+ h2 = &_h2; >+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, true); >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_VAL(io.out.timeout, 300*1000); >+ ls2.lease_epoch += 1; >+ CHECK_LEASE_V2(&io, "RH", true, lease_key2, >+ 0, 0, ls2.lease_epoch); >+ >+ CHECK_NO_BREAK(tctx); >+ >+ if (!torture_smb2_connection_ext(tctx, 0, &options1, &tree1)) { >+ torture_warning(tctx, "couldn't reconnect, bailing\n"); >+ ret = false; >+ goto done; >+ } >+ >+ ZERO_STRUCT(io); >+ io.in.fname = fname; >+ io.in.durable_open_v2 = false; >+ io.in.durable_handle_v2 = h1a; >+ io.in.create_guid = create_guid1a; >+ io.in.lease_request_v2 = &ls1a; >+ io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE; >+ >+ status = smb2_create(tree1, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h1a = io.out.file.handle; >+ h1a = &_h1a; >+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */ >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_LEASE_V2(&io, "RH", true, lease_key1a, >+ 0, 0, ls1a.lease_epoch); >+ >+ ZERO_STRUCT(io); >+ io.in.fname = fname; >+ io.in.durable_open_v2 = false; >+ io.in.durable_handle_v2 = h1b; >+ io.in.create_guid = create_guid1b; >+ io.in.lease_request_v2 = &ls1b; >+ io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE; >+ >+ status = smb2_create(tree1, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h1b = io.out.file.handle; >+ h1b = &_h1b; >+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */ >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_LEASE_V2(&io, "RH", true, lease_key1b, >+ 0, 0, ls1b.lease_epoch); >+ >+ status = smb2_util_close(tree1, *h1a); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ h1a = NULL; >+ >+ status = smb2_util_close(tree1, *h1b); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ h1b = NULL; >+ >+ status = smb2_util_close(tree2, *h2); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ h2 = NULL; >+ >+ CHECK_NO_BREAK(tctx); >+ >+done: >+ if (tree1 != NULL) { >+ smb2_keepalive(tree1->session->transport); >+ } >+ if (tree2 != NULL) { >+ smb2_keepalive(tree2->session->transport); >+ } >+ if (tree1 != NULL && h1a != NULL) { >+ smb2_util_close(tree1, *h1a); >+ } >+ if (tree1 != NULL && h1b != NULL) { >+ smb2_util_close(tree1, *h1b); >+ } >+ if (tree2 != NULL && h2 != NULL) { >+ smb2_util_close(tree2, *h2); >+ } >+ >+ if (tree1 != NULL) { >+ smb2_util_unlink(tree1, fname); >+ smb2_deltree(tree1, __func__); >+ >+ TALLOC_FREE(tree1); >+ } >+ >+ TALLOC_FREE(tree2); >+ >+ talloc_free(mem_ctx); >+ >+ return ret; >+} >+ >+/** >+ * 1. durable open with L1A(RH) on tree1 => h1a >+ * 1. durable open with L1B(RH) on tree1 => h1b >+ * 2. disconnect tree1 >+ * 3. durable open with L2(RWH) on tree2 => h2 => RH >+ * 4. reconnect tree1 >+ * 5. durable reconnect L1A(RH) => h1a >+ * 6. durable reconnect L1B(RH) => h1a >+ */ >+static bool test_durable_v2_open_keep_disconnected_rh_with_rwh_open(struct torture_context *tctx, >+ struct smb2_tree *tree1, >+ struct smb2_tree *tree2) >+{ >+ TALLOC_CTX *mem_ctx = talloc_new(tctx); >+ NTSTATUS status; >+ char fname[256]; >+ struct smb2_handle dh; >+ struct smb2_handle _h1a; >+ struct smb2_handle *h1a = NULL; >+ struct smb2_handle _h1b; >+ struct smb2_handle *h1b = NULL; >+ struct smb2_handle _h2; >+ struct smb2_handle *h2 = NULL; >+ struct smb2_create io; >+ struct GUID create_guid1a = GUID_random(); >+ struct GUID create_guid1b = GUID_random(); >+ struct GUID create_guid2 = GUID_random(); >+ struct smb2_lease ls1a; >+ uint64_t lease_key1a; >+ struct smb2_lease ls1b; >+ uint64_t lease_key1b; >+ struct smb2_lease ls2; >+ uint64_t lease_key2; >+ bool ret = true; >+ struct smbcli_options options1; >+ uint32_t caps; >+ >+ caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn); >+ if (!(caps & SMB2_CAP_LEASING)) { >+ torture_skip(tctx, "leases are not supported"); >+ } >+ >+ options1 = tree1->session->transport->options; >+ >+ tree1->session->transport->lease.handler = torture_lease_handler; >+ tree1->session->transport->lease.private_data = tree1; >+ >+ tree2->session->transport->lease.handler = torture_lease_handler; >+ tree2->session->transport->lease.private_data = tree2; >+ >+ torture_reset_lease_break_info(tctx, &lease_break_info); >+ >+ smb2_deltree(tree1, __func__); >+ status = torture_smb2_testdir(tree1, __func__, &dh); >+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done, >+ "torture_smb2_testdir failed\n"); >+ smb2_util_close(tree1, dh); >+ >+ /* Choose a random name in case the state is left a little funky. */ >+ snprintf(fname, 256, "%s\\file_%s.dat", >+ __func__, generate_random_str(tctx, 8)); >+ >+ smb2_util_unlink(tree1, fname); >+ >+ lease_key1a = random(); >+ smb2_lease_v2_create(&io, &ls1a, false /* dir */, fname, >+ lease_key1a, 0, /* parent lease key */ >+ smb2_util_lease_state("RH"), 0 /* lease epoch */); >+ io.in.durable_open = false; >+ io.in.durable_open_v2 = true; >+ io.in.persistent_open = false; >+ io.in.create_guid = create_guid1a; >+ io.in.timeout = UINT32_MAX; >+ >+ status = smb2_create(tree1, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h1a = io.out.file.handle; >+ h1a = &_h1a; >+ CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, true); >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_VAL(io.out.timeout, 300*1000); >+ ls1a.lease_epoch += 1; >+ CHECK_LEASE_V2(&io, "RH", true, lease_key1a, >+ 0, 0, ls1a.lease_epoch); >+ >+ lease_key1b = random(); >+ smb2_lease_v2_create(&io, &ls1b, false /* dir */, fname, >+ lease_key1b, 0, /* parent lease key */ >+ smb2_util_lease_state("RH"), 0 /* lease epoch */); >+ io.in.durable_open = false; >+ io.in.durable_open_v2 = true; >+ io.in.persistent_open = false; >+ io.in.create_guid = create_guid1b; >+ io.in.timeout = UINT32_MAX; >+ >+ status = smb2_create(tree1, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h1b = io.out.file.handle; >+ h1b = &_h1b; >+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, true); >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_VAL(io.out.timeout, 300*1000); >+ ls1b.lease_epoch += 1; >+ CHECK_LEASE_V2(&io, "RH", true, lease_key1b, >+ 0, 0, ls1b.lease_epoch); >+ >+ CHECK_NO_BREAK(tctx); >+ >+ /* disconnect, reconnect and then do durable reopen */ >+ TALLOC_FREE(tree1); >+ >+ CHECK_NO_BREAK(tctx); >+ >+ lease_key2 = random(); >+ smb2_lease_v2_create(&io, &ls2, false /* dir */, fname, >+ lease_key2, 0, /* parent lease key */ >+ smb2_util_lease_state("RWH"), 0 /* lease epoch */); >+ io.in.durable_open = false; >+ io.in.durable_open_v2 = true; >+ io.in.persistent_open = false; >+ io.in.create_guid = create_guid2; >+ io.in.timeout = UINT32_MAX; >+ >+ status = smb2_create(tree2, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h2 = io.out.file.handle; >+ h2 = &_h2; >+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, true); >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_VAL(io.out.timeout, 300*1000); >+ ls2.lease_epoch += 1; >+ CHECK_LEASE_V2(&io, "RH", true, lease_key2, >+ 0, 0, ls2.lease_epoch); >+ >+ CHECK_NO_BREAK(tctx); >+ >+ if (!torture_smb2_connection_ext(tctx, 0, &options1, &tree1)) { >+ torture_warning(tctx, "couldn't reconnect, bailing\n"); >+ ret = false; >+ goto done; >+ } >+ >+ ZERO_STRUCT(io); >+ io.in.fname = fname; >+ io.in.durable_open_v2 = false; >+ io.in.durable_handle_v2 = h1a; >+ io.in.create_guid = create_guid1a; >+ io.in.lease_request_v2 = &ls1a; >+ io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE; >+ >+ status = smb2_create(tree1, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h1a = io.out.file.handle; >+ h1a = &_h1a; >+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */ >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_LEASE_V2(&io, "RH", true, lease_key1a, >+ 0, 0, ls1a.lease_epoch); >+ >+ ZERO_STRUCT(io); >+ io.in.fname = fname; >+ io.in.durable_open_v2 = false; >+ io.in.durable_handle_v2 = h1b; >+ io.in.create_guid = create_guid1b; >+ io.in.lease_request_v2 = &ls1b; >+ io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE; >+ >+ status = smb2_create(tree1, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h1b = io.out.file.handle; >+ h1b = &_h1b; >+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */ >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_LEASE_V2(&io, "RH", true, lease_key1b, >+ 0, 0, ls1b.lease_epoch); >+ >+ status = smb2_util_close(tree1, *h1a); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ h1a = NULL; >+ >+ status = smb2_util_close(tree1, *h1b); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ h1b = NULL; >+ >+ status = smb2_util_close(tree2, *h2); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ h2 = NULL; >+ >+ CHECK_NO_BREAK(tctx); >+ >+done: >+ if (tree1 != NULL) { >+ smb2_keepalive(tree1->session->transport); >+ } >+ if (tree2 != NULL) { >+ smb2_keepalive(tree2->session->transport); >+ } >+ if (tree1 != NULL && h1a != NULL) { >+ smb2_util_close(tree1, *h1a); >+ } >+ if (tree1 != NULL && h1b != NULL) { >+ smb2_util_close(tree1, *h1b); >+ } >+ if (tree2 != NULL && h2 != NULL) { >+ smb2_util_close(tree2, *h2); >+ } >+ >+ if (tree1 != NULL) { >+ smb2_util_unlink(tree1, fname); >+ smb2_deltree(tree1, __func__); >+ >+ TALLOC_FREE(tree1); >+ } >+ >+ TALLOC_FREE(tree2); >+ >+ talloc_free(mem_ctx); >+ >+ return ret; >+} >+ >+/** >+ * 1. durable open with L1(RWH) on tree1 => h1 >+ * 2. disconnect tree1 >+ * 3. stat open on tree2 => h2 >+ * 4. reconnect tree1 >+ * 5. durable reconnect L1(RWH) => h1 >+ */ >+static bool test_durable_v2_open_keep_disconnected_rwh_with_stat_open(struct torture_context *tctx, >+ struct smb2_tree *tree1, >+ struct smb2_tree *tree2) >+{ >+ TALLOC_CTX *mem_ctx = talloc_new(tctx); >+ NTSTATUS status; >+ char fname[256]; >+ struct smb2_handle dh; >+ struct smb2_handle _h1; >+ struct smb2_handle *h1 = NULL; >+ struct smb2_handle _h2; >+ struct smb2_handle *h2 = NULL; >+ struct smb2_create io; >+ struct GUID create_guid1 = GUID_random(); >+ struct smb2_lease ls1; >+ uint64_t lease_key1; >+ bool ret = true; >+ struct smbcli_options options1; >+ uint32_t caps; >+ >+ caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn); >+ if (!(caps & SMB2_CAP_LEASING)) { >+ torture_skip(tctx, "leases are not supported"); >+ } >+ >+ options1 = tree1->session->transport->options; >+ >+ tree1->session->transport->lease.handler = torture_lease_handler; >+ tree1->session->transport->lease.private_data = tree1; >+ >+ tree2->session->transport->lease.handler = torture_lease_handler; >+ tree2->session->transport->lease.private_data = tree2; >+ >+ torture_reset_lease_break_info(tctx, &lease_break_info); >+ >+ smb2_deltree(tree1, __func__); >+ status = torture_smb2_testdir(tree1, __func__, &dh); >+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done, >+ "torture_smb2_testdir failed\n"); >+ smb2_util_close(tree1, dh); >+ >+ /* Choose a random name in case the state is left a little funky. */ >+ snprintf(fname, 256, "%s\\file_%s.dat", >+ __func__, generate_random_str(tctx, 8)); >+ >+ smb2_util_unlink(tree1, fname); >+ >+ lease_key1 = random(); >+ smb2_lease_v2_create(&io, &ls1, false /* dir */, fname, >+ lease_key1, 0, /* parent lease key */ >+ smb2_util_lease_state("RWH"), 0 /* lease epoch */); >+ io.in.durable_open = false; >+ io.in.durable_open_v2 = true; >+ io.in.persistent_open = false; >+ io.in.create_guid = create_guid1; >+ io.in.timeout = UINT32_MAX; >+ >+ status = smb2_create(tree1, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h1 = io.out.file.handle; >+ h1 = &_h1; >+ CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, true); >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_VAL(io.out.timeout, 300*1000); >+ ls1.lease_epoch += 1; >+ CHECK_LEASE_V2(&io, "RWH", true, lease_key1, >+ 0, 0, ls1.lease_epoch); >+ >+ CHECK_NO_BREAK(tctx); >+ >+ /* disconnect, reconnect and then do durable reopen */ >+ TALLOC_FREE(tree1); >+ >+ CHECK_NO_BREAK(tctx); >+ >+ smb2_generic_create(&io, NULL, false /* dir */, fname, >+ FILE_OPEN_IF, 0, 0, 0); >+ io.in.desired_access = SEC_FILE_READ_ATTRIBUTE; >+ status = smb2_create(tree2, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h2 = io.out.file.handle; >+ h2 = &_h2; >+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, false); >+ CHECK_VAL(io.out.persistent_open, false); >+ >+ CHECK_NO_BREAK(tctx); >+ >+ if (!torture_smb2_connection_ext(tctx, 0, &options1, &tree1)) { >+ torture_warning(tctx, "couldn't reconnect, bailing\n"); >+ ret = false; >+ goto done; >+ } >+ >+ ZERO_STRUCT(io); >+ io.in.fname = fname; >+ io.in.durable_open_v2 = false; >+ io.in.durable_handle_v2 = h1; >+ io.in.create_guid = create_guid1; >+ io.in.lease_request_v2 = &ls1; >+ io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE; >+ >+ status = smb2_create(tree1, mem_ctx, &io); >+ >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h1 = io.out.file.handle; >+ h1 = &_h1; >+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */ >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_LEASE_V2(&io, "RWH", true, lease_key1, >+ 0, 0, ls1.lease_epoch); >+ >+ status = smb2_util_close(tree1, *h1); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ h1 = NULL; >+ >+ status = smb2_util_close(tree2, *h2); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ h2 = NULL; >+ >+ CHECK_NO_BREAK(tctx); >+ >+done: >+ if (tree1 != NULL) { >+ smb2_keepalive(tree1->session->transport); >+ } >+ if (tree2 != NULL) { >+ smb2_keepalive(tree2->session->transport); >+ } >+ if (tree1 != NULL && h1 != NULL) { >+ smb2_util_close(tree1, *h1); >+ } >+ if (tree2 != NULL && h2 != NULL) { >+ smb2_util_close(tree2, *h2); >+ } >+ >+ if (tree1 != NULL) { >+ smb2_util_unlink(tree1, fname); >+ smb2_deltree(tree1, __func__); >+ >+ TALLOC_FREE(tree1); >+ } >+ >+ TALLOC_FREE(tree2); >+ >+ talloc_free(mem_ctx); >+ >+ return ret; >+} >+ >+/** >+ * 1. durable open with L1(RWH) on tree1 => h1 >+ * 2. disconnect tree1 >+ * 3. durable open with L2(RWH) on tree2 => h2 >+ * 4. reconnect tree1 >+ * 5. durable reconnect L1(RH) => h1 => not found >+ */ >+static bool test_durable_v2_open_purge_disconnected_rwh_with_rwh_open(struct torture_context *tctx, >+ struct smb2_tree *tree1, >+ struct smb2_tree *tree2) >+{ >+ TALLOC_CTX *mem_ctx = talloc_new(tctx); >+ NTSTATUS status; >+ char fname[256]; >+ struct smb2_handle dh; >+ struct smb2_handle _h1; >+ struct smb2_handle *h1 = NULL; >+ struct smb2_handle _h2; >+ struct smb2_handle *h2 = NULL; >+ struct smb2_create io; >+ struct GUID create_guid1 = GUID_random(); >+ struct GUID create_guid2 = GUID_random(); >+ struct smb2_lease ls1; >+ uint64_t lease_key1; >+ struct smb2_lease ls2; >+ uint64_t lease_key2; >+ bool ret = true; >+ struct smbcli_options options1; >+ uint32_t caps; >+ >+ caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn); >+ if (!(caps & SMB2_CAP_LEASING)) { >+ torture_skip(tctx, "leases are not supported"); >+ } >+ >+ options1 = tree1->session->transport->options; >+ >+ tree1->session->transport->lease.handler = torture_lease_handler; >+ tree1->session->transport->lease.private_data = tree1; >+ >+ tree2->session->transport->lease.handler = torture_lease_handler; >+ tree2->session->transport->lease.private_data = tree2; >+ >+ torture_reset_lease_break_info(tctx, &lease_break_info); >+ >+ smb2_deltree(tree1, __func__); >+ status = torture_smb2_testdir(tree1, __func__, &dh); >+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done, >+ "torture_smb2_testdir failed\n"); >+ smb2_util_close(tree1, dh); >+ >+ /* Choose a random name in case the state is left a little funky. */ >+ snprintf(fname, 256, "%s\\file_%s.dat", >+ __func__, generate_random_str(tctx, 8)); >+ >+ smb2_util_unlink(tree1, fname); >+ >+ lease_key1 = random(); >+ smb2_lease_v2_create(&io, &ls1, false /* dir */, fname, >+ lease_key1, 0, /* parent lease key */ >+ smb2_util_lease_state("RWH"), 0 /* lease epoch */); >+ io.in.durable_open = false; >+ io.in.durable_open_v2 = true; >+ io.in.persistent_open = false; >+ io.in.create_guid = create_guid1; >+ io.in.timeout = UINT32_MAX; >+ >+ status = smb2_create(tree1, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h1 = io.out.file.handle; >+ h1 = &_h1; >+ CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, true); >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_VAL(io.out.timeout, 300*1000); >+ ls1.lease_epoch += 1; >+ CHECK_LEASE_V2(&io, "RWH", true, lease_key1, >+ 0, 0, ls1.lease_epoch); >+ >+ CHECK_NO_BREAK(tctx); >+ >+ /* disconnect, reconnect and then do durable reopen */ >+ TALLOC_FREE(tree1); >+ >+ CHECK_NO_BREAK(tctx); >+ >+ lease_key2 = random(); >+ smb2_lease_v2_create(&io, &ls2, false /* dir */, fname, >+ lease_key2, 0, /* parent lease key */ >+ smb2_util_lease_state("RWH"), 0 /* lease epoch */); >+ io.in.durable_open = false; >+ io.in.durable_open_v2 = true; >+ io.in.persistent_open = false; >+ io.in.create_guid = create_guid2; >+ io.in.timeout = UINT32_MAX; >+ >+ status = smb2_create(tree2, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h2 = io.out.file.handle; >+ h2 = &_h2; >+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, true); >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_VAL(io.out.timeout, 300*1000); >+ ls2.lease_epoch += 1; >+ CHECK_LEASE_V2(&io, "RWH", true, lease_key2, >+ 0, 0, ls2.lease_epoch); >+ >+ CHECK_NO_BREAK(tctx); >+ >+ if (!torture_smb2_connection_ext(tctx, 0, &options1, &tree1)) { >+ torture_warning(tctx, "couldn't reconnect, bailing\n"); >+ ret = false; >+ goto done; >+ } >+ >+ ZERO_STRUCT(io); >+ io.in.fname = fname; >+ io.in.durable_open_v2 = false; >+ io.in.durable_handle_v2 = h1; >+ io.in.create_guid = create_guid1; >+ io.in.lease_request_v2 = &ls1; >+ io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE; >+ ls1.lease_state = smb2_util_lease_state("RH"); >+ >+ status = smb2_create(tree1, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); >+ >+ status = smb2_util_close(tree2, *h2); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ h2 = NULL; >+ >+ CHECK_NO_BREAK(tctx); >+ >+done: >+ if (tree1 != NULL) { >+ smb2_keepalive(tree1->session->transport); >+ } >+ if (tree2 != NULL) { >+ smb2_keepalive(tree2->session->transport); >+ } >+ if (tree1 != NULL && h1 != NULL) { >+ smb2_util_close(tree1, *h1); >+ } >+ if (tree2 != NULL && h2 != NULL) { >+ smb2_util_close(tree2, *h2); >+ } >+ >+ if (tree1 != NULL) { >+ smb2_util_unlink(tree1, fname); >+ smb2_deltree(tree1, __func__); >+ >+ TALLOC_FREE(tree1); >+ } >+ >+ TALLOC_FREE(tree2); >+ >+ talloc_free(mem_ctx); >+ >+ return ret; >+} >+ >+/** >+ * 1. durable open with L1(RWH) on tree1 => h1 >+ * 2. disconnect tree1 >+ * 3. durable open with L2(RH) on tree2 => h2 >+ * 4. reconnect tree1 >+ * 5. durable reconnect L1(RH) => h1 => not found >+ */ >+static bool test_durable_v2_open_purge_disconnected_rwh_with_rh_open(struct torture_context *tctx, >+ struct smb2_tree *tree1, >+ struct smb2_tree *tree2) >+{ >+ TALLOC_CTX *mem_ctx = talloc_new(tctx); >+ NTSTATUS status; >+ char fname[256]; >+ struct smb2_handle dh; >+ struct smb2_handle _h1; >+ struct smb2_handle *h1 = NULL; >+ struct smb2_handle _h2; >+ struct smb2_handle *h2 = NULL; >+ struct smb2_create io; >+ struct GUID create_guid1 = GUID_random(); >+ struct GUID create_guid2 = GUID_random(); >+ struct smb2_lease ls1; >+ uint64_t lease_key1; >+ struct smb2_lease ls2; >+ uint64_t lease_key2; >+ bool ret = true; >+ struct smbcli_options options1; >+ uint32_t caps; >+ >+ caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn); >+ if (!(caps & SMB2_CAP_LEASING)) { >+ torture_skip(tctx, "leases are not supported"); >+ } >+ >+ options1 = tree1->session->transport->options; >+ >+ tree1->session->transport->lease.handler = torture_lease_handler; >+ tree1->session->transport->lease.private_data = tree1; >+ >+ tree2->session->transport->lease.handler = torture_lease_handler; >+ tree2->session->transport->lease.private_data = tree2; >+ >+ torture_reset_lease_break_info(tctx, &lease_break_info); >+ >+ smb2_deltree(tree1, __func__); >+ status = torture_smb2_testdir(tree1, __func__, &dh); >+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done, >+ "torture_smb2_testdir failed\n"); >+ smb2_util_close(tree1, dh); >+ >+ /* Choose a random name in case the state is left a little funky. */ >+ snprintf(fname, 256, "%s\\file_%s.dat", >+ __func__, generate_random_str(tctx, 8)); >+ >+ smb2_util_unlink(tree1, fname); >+ >+ lease_key1 = random(); >+ smb2_lease_v2_create(&io, &ls1, false /* dir */, fname, >+ lease_key1, 0, /* parent lease key */ >+ smb2_util_lease_state("RWH"), 0 /* lease epoch */); >+ io.in.durable_open = false; >+ io.in.durable_open_v2 = true; >+ io.in.persistent_open = false; >+ io.in.create_guid = create_guid1; >+ io.in.timeout = UINT32_MAX; >+ >+ status = smb2_create(tree1, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h1 = io.out.file.handle; >+ h1 = &_h1; >+ CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, true); >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_VAL(io.out.timeout, 300*1000); >+ ls1.lease_epoch += 1; >+ CHECK_LEASE_V2(&io, "RWH", true, lease_key1, >+ 0, 0, ls1.lease_epoch); >+ >+ CHECK_NO_BREAK(tctx); >+ >+ /* disconnect, reconnect and then do durable reopen */ >+ TALLOC_FREE(tree1); >+ >+ CHECK_NO_BREAK(tctx); >+ >+ lease_key2 = random(); >+ smb2_lease_v2_create(&io, &ls2, false /* dir */, fname, >+ lease_key2, 0, /* parent lease key */ >+ smb2_util_lease_state("RH"), 0 /* lease epoch */); >+ io.in.durable_open = false; >+ io.in.durable_open_v2 = true; >+ io.in.persistent_open = false; >+ io.in.create_guid = create_guid2; >+ io.in.timeout = UINT32_MAX; >+ >+ status = smb2_create(tree2, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h2 = io.out.file.handle; >+ h2 = &_h2; >+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, true); >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_VAL(io.out.timeout, 300*1000); >+ ls2.lease_epoch += 1; >+ CHECK_LEASE_V2(&io, "RH", true, lease_key2, >+ 0, 0, ls2.lease_epoch); >+ >+ CHECK_NO_BREAK(tctx); >+ >+ if (!torture_smb2_connection_ext(tctx, 0, &options1, &tree1)) { >+ torture_warning(tctx, "couldn't reconnect, bailing\n"); >+ ret = false; >+ goto done; >+ } >+ >+ ZERO_STRUCT(io); >+ io.in.fname = fname; >+ io.in.durable_open_v2 = false; >+ io.in.durable_handle_v2 = h1; >+ io.in.create_guid = create_guid1; >+ io.in.lease_request_v2 = &ls1; >+ io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE; >+ ls1.lease_state = smb2_util_lease_state("RH"); >+ >+ status = smb2_create(tree1, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); >+ >+ status = smb2_util_close(tree2, *h2); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ h2 = NULL; >+ >+ CHECK_NO_BREAK(tctx); >+ >+done: >+ if (tree1 != NULL) { >+ smb2_keepalive(tree1->session->transport); >+ } >+ if (tree2 != NULL) { >+ smb2_keepalive(tree2->session->transport); >+ } >+ if (tree1 != NULL && h1 != NULL) { >+ smb2_util_close(tree1, *h1); >+ } >+ if (tree2 != NULL && h2 != NULL) { >+ smb2_util_close(tree2, *h2); >+ } >+ >+ if (tree1 != NULL) { >+ smb2_util_unlink(tree1, fname); >+ smb2_deltree(tree1, __func__); >+ >+ TALLOC_FREE(tree1); >+ } >+ >+ TALLOC_FREE(tree2); >+ >+ talloc_free(mem_ctx); >+ >+ return ret; >+} >+ >+/** >+ * 1. durable open with L1A(RH) on tree1 => h1a >+ * 2. durable open with L1B(RH) on tree1 => h1b >+ * 3. disconnect tree1 >+ * 4. open with SHARE_NONE on tree2 => h2 >+ * 5. reconnect tree1 >+ * 6. durable reconnect L1A(RH) => not found >+ * 7. durable reconnect L1B(RH) => not found >+ */ >+static bool test_durable_v2_open_purge_disconnected_rh_with_share_none_open(struct torture_context *tctx, >+ struct smb2_tree *tree1, >+ struct smb2_tree *tree2) >+{ >+ TALLOC_CTX *mem_ctx = talloc_new(tctx); >+ NTSTATUS status; >+ char fname[256]; >+ struct smb2_handle dh; >+ struct smb2_handle _h1a; >+ struct smb2_handle *h1a = NULL; >+ struct smb2_handle _h1b; >+ struct smb2_handle *h1b = NULL; >+ struct smb2_handle _h2; >+ struct smb2_handle *h2 = NULL; >+ struct smb2_create io; >+ struct GUID create_guid1a = GUID_random(); >+ struct GUID create_guid1b = GUID_random(); >+ struct smb2_lease ls1a; >+ uint64_t lease_key1a; >+ struct smb2_lease ls1b; >+ uint64_t lease_key1b; >+ bool ret = true; >+ struct smbcli_options options1; >+ uint32_t caps; >+ >+ caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn); >+ if (!(caps & SMB2_CAP_LEASING)) { >+ torture_skip(tctx, "leases are not supported"); >+ } >+ >+ options1 = tree1->session->transport->options; >+ >+ tree1->session->transport->lease.handler = torture_lease_handler; >+ tree1->session->transport->lease.private_data = tree1; >+ >+ tree2->session->transport->lease.handler = torture_lease_handler; >+ tree2->session->transport->lease.private_data = tree2; >+ >+ torture_reset_lease_break_info(tctx, &lease_break_info); >+ >+ smb2_deltree(tree1, __func__); >+ status = torture_smb2_testdir(tree1, __func__, &dh); >+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done, >+ "torture_smb2_testdir failed\n"); >+ smb2_util_close(tree1, dh); >+ >+ /* Choose a random name in case the state is left a little funky. */ >+ snprintf(fname, 256, "%s\\file_%s.dat", >+ __func__, generate_random_str(tctx, 8)); >+ >+ smb2_util_unlink(tree1, fname); >+ >+ lease_key1a = random(); >+ smb2_lease_v2_create(&io, &ls1a, false /* dir */, fname, >+ lease_key1a, 0, /* parent lease key */ >+ smb2_util_lease_state("RH"), 0 /* lease epoch */); >+ io.in.durable_open = false; >+ io.in.durable_open_v2 = true; >+ io.in.persistent_open = false; >+ io.in.create_guid = create_guid1a; >+ io.in.timeout = UINT32_MAX; >+ >+ status = smb2_create(tree1, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h1a = io.out.file.handle; >+ h1a = &_h1a; >+ CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, true); >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_VAL(io.out.timeout, 300*1000); >+ ls1a.lease_epoch += 1; >+ CHECK_LEASE_V2(&io, "RH", true, lease_key1a, >+ 0, 0, ls1a.lease_epoch); >+ >+ lease_key1b = random(); >+ smb2_lease_v2_create(&io, &ls1b, false /* dir */, fname, >+ lease_key1b, 0, /* parent lease key */ >+ smb2_util_lease_state("RH"), 0 /* lease epoch */); >+ io.in.durable_open = false; >+ io.in.durable_open_v2 = true; >+ io.in.persistent_open = false; >+ io.in.create_guid = create_guid1b; >+ io.in.timeout = UINT32_MAX; >+ >+ status = smb2_create(tree1, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h1b = io.out.file.handle; >+ h1b = &_h1b; >+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, true); >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_VAL(io.out.timeout, 300*1000); >+ ls1b.lease_epoch += 1; >+ CHECK_LEASE_V2(&io, "RH", true, lease_key1b, >+ 0, 0, ls1b.lease_epoch); >+ >+ CHECK_NO_BREAK(tctx); >+ >+ /* disconnect, reconnect and then do durable reopen */ >+ TALLOC_FREE(tree1); >+ >+ CHECK_NO_BREAK(tctx); >+ >+ smb2_generic_create_share(&io, &ls1a, false /* dir */, fname, >+ NTCREATEX_DISP_OPEN_IF, >+ FILE_SHARE_NONE, >+ SMB2_OPLOCK_LEVEL_NONE, 0, 0); >+ status = smb2_create(tree2, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h2 = io.out.file.handle; >+ h2 = &_h2; >+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, false); >+ CHECK_VAL(io.out.persistent_open, false); >+ >+ CHECK_NO_BREAK(tctx); >+ >+ if (!torture_smb2_connection_ext(tctx, 0, &options1, &tree1)) { >+ torture_warning(tctx, "couldn't reconnect, bailing\n"); >+ ret = false; >+ goto done; >+ } >+ >+ ZERO_STRUCT(io); >+ io.in.fname = fname; >+ io.in.durable_open_v2 = false; >+ io.in.durable_handle_v2 = h1a; >+ io.in.create_guid = create_guid1a; >+ io.in.lease_request_v2 = &ls1a; >+ io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE; >+ >+ status = smb2_create(tree1, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); >+ h1a = NULL; >+ >+ ZERO_STRUCT(io); >+ io.in.fname = fname; >+ io.in.durable_open_v2 = false; >+ io.in.durable_handle_v2 = h1b; >+ io.in.create_guid = create_guid1b; >+ io.in.lease_request_v2 = &ls1b; >+ io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE; >+ >+ status = smb2_create(tree1, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); >+ h1b = NULL; >+ >+ status = smb2_util_close(tree2, *h2); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ h2 = NULL; >+ >+ CHECK_NO_BREAK(tctx); >+ >+done: >+ if (tree1 != NULL) { >+ smb2_keepalive(tree1->session->transport); >+ } >+ if (tree2 != NULL) { >+ smb2_keepalive(tree2->session->transport); >+ } >+ if (tree1 != NULL && h1a != NULL) { >+ smb2_util_close(tree1, *h1a); >+ } >+ if (tree1 != NULL && h1b != NULL) { >+ smb2_util_close(tree1, *h1b); >+ } >+ if (tree2 != NULL && h2 != NULL) { >+ smb2_util_close(tree2, *h2); >+ } >+ >+ if (tree1 != NULL) { >+ smb2_util_unlink(tree1, fname); >+ smb2_deltree(tree1, __func__); >+ >+ TALLOC_FREE(tree1); >+ } >+ >+ TALLOC_FREE(tree2); >+ >+ talloc_free(mem_ctx); >+ >+ return ret; >+} >+ >+/** >+ * 1. durable open with L1A(RH) on tree1 => h1a >+ * 2. durable open with L1B(RH) on tree1 => h1b >+ * 3. durable open with L2(RH) on tree2 => h2 >+ * 4. disconnect tree2 >+ * 5.1 write to h1a >+ * 5.2 lease break to NONE for L1B (ack requested, but ignored) >+ * 6. reconnect tree2 >+ * 7. durable reconnect L2(RH) => h2 => not found >+ * 8. close h1a >+ * 9. durable open with L1A(RWH) on tree1 => h1a only RH >+ */ >+static bool test_durable_v2_open_purge_disconnected_rh_with_write(struct torture_context *tctx, >+ struct smb2_tree *tree1, >+ struct smb2_tree *tree2) >+{ >+ TALLOC_CTX *mem_ctx = talloc_new(tctx); >+ NTSTATUS status; >+ char fname[256]; >+ struct smb2_handle dh; >+ struct smb2_handle _h1a; >+ struct smb2_handle *h1a = NULL; >+ struct smb2_handle _h1b; >+ struct smb2_handle *h1b = NULL; >+ struct smb2_handle _h2; >+ struct smb2_handle *h2 = NULL; >+ struct smb2_create io; >+ struct GUID create_guid1a = GUID_random(); >+ struct GUID create_guid1b = GUID_random(); >+ struct GUID create_guid2 = GUID_random(); >+ struct smb2_lease ls1a; >+ uint64_t lease_key1a; >+ struct smb2_lease ls1b; >+ uint64_t lease_key1b; >+ struct smb2_lease ls2; >+ uint64_t lease_key2; >+ struct smb2_write wrt; >+ bool ret = true; >+ struct smbcli_options options2; >+ uint32_t caps; >+ >+ caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn); >+ if (!(caps & SMB2_CAP_LEASING)) { >+ torture_skip(tctx, "leases are not supported"); >+ } >+ >+ options2 = tree2->session->transport->options; >+ >+ tree1->session->transport->lease.handler = torture_lease_handler; >+ tree1->session->transport->lease.private_data = tree1; >+ >+ tree2->session->transport->lease.handler = torture_lease_handler; >+ tree2->session->transport->lease.private_data = tree2; >+ >+ torture_reset_lease_break_info(tctx, &lease_break_info); >+ >+ smb2_deltree(tree1, __func__); >+ status = torture_smb2_testdir(tree1, __func__, &dh); >+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done, >+ "torture_smb2_testdir failed\n"); >+ smb2_util_close(tree1, dh); >+ >+ /* Choose a random name in case the state is left a little funky. */ >+ snprintf(fname, 256, "%s\\file_%s.dat", >+ __func__, generate_random_str(tctx, 8)); >+ >+ smb2_util_unlink(tree1, fname); >+ >+ lease_key1a = random(); >+ smb2_lease_v2_create(&io, &ls1a, false /* dir */, fname, >+ lease_key1a, 0, /* parent lease key */ >+ smb2_util_lease_state("RH"), 0 /* lease epoch */); >+ io.in.durable_open = false; >+ io.in.durable_open_v2 = true; >+ io.in.persistent_open = false; >+ io.in.create_guid = create_guid1a; >+ io.in.timeout = UINT32_MAX; >+ >+ status = smb2_create(tree1, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h1a = io.out.file.handle; >+ h1a = &_h1a; >+ CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, true); >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_VAL(io.out.timeout, 300*1000); >+ ls1a.lease_epoch += 1; >+ CHECK_LEASE_V2(&io, "RH", true, lease_key1a, >+ 0, 0, ls1a.lease_epoch); >+ >+ lease_key1b = random(); >+ smb2_lease_v2_create(&io, &ls1b, false /* dir */, fname, >+ lease_key1b, 0, /* parent lease key */ >+ smb2_util_lease_state("RH"), 0 /* lease epoch */); >+ io.in.durable_open = false; >+ io.in.durable_open_v2 = true; >+ io.in.persistent_open = false; >+ io.in.create_guid = create_guid1b; >+ io.in.timeout = UINT32_MAX; >+ >+ status = smb2_create(tree1, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h1b = io.out.file.handle; >+ h1b = &_h1b; >+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, true); >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_VAL(io.out.timeout, 300*1000); >+ ls1b.lease_epoch += 1; >+ CHECK_LEASE_V2(&io, "RH", true, lease_key1b, >+ 0, 0, ls1b.lease_epoch); >+ >+ lease_key2 = random(); >+ smb2_lease_v2_create(&io, &ls2, false /* dir */, fname, >+ lease_key2, 0, /* parent lease key */ >+ smb2_util_lease_state("RH"), 0 /* lease epoch */); >+ io.in.durable_open = false; >+ io.in.durable_open_v2 = true; >+ io.in.persistent_open = false; >+ io.in.create_guid = create_guid2; >+ io.in.timeout = UINT32_MAX; >+ >+ status = smb2_create(tree2, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h2 = io.out.file.handle; >+ h2 = &_h2; >+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, true); >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_VAL(io.out.timeout, 300*1000); >+ ls2.lease_epoch += 1; >+ CHECK_LEASE_V2(&io, "RH", true, lease_key2, >+ 0, 0, ls2.lease_epoch); >+ >+ CHECK_NO_BREAK(tctx); >+ >+ /* disconnect, reconnect and then do durable reopen */ >+ TALLOC_FREE(tree2); >+ >+ CHECK_NO_BREAK(tctx); >+ lease_break_info.lease_skip_ack = true; >+ >+ ZERO_STRUCT(wrt); >+ wrt.in.file.handle = *h1a; >+ wrt.in.offset = 0; >+ wrt.in.data = data_blob_string_const("data"); >+ status = smb2_write(tree1, &wrt); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ >+ ls1b.lease_epoch += 1; >+ CHECK_BREAK_INFO_V2(tree1->session->transport, >+ "RH", "", lease_key1b, ls1b.lease_epoch); >+ torture_reset_lease_break_info(tctx, &lease_break_info); >+ CHECK_NO_BREAK(tctx); >+ >+ if (!torture_smb2_connection_ext(tctx, 0, &options2, &tree2)) { >+ torture_warning(tctx, "couldn't reconnect, bailing\n"); >+ ret = false; >+ goto done; >+ } >+ >+ tree2->session->transport->lease.handler = torture_lease_handler; >+ tree2->session->transport->lease.private_data = tree2; >+ >+ ZERO_STRUCT(io); >+ io.in.fname = fname; >+ io.in.durable_open_v2 = false; >+ io.in.durable_handle_v2 = h2; >+ io.in.create_guid = create_guid2; >+ io.in.lease_request_v2 = &ls2; >+ io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE; >+ >+ status = smb2_create(tree2, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); >+ h2 = NULL; >+ >+ status = smb2_util_close(tree1, *h1a); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ h1a = NULL; >+ >+ /* >+ * Now there's only lease_key2 with state NONE >+ * >+ * And that means an additional open still >+ * only gets RH... >+ */ >+ smb2_lease_v2_create(&io, &ls1a, false /* dir */, fname, >+ lease_key1a, 0, /* parent lease key */ >+ smb2_util_lease_state("RHW"), 0 /* lease epoch */); >+ io.in.durable_open = false; >+ io.in.durable_open_v2 = true; >+ io.in.persistent_open = false; >+ io.in.create_guid = create_guid1a; >+ io.in.timeout = UINT32_MAX; >+ >+ status = smb2_create(tree1, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h1a = io.out.file.handle; >+ h1a = &_h1a; >+ CHECK_VAL(io.out.create_action, NTCREATEX_ACTION_EXISTED); >+ CHECK_VAL(io.out.size, wrt.in.data.length); >+ CHECK_VAL(io.out.file_attr, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, true); >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_VAL(io.out.timeout, 300*1000); >+ ls1a.lease_epoch += 1; >+ CHECK_LEASE_V2(&io, "RH", true, lease_key1a, >+ 0, 0, ls1a.lease_epoch); >+ >+ status = smb2_util_close(tree1, *h1a); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ h1a = NULL; >+ >+ status = smb2_util_close(tree1, *h1b); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ h1b = NULL; >+ >+ CHECK_NO_BREAK(tctx); >+ >+done: >+ if (tree1 != NULL) { >+ smb2_keepalive(tree1->session->transport); >+ } >+ if (tree2 != NULL) { >+ smb2_keepalive(tree2->session->transport); >+ } >+ if (tree1 != NULL && h1a != NULL) { >+ smb2_util_close(tree1, *h1a); >+ } >+ if (tree1 != NULL && h1b != NULL) { >+ smb2_util_close(tree1, *h1b); >+ } >+ if (tree2 != NULL && h2 != NULL) { >+ smb2_util_close(tree2, *h2); >+ } >+ >+ if (tree1 != NULL) { >+ smb2_util_unlink(tree1, fname); >+ smb2_deltree(tree1, __func__); >+ >+ TALLOC_FREE(tree1); >+ } >+ >+ TALLOC_FREE(tree2); >+ >+ talloc_free(mem_ctx); >+ >+ return ret; >+} >+ >+/** >+ * 1. durable open with L1A(RH) on tree1 => h1a >+ * 2. durable open with L1B(RH) on tree1 => h1b >+ * 3. durable open with L2(RH) on tree2 => h2 >+ * 4. disconnect tree2 >+ * 5.1 rename h1a >+ * 5.2 lease break to R for L1B (ack requested, and required) >+ * 6. reconnect tree2 >+ * 7. durable reconnect L2(RH) => h2 => not found >+ */ >+static bool test_durable_v2_open_purge_disconnected_rh_with_rename(struct torture_context *tctx, >+ struct smb2_tree *tree1, >+ struct smb2_tree *tree2) >+{ >+ TALLOC_CTX *mem_ctx = talloc_new(tctx); >+ NTSTATUS status; >+ char fname[128]; >+ char fname_renamed[140]; >+ struct smb2_handle dh; >+ struct smb2_handle _h1a; >+ struct smb2_handle *h1a = NULL; >+ struct smb2_handle _h1b; >+ struct smb2_handle *h1b = NULL; >+ struct smb2_handle _h2; >+ struct smb2_handle *h2 = NULL; >+ struct smb2_create io; >+ struct GUID create_guid1a = GUID_random(); >+ struct GUID create_guid1b = GUID_random(); >+ struct GUID create_guid2 = GUID_random(); >+ struct smb2_lease ls1a; >+ uint64_t lease_key1a; >+ struct smb2_lease ls1b; >+ uint64_t lease_key1b; >+ struct smb2_lease ls2; >+ uint64_t lease_key2; >+ union smb_setfileinfo sinfo; >+ bool ret = true; >+ struct smbcli_options options2; >+ uint32_t caps; >+ >+ caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn); >+ if (!(caps & SMB2_CAP_LEASING)) { >+ torture_skip(tctx, "leases are not supported"); >+ } >+ >+ options2 = tree2->session->transport->options; >+ >+ tree1->session->transport->lease.handler = torture_lease_handler; >+ tree1->session->transport->lease.private_data = tree1; >+ >+ tree2->session->transport->lease.handler = torture_lease_handler; >+ tree2->session->transport->lease.private_data = tree2; >+ >+ torture_reset_lease_break_info(tctx, &lease_break_info); >+ >+ smb2_deltree(tree1, __func__); >+ status = torture_smb2_testdir(tree1, __func__, &dh); >+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done, >+ "torture_smb2_testdir failed\n"); >+ smb2_util_close(tree1, dh); >+ >+ /* Choose a random name in case the state is left a little funky. */ >+ snprintf(fname, 128, "%s\\file_%s.dat", >+ __func__, generate_random_str(tctx, 8)); >+ snprintf(fname_renamed, 140, "%s.renamed", fname); >+ >+ smb2_util_unlink(tree1, fname); >+ smb2_util_unlink(tree1, fname_renamed); >+ >+ lease_key1a = random(); >+ smb2_lease_v2_create(&io, &ls1a, false /* dir */, fname, >+ lease_key1a, 0, /* parent lease key */ >+ smb2_util_lease_state("RH"), 0 /* lease epoch */); >+ io.in.durable_open = false; >+ io.in.durable_open_v2 = true; >+ io.in.persistent_open = false; >+ io.in.create_guid = create_guid1a; >+ io.in.timeout = UINT32_MAX; >+ >+ status = smb2_create(tree1, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h1a = io.out.file.handle; >+ h1a = &_h1a; >+ CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, true); >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_VAL(io.out.timeout, 300*1000); >+ ls1a.lease_epoch += 1; >+ CHECK_LEASE_V2(&io, "RH", true, lease_key1a, >+ 0, 0, ls1a.lease_epoch); >+ >+ lease_key1b = random(); >+ smb2_lease_v2_create(&io, &ls1b, false /* dir */, fname, >+ lease_key1b, 0, /* parent lease key */ >+ smb2_util_lease_state("RH"), 0 /* lease epoch */); >+ io.in.durable_open = false; >+ io.in.durable_open_v2 = true; >+ io.in.persistent_open = false; >+ io.in.create_guid = create_guid1b; >+ io.in.timeout = UINT32_MAX; >+ >+ status = smb2_create(tree1, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h1b = io.out.file.handle; >+ h1b = &_h1b; >+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, true); >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_VAL(io.out.timeout, 300*1000); >+ ls1b.lease_epoch += 1; >+ CHECK_LEASE_V2(&io, "RH", true, lease_key1b, >+ 0, 0, ls1b.lease_epoch); >+ >+ lease_key2 = random(); >+ smb2_lease_v2_create(&io, &ls2, false /* dir */, fname, >+ lease_key2, 0, /* parent lease key */ >+ smb2_util_lease_state("RH"), 0 /* lease epoch */); >+ io.in.durable_open = false; >+ io.in.durable_open_v2 = true; >+ io.in.persistent_open = false; >+ io.in.create_guid = create_guid2; >+ io.in.timeout = UINT32_MAX; >+ >+ status = smb2_create(tree2, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ _h2 = io.out.file.handle; >+ h2 = &_h2; >+ CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE); >+ CHECK_VAL(io.out.durable_open, false); >+ CHECK_VAL(io.out.durable_open_v2, true); >+ CHECK_VAL(io.out.persistent_open, false); >+ CHECK_VAL(io.out.timeout, 300*1000); >+ ls2.lease_epoch += 1; >+ CHECK_LEASE_V2(&io, "RH", true, lease_key2, >+ 0, 0, ls2.lease_epoch); >+ >+ CHECK_NO_BREAK(tctx); >+ >+ /* disconnect, reconnect and then do durable reopen */ >+ TALLOC_FREE(tree2); >+ >+ CHECK_NO_BREAK(tctx); >+ >+ ZERO_STRUCT(sinfo); >+ sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; >+ sinfo.rename_information.in.file.handle = *h1a; >+ sinfo.rename_information.in.overwrite = 0; >+ sinfo.rename_information.in.root_fid = 0; >+ sinfo.rename_information.in.new_name = fname_renamed; >+ status = smb2_setinfo_file(tree1, &sinfo); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ >+ ls1b.lease_epoch += 1; >+ CHECK_BREAK_INFO_V2(tree1->session->transport, >+ "RH", "R", lease_key1b, ls1b.lease_epoch); >+ torture_reset_lease_break_info(tctx, &lease_break_info); >+ CHECK_NO_BREAK(tctx); >+ >+ if (!torture_smb2_connection_ext(tctx, 0, &options2, &tree2)) { >+ torture_warning(tctx, "couldn't reconnect, bailing\n"); >+ ret = false; >+ goto done; >+ } >+ >+ tree2->session->transport->lease.handler = torture_lease_handler; >+ tree2->session->transport->lease.private_data = tree2; >+ >+ ZERO_STRUCT(io); >+ io.in.fname = fname; >+ io.in.durable_open_v2 = false; >+ io.in.durable_handle_v2 = h2; >+ io.in.create_guid = create_guid2; >+ io.in.lease_request_v2 = &ls2; >+ io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE; >+ >+ status = smb2_create(tree2, mem_ctx, &io); >+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); >+ h2 = NULL; >+ >+ status = smb2_util_close(tree1, *h1a); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ h1a = NULL; >+ >+ status = smb2_util_close(tree1, *h1b); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ h1b = NULL; >+ >+ CHECK_NO_BREAK(tctx); >+ >+done: >+ if (tree1 != NULL) { >+ smb2_keepalive(tree1->session->transport); >+ } >+ if (tree2 != NULL) { >+ smb2_keepalive(tree2->session->transport); >+ } >+ if (tree1 != NULL && h1a != NULL) { >+ smb2_util_close(tree1, *h1a); >+ } >+ if (tree1 != NULL && h1b != NULL) { >+ smb2_util_close(tree1, *h1b); >+ } >+ if (tree2 != NULL && h2 != NULL) { >+ smb2_util_close(tree2, *h2); >+ } >+ >+ if (tree1 != NULL) { >+ smb2_util_unlink(tree1, fname); >+ smb2_util_unlink(tree1, fname_renamed); >+ smb2_deltree(tree1, __func__); >+ >+ TALLOC_FREE(tree1); >+ } >+ >+ TALLOC_FREE(tree2); >+ >+ talloc_free(mem_ctx); >+ >+ return ret; >+} >+ > /** > * Test durable request / reconnect with AppInstanceId > */ >@@ -3277,6 +5119,15 @@ struct torture_suite *torture_smb2_durable_v2_open_init(TALLOC_CTX *ctx) > torture_suite_add_1smb2_test(suite, "statRH-and-lease", test_durable_v2_open_statRH_and_lease); > torture_suite_add_1smb2_test(suite, "two-same-lease", test_durable_v2_open_two_same_lease); > torture_suite_add_1smb2_test(suite, "two-different-lease", test_durable_v2_open_two_different_leases); >+ torture_suite_add_2smb2_test(suite, "keep-disconnected-rh-with-stat-open", test_durable_v2_open_keep_disconnected_rh_with_stat_open); >+ torture_suite_add_2smb2_test(suite, "keep-disconnected-rh-with-rh-open", test_durable_v2_open_keep_disconnected_rh_with_rh_open); >+ torture_suite_add_2smb2_test(suite, "keep-disconnected-rh-with-rwh-open", test_durable_v2_open_keep_disconnected_rh_with_rwh_open); >+ torture_suite_add_2smb2_test(suite, "keep-disconnected-rwh-with-stat-open", test_durable_v2_open_keep_disconnected_rwh_with_stat_open); >+ torture_suite_add_2smb2_test(suite, "purge-disconnected-rwh-with-rwh-open", test_durable_v2_open_purge_disconnected_rwh_with_rwh_open); >+ torture_suite_add_2smb2_test(suite, "purge-disconnected-rwh-with-rh-open", test_durable_v2_open_purge_disconnected_rwh_with_rh_open); >+ torture_suite_add_2smb2_test(suite, "purge-disconnected-rh-with-share-none-open", test_durable_v2_open_purge_disconnected_rh_with_share_none_open); >+ torture_suite_add_2smb2_test(suite, "purge-disconnected-rh-with-write", test_durable_v2_open_purge_disconnected_rh_with_write); >+ torture_suite_add_2smb2_test(suite, "purge-disconnected-rh-with-rename", test_durable_v2_open_purge_disconnected_rh_with_rename); > torture_suite_add_2smb2_test(suite, "app-instance", test_durable_v2_open_app_instance); > torture_suite_add_1smb2_test(suite, "persistent-open-oplock", test_persistent_open_oplock); > torture_suite_add_1smb2_test(suite, "persistent-open-lease", test_persistent_open_lease); >-- >2.34.1 > > >From 061635ee55101b0bbfdc5b837d65008d6978e3e7 Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >Date: Thu, 29 Aug 2024 20:20:23 +0200 >Subject: [PATCH 08/10] s3:smbd: let durable_reconnect_fn already check for a > disconnected handle with the correct file_id > >We'll soon allow more than one disconnected durable handle, so >we need to find the correct one instead of assuming only a single >one. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15649 >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15651 > >Signed-off-by: Stefan Metzmacher <metze@samba.org> >Reviewed-by: Ralph Boehme <slow@samba.org> >(cherry picked from commit 2869bd1a507e7376f0bb0ec68ed4e045b043cfdb) >--- > source3/smbd/durable.c | 29 +++++++++++++++++++++-------- > 1 file changed, 21 insertions(+), 8 deletions(-) > >diff --git a/source3/smbd/durable.c b/source3/smbd/durable.c >index 2325e69cccc6..bd0c9f58e240 100644 >--- a/source3/smbd/durable.c >+++ b/source3/smbd/durable.c >@@ -506,19 +506,33 @@ static bool vfs_default_durable_reconnect_check_stat( > return true; > } > >+struct durable_reconnect_state { >+ struct smbXsrv_open *op; >+ struct share_mode_entry *e; >+}; >+ > static bool durable_reconnect_fn( > struct share_mode_entry *e, > bool *modified, > void *private_data) > { >- struct share_mode_entry *dst_e = private_data; >+ struct durable_reconnect_state *state = private_data; >+ uint64_t id = state->op->global->open_persistent_id; >+ >+ if (e->share_file_id != id) { >+ return false; /* Look at potential other entries */ >+ } > >- if (dst_e->pid.pid != 0) { >+ if (!server_id_is_disconnected(&e->pid)) { >+ return false; /* Look at potential other entries */ >+ } >+ >+ if (state->e->share_file_id == id) { > DBG_INFO("Found more than one entry, invalidating previous\n"); >- dst_e->pid.pid = 0; >+ *state->e = (struct share_mode_entry) { .pid = { .pid = 0, }}; > return true; /* end the loop through share mode entries */ > } >- *dst_e = *e; >+ *state->e = *e; > return false; /* Look at potential other entries */ > } > >@@ -533,7 +547,8 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn, > const struct loadparm_substitution *lp_sub = > loadparm_s3_global_substitution(); > struct share_mode_lock *lck; >- struct share_mode_entry e; >+ struct share_mode_entry e = { .pid = { .pid = 0, }}; >+ struct durable_reconnect_state rstate = { .op = op, .e = &e, }; > struct files_struct *fsp = NULL; > NTSTATUS status; > bool ok; >@@ -626,9 +641,7 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn, > return NT_STATUS_OBJECT_NAME_NOT_FOUND; > } > >- e = (struct share_mode_entry) { .pid.pid = 0 }; >- >- ok = share_mode_forall_entries(lck, durable_reconnect_fn, &e); >+ ok = share_mode_forall_entries(lck, durable_reconnect_fn, &rstate); > if (!ok) { > DBG_WARNING("share_mode_forall_entries failed\n"); > status = NT_STATUS_INTERNAL_DB_ERROR; >-- >2.34.1 > > >From b5981610357cdfeead4358654292d8687a82f019 Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >Date: Thu, 29 Aug 2024 18:43:14 +0200 >Subject: [PATCH 09/10] s3:smbd: allow reset_share_mode_entry() to handle more > than one durable handle > >This means that multiple durable handles with RH leases can >co-exist now... Before only the last remaining durable handle >was able to pass the SMB_VFS_DURABLE_DISCONNECT() step. > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15649 >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15651 > >Signed-off-by: Stefan Metzmacher <metze@samba.org> >Reviewed-by: Ralph Boehme <slow@samba.org> >(cherry picked from commit b1e5f5d8d2852b66ca4c858d14d367ffe228a88d) >--- > .../knownfail.d/smb2.durable-v2-open.bug15651 | 5 - > source3/locking/share_mode_lock.c | 315 ++++++++++++++++-- > 2 files changed, 293 insertions(+), 27 deletions(-) > delete mode 100644 selftest/knownfail.d/smb2.durable-v2-open.bug15651 > >diff --git a/selftest/knownfail.d/smb2.durable-v2-open.bug15651 b/selftest/knownfail.d/smb2.durable-v2-open.bug15651 >deleted file mode 100644 >index 1702a3a65809..000000000000 >--- a/selftest/knownfail.d/smb2.durable-v2-open.bug15651 >+++ /dev/null >@@ -1,5 +0,0 @@ >-^samba3.smb2.durable-v2-open.statRH-and-lease >-^samba3.smb2.durable-v2-open.two-same-lease >-^samba3.smb2.durable-v2-open.two-different-lease >-^samba3.smb2.durable-v2-open.keep-disconnected-rh-with-stat-open >-^samba3.smb2.durable-v2-open.keep-disconnected-rwh-with-stat-open >diff --git a/source3/locking/share_mode_lock.c b/source3/locking/share_mode_lock.c >index 3fc7d56562a4..4bbccdcd3bd1 100644 >--- a/source3/locking/share_mode_lock.c >+++ b/source3/locking/share_mode_lock.c >@@ -2703,16 +2703,25 @@ bool reset_share_mode_entry( > struct share_mode_data *d = NULL; > TDB_DATA key = locking_key(&id); > struct locking_tdb_data *ltdb = NULL; >- struct share_mode_entry e; >+ struct share_mode_entry e = { .pid.pid = 0 }; > struct share_mode_entry_buf e_buf; >+ size_t old_idx; >+ size_t new_idx; >+ bool found; > NTSTATUS status; >- int cmp; > bool ret = false; > bool ok; >+ struct file_id_buf id_buf; >+ struct server_id_buf pid_buf1; >+ struct server_id_buf pid_buf2; >+ size_t low_idx1, low_idx2, low_num; >+ size_t mid_idx1, mid_idx2, mid_num; >+ size_t high_idx1, high_idx2, high_num; >+ TDB_DATA dbufs[4]; >+ size_t num_dbufs = 0; > > status = share_mode_lock_access_private_data(lck, &d); > if (!NT_STATUS_IS_OK(status)) { >- struct file_id_buf id_buf; > /* Any error recovery possible here ? */ > DBG_ERR("share_mode_lock_access_private_data() failed for " > "%s - %s\n", >@@ -2728,29 +2737,54 @@ bool reset_share_mode_entry( > return false; > } > >- if (ltdb->num_share_entries != 1) { >- DBG_DEBUG("num_share_modes=%zu\n", ltdb->num_share_entries); >- goto done; >- } >+ DBG_DEBUG("%s - num_share_modes=%zu\n", >+ file_id_str_buf(id, &id_buf), >+ ltdb->num_share_entries); > >- ok = share_mode_entry_get(ltdb->share_entries, &e); >- if (!ok) { >- DBG_WARNING("share_mode_entry_get failed\n"); >+ new_idx = share_mode_entry_find( >+ ltdb->share_entries, >+ ltdb->num_share_entries, >+ new_pid, >+ new_share_file_id, >+ &e, >+ &found); >+ if (found) { >+ DBG_ERR("%s - num_share_modes=%zu " >+ "found NEW[%s][%"PRIu64"]\n", >+ file_id_str_buf(id, &id_buf), >+ ltdb->num_share_entries, >+ server_id_str_buf(new_pid, &pid_buf2), >+ new_share_file_id); > goto done; > } > >- cmp = share_mode_entry_cmp( >- old_pid, old_share_file_id, e.pid, e.share_file_id); >- if (cmp != 0) { >- struct server_id_buf tmp1, tmp2; >- DBG_WARNING("Expected pid=%s, file_id=%"PRIu64", " >- "got pid=%s, file_id=%"PRIu64"\n", >- server_id_str_buf(old_pid, &tmp1), >- old_share_file_id, >- server_id_str_buf(e.pid, &tmp2), >- e.share_file_id); >+ old_idx = share_mode_entry_find( >+ ltdb->share_entries, >+ ltdb->num_share_entries, >+ old_pid, >+ old_share_file_id, >+ &e, >+ &found); >+ if (!found) { >+ DBG_WARNING("%s - num_share_modes=%zu " >+ "OLD[%s][%"PRIu64"] not found\n", >+ file_id_str_buf(id, &id_buf), >+ ltdb->num_share_entries, >+ server_id_str_buf(old_pid, &pid_buf1), >+ old_share_file_id); > goto done; > } >+ DBG_DEBUG("%s - num_share_modes=%zu " >+ "OLD[%s][%"PRIu64"] => idx=%zu " >+ "NEW[%s][%"PRIu64"] => idx=%zu\n", >+ file_id_str_buf(id, &id_buf), >+ ltdb->num_share_entries, >+ server_id_str_buf(old_pid, &pid_buf1), >+ old_share_file_id, >+ old_idx, >+ server_id_str_buf(new_pid, &pid_buf2), >+ new_share_file_id, >+ new_idx); > > e.pid = new_pid; > if (new_mid != UINT64_MAX) { >@@ -2764,11 +2798,248 @@ bool reset_share_mode_entry( > goto done; > } > >- ltdb->share_entries = e_buf.buf; >+ /* >+ * The logic to remove the existing >+ * entry and add the new one at the >+ * same time is a bit complex because >+ * we need to keep the entries sorted. >+ * >+ * The following examples should catch >+ * the corner cases and show that >+ * the {low,mid,high}_{idx1,num} are >+ * correctly calculated and the new >+ * entry is put before or after the mid >+ * elements... >+ * >+ * 1. >+ * 0 >+ * 1 >+ * 2 <- old_idx >+ * new_idx -> 3 >+ * 3 >+ * 4 >+ * >+ * low_idx1 = 0; >+ * low_idx2 = MIN(old_idx, new_idx); => 2 >+ * low_num = low_idx2 - low_idx1; => 2 >+ * >+ * if (new < old) => new; => no >+ * >+ * mid_idx1 = MIN(old_idx+1, new_idx); => 3 >+ * mid_idx2 = MAX(old_idx, new_idx); => 3 >+ * mid_num = mid_idx2 - mid_idx1; => 0 >+ * >+ * if (new >= old) => new; => yes >+ * >+ * high_idx1 = MAX(old_idx+1, new_idx); => 3 >+ * high_idx2 = num_share_entries; => 5 >+ * high_num = high_idx2 - high_idx1 = 2 >+ * >+ * 2. >+ * 0 >+ * 1 >+ * new_idx -> 2 >+ * 2 <- old_idx >+ * 3 >+ * 4 >+ * >+ * low_idx1 = 0; >+ * low_idx2 = MIN(old_idx, new_idx); => 2 >+ * low_num = low_idx2 - low_idx1; => 2 >+ * >+ * if (new < old) => new; => no >+ * >+ * mid_idx1 = MIN(old_idx+1, new_idx); => 2 >+ * mid_idx2 = MAX(old_idx, new_idx); => 2 >+ * mid_num = mid_idx2 - mid_idx1; => 0 >+ * >+ * if (new >= old) => new; => yes >+ * >+ * high_idx1 = MAX(old_idx+1, new_idx); => 3 >+ * high_idx2 = num_share_entries; => 5 >+ * high_num = high_idx2 - high_idx1 = 2 >+ * >+ * 3. >+ * 0 >+ * 1 <- old_idx >+ * 2 >+ * new_idx -> 3 >+ * 3 >+ * 4 >+ * >+ * low_idx1 = 0; >+ * low_idx2 = MIN(old_idx, new_idx); => 1 >+ * low_num = low_idx2 - low_idx1; => 1 >+ * >+ * if (new < old) => new; => no >+ * >+ * mid_idx1 = MIN(old_idx+1, new_idx); => 2 >+ * mid_idx2 = MAX(old_idx, new_idx); => 3 >+ * mid_num = mid_idx2 - mid_idx1; => 1 >+ * >+ * if (new >= old) => new; => yes >+ * >+ * high_idx1 = MAX(old_idx+1, new_idx); => 3 >+ * high_idx2 = num_share_entries; => 5 >+ * high_num = high_idx2 - high_idx1 = 2 >+ * >+ * 4. >+ * 0 >+ * new_idx -> 1 >+ * 1 >+ * 2 >+ * 3 <- old_idx >+ * 4 >+ * >+ * low_idx1 = 0; >+ * low_idx2 = MIN(old_idx, new_idx); => 1 >+ * low_num = low_idx2 - low_idx1; => 1 >+ * >+ * if (new < old) => new; => yes >+ * >+ * mid_idx1 = MIN(old_idx+1, new_idx); => 1 >+ * mid_idx2 = MAX(old_idx, new_idx); => 3 >+ * mid_num = mid_idx2 - mid_idx1; => 2 >+ * >+ * if (new >= old) => new; => no >+ * >+ * high_idx1 = MAX(old_idx+1, new_idx); => 4 >+ * high_idx2 = num_share_entries; => 5 >+ * high_num = high_idx2 - high_idx1 = 1 >+ * >+ * 5. >+ * new_idx -> 0 >+ * 0 >+ * 1 >+ * 2 >+ * 3 >+ * 4 <- old_idx >+ * >+ * low_idx1 = 0; >+ * low_idx2 = MIN(old_idx, new_idx); => 0 >+ * low_num = low_idx2 - low_idx1; => 0 >+ * >+ * if (new < old) => new; => yes >+ * >+ * mid_idx1 = MIN(old_idx+1, new_idx); => 0 >+ * mid_idx2 = MAX(old_idx, new_idx); => 4 >+ * mid_num = mid_idx2 - mid_idx1; => 4 >+ * >+ * if (new >= old) => new; => no >+ * >+ * high_idx1 = MAX(old_idx+1, new_idx); => 5 >+ * high_idx2 = num_share_entries; => 5 >+ * high_num = high_idx2 - high_idx1 = 0 >+ * >+ * 6. >+ * new_idx -> 0 >+ * 0 <- old_idx >+ * >+ * low_idx1 = 0; >+ * low_idx2 = MIN(old_idx, new_idx); => 0 >+ * low_num = low_idx2 - low_idx1; => 0 >+ * >+ * if (new < old) => new; => no >+ * >+ * mid_idx1 = MIN(old_idx+1, new_idx); => 0 >+ * mid_idx2 = MAX(old_idx, new_idx); => 0 >+ * mid_num = mid_idx2 - mid_idx1; => 0 >+ * >+ * if (new >= old) => new; => yes >+ * >+ * high_idx1 = MAX(old_idx+1, new_idx); => 1 >+ * high_idx2 = num_share_entries; => 1 >+ * high_num = high_idx2 - high_idx1 = 0 >+ * >+ * 7. >+ * 0 <- old_idx >+ * new_idx -> 1 >+ * >+ * low_idx1 = 0; >+ * low_idx2 = MIN(old_idx, new_idx); => 0 >+ * low_num = low_idx2 - low_idx1; => 0 >+ * >+ * if (new < old) => new; => no >+ * >+ * mid_idx1 = MIN(old_idx+1, new_idx); => 1 >+ * mid_idx2 = MAX(old_idx, new_idx); => 1 >+ * mid_num = mid_idx2 - mid_idx1; => 0 >+ * >+ * if (new >= old) => new; => yes >+ * >+ * high_idx1 = MAX(old_idx+1, new_idx); => 1 >+ * high_idx2 = num_share_entries; => 1 >+ * high_num = high_idx2 - high_idx1 = 0 >+ */ >+ low_idx1 = 0; >+ low_idx2 = MIN(old_idx, new_idx); >+ low_num = low_idx2 - low_idx1; >+ mid_idx1 = MIN(old_idx+1, new_idx); >+ mid_idx2 = MAX(old_idx, new_idx); >+ mid_num = mid_idx2 - mid_idx1; >+ high_idx1 = MAX(old_idx+1, new_idx); >+ high_idx2 = ltdb->num_share_entries; >+ high_num = high_idx2 - high_idx1; >+ >+ if (low_num != 0) { >+ dbufs[num_dbufs] = (TDB_DATA) { >+ .dptr = discard_const_p(uint8_t, ltdb->share_entries) + >+ low_idx1 * SHARE_MODE_ENTRY_SIZE, >+ .dsize = low_num * SHARE_MODE_ENTRY_SIZE, >+ }; >+ num_dbufs += 1; >+ } >+ >+ if (new_idx < old_idx) { >+ dbufs[num_dbufs] = (TDB_DATA) { >+ .dptr = e_buf.buf, .dsize = SHARE_MODE_ENTRY_SIZE, >+ }; >+ num_dbufs += 1; >+ } >+ >+ if (mid_num != 0) { >+ dbufs[num_dbufs] = (TDB_DATA) { >+ .dptr = discard_const_p(uint8_t, ltdb->share_entries) + >+ mid_idx1 * SHARE_MODE_ENTRY_SIZE, >+ .dsize = mid_num * SHARE_MODE_ENTRY_SIZE, >+ }; >+ num_dbufs += 1; >+ } >+ >+ if (new_idx >= old_idx) { >+ dbufs[num_dbufs] = (TDB_DATA) { >+ .dptr = e_buf.buf, .dsize = SHARE_MODE_ENTRY_SIZE, >+ }; >+ num_dbufs += 1; >+ } >+ >+ if (high_num != 0) { >+ dbufs[num_dbufs] = (TDB_DATA) { >+ .dptr = discard_const_p(uint8_t, ltdb->share_entries) + >+ high_idx1 * SHARE_MODE_ENTRY_SIZE, >+ .dsize = high_num * SHARE_MODE_ENTRY_SIZE, >+ }; >+ num_dbufs += 1; >+ } > >+ { >+ size_t i; >+ for (i=0; i<num_dbufs; i++) { >+ DBG_DEBUG("dbufs[%zu]=(%p, %zu)\n", >+ i, >+ dbufs[i].dptr, >+ dbufs[i].dsize); >+ } >+ } >+ >+ /* >+ * We completely rewrite the entries... >+ */ >+ ltdb->share_entries = NULL; >+ ltdb->num_share_entries = 0; > d->modified = true; > >- status = share_mode_data_ltdb_store(d, key, ltdb, NULL, 0); >+ status = share_mode_data_ltdb_store(d, key, ltdb, dbufs, num_dbufs); > if (!NT_STATUS_IS_OK(status)) { > DBG_ERR("share_mode_data_ltdb_store failed: %s\n", > nt_errstr(status)); >-- >2.34.1 > > >From 90b86ed2d6eb393e6e9070df27b1fe8a40989545 Mon Sep 17 00:00:00 2001 >From: Stefan Metzmacher <metze@samba.org> >Date: Fri, 30 Aug 2024 14:16:12 +0200 >Subject: [PATCH 10/10] s3:smbd: avoid false positives for got_oplock and > have_other_lease in delay_for_oplock_fn > >stat opens should not cause a oplock/lease downgrade if >they don't have a lease attached to itself. > >Note that opens broken to NONE still count if they are >non-stat opens... > >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15649 >BUG: https://bugzilla.samba.org/show_bug.cgi?id=15651 > >Signed-off-by: Stefan Metzmacher <metze@samba.org> >Reviewed-by: Ralph Boehme <slow@samba.org> > >Autobuild-User(master): Stefan Metzmacher <metze@samba.org> >Autobuild-Date(master): Thu Oct 10 13:59:18 UTC 2024 on atb-devel-224 > >(cherry picked from commit dd5b9e08c7a98c54b62d3b097c75faa09cd17da7) >--- > selftest/knownfail | 1 - > .../knownfail.d/smb2.durable-v2-open.bug15649 | 2 -- > source3/smbd/open.c | 26 ++++++++++++++----- > 3 files changed, 20 insertions(+), 9 deletions(-) > delete mode 100644 selftest/knownfail.d/smb2.durable-v2-open.bug15649 > >diff --git a/selftest/knownfail b/selftest/knownfail >index 03f8b4669944..31e70a1a9d34 100644 >--- a/selftest/knownfail >+++ b/selftest/knownfail >@@ -218,7 +218,6 @@ > ^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.statopen3 > ^samba3.smb2.lease.unlink # we currently do not downgrade RH lease to R after unlink > ^samba4.smb2.ioctl.compress_notsup.*\(ad_dc_ntvfs\) > ^samba3.raw.session.*reauth2 # maybe fix this? >diff --git a/selftest/knownfail.d/smb2.durable-v2-open.bug15649 b/selftest/knownfail.d/smb2.durable-v2-open.bug15649 >deleted file mode 100644 >index 748b6c3150ec..000000000000 >--- a/selftest/knownfail.d/smb2.durable-v2-open.bug15649 >+++ /dev/null >@@ -1,2 +0,0 @@ >-^samba3.smb2.durable-v2-open.stat-and-lease >-^samba3.smb2.durable-v2-open.nonstat-and-lease >diff --git a/source3/smbd/open.c b/source3/smbd/open.c >index bc4dcd67e02c..22a39ac35ef6 100644 >--- a/source3/smbd/open.c >+++ b/source3/smbd/open.c >@@ -2464,7 +2464,7 @@ struct delay_for_oplock_state { > bool first_open_attempt; > bool got_handle_lease; > bool got_oplock; >- bool have_other_lease; >+ bool disallow_write_lease; > uint32_t total_lease_types; > bool delay; > struct blocker_debug_state *blocker_debug_state; >@@ -2572,15 +2572,27 @@ static bool delay_for_oplock_fn( > } > > if (!state->got_oplock && >+ (e->op_type != NO_OPLOCK) && > (e->op_type != LEASE_OPLOCK) && > !share_entry_stale_pid(e)) { > state->got_oplock = true; > } > >- if (!state->have_other_lease && >+ /* >+ * Two things prevent a write lease >+ * to be granted: >+ * >+ * 1. Any oplock or lease (even broken to NONE) >+ * 2. An open with an access mask other than >+ * FILE_READ_ATTRIBUTES, FILE_WRITE_ATTRIBUTES >+ * or SYNCHRONIZE_ACCESS >+ */ >+ if (!state->disallow_write_lease && >+ (e->op_type != NO_OPLOCK || !is_oplock_stat_open(e->access_mask)) && > !is_same_lease(fsp, e, lease) && >- !share_entry_stale_pid(e)) { >- state->have_other_lease = true; >+ !share_entry_stale_pid(e)) >+ { >+ state->disallow_write_lease = true; > } > > if (e_is_lease && is_lease_stat_open(fsp->access_mask)) { >@@ -2814,9 +2826,11 @@ grant: > granted &= ~SMB2_LEASE_READ; > } > >- if (state.have_other_lease) { >+ if (state.disallow_write_lease) { > /* >- * Can grant only one writer >+ * Can grant only a write lease >+ * if there are no other leases >+ * and no other non-stat opens. > */ > granted &= ~SMB2_LEASE_WRITE; > } >-- >2.34.1 >
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:
slow
:
review+
Actions:
View
Attachments on
bug 15649
:
18302
| 18464 |
18465