From 717e167da70f1c31017bc563d4cfb6f365099d16 Mon Sep 17 00:00:00 2001 From: eabdullin Date: Fri, 20 Jun 2025 18:13:23 +0000 Subject: [PATCH] import UBI samba-4.21.3-104.el10_0 --- redhat-4.21.patch | 3655 ++++++++++++++++++++++++++++++++++++++++++++- samba.spec | 8 +- 2 files changed, 3649 insertions(+), 14 deletions(-) diff --git a/redhat-4.21.patch b/redhat-4.21.patch index 1f78869..f0bbdee 100644 --- a/redhat-4.21.patch +++ b/redhat-4.21.patch @@ -1,7 +1,7 @@ From 9032322cc713e82a316b271bb2fa0a867c69b021 Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Mon, 22 Jul 2024 12:26:55 +0200 -Subject: [PATCH 1/6] s3:notifyd: Use a watcher per db record +Subject: [PATCH 01/24] s3:notifyd: Use a watcher per db record MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @@ -509,13 +509,13 @@ index 36c08f47c54..db8e6e1c005 100644 #endif -- -2.48.1 +2.49.0 From da6309049eb21ec5cd6bdf7942203960adbc37c0 Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Thu, 5 Dec 2024 16:35:51 +1300 -Subject: [PATCH 2/6] util: add a crypt wrapper, derived from +Subject: [PATCH 02/24] util: add a crypt wrapper, derived from dsdb:password_hash This is going to be used by the dsdb password_hash module, and exposed @@ -661,13 +661,13 @@ index b4fcfeaba07..7de9c0b7b17 100644 bld.SAMBA_SUBSYSTEM('UNIX_PRIVS', source='unix_privs.c', -- -2.48.1 +2.49.0 From 334093563640f232bb337675417f1e8a410987de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Filipensk=C3=BD?= Date: Mon, 20 Jan 2025 16:00:51 +0100 -Subject: [PATCH 3/6] s3: Add new keytab specifiers +Subject: [PATCH 03/24] s3: Add new keytab specifiers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @@ -2122,13 +2122,13 @@ index 2c38b53ccca..82c64984787 100755 # Other approach could e.g. compare first six entries from the template. # The 6 entries correspond to password and old_password, each has 3 enc. types. -- -2.48.1 +2.49.0 From f1e0fce49fbd1890da053d05c8511010cb7f2911 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Filipensk=C3=BD?= Date: Tue, 14 Jan 2025 11:29:54 +0100 -Subject: [PATCH 4/6] docs-xml:smbdotconf: Document new options for 'sync +Subject: [PATCH 04/24] docs-xml:smbdotconf: Document new options for 'sync machinepassword to keytab' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 @@ -2279,13 +2279,13 @@ index f7dc30023d4..02eaf3162c0 100644 . -- -2.48.1 +2.49.0 From 4dc163e87824aac33107767881d4a47033c5d9dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Filipensk=C3=BD?= Date: Fri, 14 Feb 2025 17:28:54 +0100 -Subject: [PATCH 5/6] s3:libads: Remove specifier for 'host' principal from +Subject: [PATCH 05/24] s3:libads: Remove specifier for 'host' principal from 'sync machine password to keytab' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 @@ -2486,14 +2486,14 @@ index 82c64984787..21edf8b8882 100755 5 aes256-cts-hmac-sha1-96 wurst1/brot@ADDOM.SAMBA.EXAMPLE.COM 6 aes256-cts-hmac-sha1-96 wurst1/brot@ADDOM.SAMBA.EXAMPLE.COM -- -2.48.1 +2.49.0 From 8bb9f6f5d9f5db755dfd950260288dfd746cfbb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Filipensk=C3=BD?= Date: Fri, 14 Feb 2025 17:27:26 +0100 -Subject: [PATCH 6/6] docs: Update documentation for 'sync machine password to - keytab' +Subject: [PATCH 06/24] docs: Update documentation for 'sync machine password + to keytab' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @@ -2578,5 +2578,3634 @@ index 02eaf3162c0..ec3fffc1119 100644 If sync_etypes or sync_kvno or sync_spns is present then winbind connects to DC. For "offline domain join" it might be useful not to use these options. -- -2.48.1 +2.49.0 + + +From 205bed2a3a8cb8d2ff9651244aab02b2f9f602ae Mon Sep 17 00:00:00 2001 +From: Jeremy Allison +Date: Wed, 15 Jan 2025 10:21:19 -0800 +Subject: [PATCH 07/24] auth: Add missing talloc_free() in error code path. +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=15782 + +Signed-off-by: Jeremy Allison +Reviewed-by: Guenther Deschner + +Autobuild-User(master): Günther Deschner +Autobuild-Date(master): Thu Jan 16 14:32:39 UTC 2025 on atb-devel-224 + +(cherry picked from commit c514ce8dcadcbbf0d86f3038d2be0f9253a76b75) +--- + auth/kerberos/kerberos_pac.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/auth/kerberos/kerberos_pac.c b/auth/kerberos/kerberos_pac.c +index b6272ac15eb..1f7d3e7ef26 100644 +--- a/auth/kerberos/kerberos_pac.c ++++ b/auth/kerberos/kerberos_pac.c +@@ -360,6 +360,7 @@ NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx, + if (ret) { + DEBUG(5, ("PAC Decode: Failed to verify the service " + "signature: %s\n", error_message(ret))); ++ talloc_free(tmp_ctx); + return NT_STATUS_ACCESS_DENIED; + } + +-- +2.49.0 + + +From b531c84559e2391c38e4c7640610462046d2d7c6 Mon Sep 17 00:00:00 2001 +From: Jeremy Allison +Date: Thu, 16 Jan 2025 16:12:31 -0800 +Subject: [PATCH 08/24] auth: Cleanup exit code paths in kerberos_decode_pac(). +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +One more memory leak missed and now fixed. tmp_ctx +must be freed once the pac data is talloc_move'd. + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=15782 + +Signed-off-by: Jeremy Allison +Reviewed-by: Jennifer Sutton +Reviewed-by: Christian Ambach +Reviewed-by: Guenther Deschner + +Autobuild-User(master): Günther Deschner +Autobuild-Date(master): Fri Jan 17 12:01:47 UTC 2025 on atb-devel-224 + +(cherry picked from commit f9eb0b248da0689c82656f3e482161c45749afb6) +--- + auth/kerberos/kerberos_pac.c | 88 ++++++++++++++++++------------------ + 1 file changed, 43 insertions(+), 45 deletions(-) + +diff --git a/auth/kerberos/kerberos_pac.c b/auth/kerberos/kerberos_pac.c +index 1f7d3e7ef26..4c61cfe838f 100644 +--- a/auth/kerberos/kerberos_pac.c ++++ b/auth/kerberos/kerberos_pac.c +@@ -137,7 +137,7 @@ NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx, + time_t tgs_authtime, + struct PAC_DATA **pac_data_out) + { +- NTSTATUS status; ++ NTSTATUS status = NT_STATUS_NO_MEMORY; + enum ndr_err_code ndr_err; + krb5_error_code ret; + DATA_BLOB modified_pac_blob; +@@ -173,8 +173,8 @@ NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx, + kdc_sig_wipe = talloc(tmp_ctx, struct PAC_SIGNATURE_DATA); + srv_sig_wipe = talloc(tmp_ctx, struct PAC_SIGNATURE_DATA); + if (!pac_data_raw || !pac_data || !kdc_sig_wipe || !srv_sig_wipe) { +- talloc_free(tmp_ctx); +- return NT_STATUS_NO_MEMORY; ++ status = NT_STATUS_NO_MEMORY; ++ goto out; + } + + ndr_err = ndr_pull_struct_blob(&pac_data_blob, pac_data, pac_data, +@@ -183,15 +183,14 @@ NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx, + status = ndr_map_error2ntstatus(ndr_err); + DEBUG(0,("can't parse the PAC: %s\n", + nt_errstr(status))); +- talloc_free(tmp_ctx); +- return status; ++ goto out; + } + + if (pac_data->num_buffers < 4) { + /* we need logon_info, service_key and kdc_key */ + DEBUG(0,("less than 4 PAC buffers\n")); +- talloc_free(tmp_ctx); +- return NT_STATUS_INVALID_PARAMETER; ++ status = NT_STATUS_INVALID_PARAMETER; ++ goto out; + } + + ndr_err = ndr_pull_struct_blob( +@@ -201,15 +200,14 @@ NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx, + status = ndr_map_error2ntstatus(ndr_err); + DEBUG(0,("can't parse the PAC: %s\n", + nt_errstr(status))); +- talloc_free(tmp_ctx); +- return status; ++ goto out; + } + + if (pac_data_raw->num_buffers < 4) { + /* we need logon_info, service_key and kdc_key */ + DEBUG(0,("less than 4 PAC buffers\n")); +- talloc_free(tmp_ctx); +- return NT_STATUS_INVALID_PARAMETER; ++ status = NT_STATUS_INVALID_PARAMETER; ++ goto out; + } + + if (pac_data->num_buffers != pac_data_raw->num_buffers) { +@@ -217,8 +215,8 @@ NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx, + DEBUG(0, ("misparse! PAC_DATA has %d buffers while " + "PAC_DATA_RAW has %d\n", pac_data->num_buffers, + pac_data_raw->num_buffers)); +- talloc_free(tmp_ctx); +- return NT_STATUS_INVALID_PARAMETER; ++ status = NT_STATUS_INVALID_PARAMETER; ++ goto out; + } + + for (i=0; i < pac_data->num_buffers; i++) { +@@ -229,8 +227,8 @@ NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx, + DEBUG(0, ("misparse! PAC_DATA buffer %d has type " + "%d while PAC_DATA_RAW has %d\n", i, + data_buf->type, raw_buf->type)); +- talloc_free(tmp_ctx); +- return NT_STATUS_INVALID_PARAMETER; ++ status = NT_STATUS_INVALID_PARAMETER; ++ goto out; + } + switch (data_buf->type) { + case PAC_TYPE_LOGON_INFO: +@@ -263,26 +261,26 @@ NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx, + + if (!logon_info) { + DEBUG(0,("PAC no logon_info\n")); +- talloc_free(tmp_ctx); +- return NT_STATUS_INVALID_PARAMETER; ++ status = NT_STATUS_INVALID_PARAMETER; ++ goto out; + } + + if (!logon_name) { + DEBUG(0,("PAC no logon_name\n")); +- talloc_free(tmp_ctx); +- return NT_STATUS_INVALID_PARAMETER; ++ status = NT_STATUS_INVALID_PARAMETER; ++ goto out; + } + + if (!srv_sig_ptr || !srv_sig_blob) { + DEBUG(0,("PAC no srv_key\n")); +- talloc_free(tmp_ctx); +- return NT_STATUS_INVALID_PARAMETER; ++ status = NT_STATUS_INVALID_PARAMETER; ++ goto out; + } + + if (!kdc_sig_ptr || !kdc_sig_blob) { + DEBUG(0,("PAC no kdc_key\n")); +- talloc_free(tmp_ctx); +- return NT_STATUS_INVALID_PARAMETER; ++ status = NT_STATUS_INVALID_PARAMETER; ++ goto out; + } + + /* Find and zero out the signatures, +@@ -297,8 +295,7 @@ NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx, + status = ndr_map_error2ntstatus(ndr_err); + DEBUG(0,("can't parse the KDC signature: %s\n", + nt_errstr(status))); +- talloc_free(tmp_ctx); +- return status; ++ goto out; + } + + ndr_err = ndr_pull_struct_blob( +@@ -308,8 +305,7 @@ NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx, + status = ndr_map_error2ntstatus(ndr_err); + DEBUG(0,("can't parse the SRV signature: %s\n", + nt_errstr(status))); +- talloc_free(tmp_ctx); +- return status; ++ goto out; + } + + /* Now zero the decoded structure */ +@@ -326,8 +322,7 @@ NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx, + status = ndr_map_error2ntstatus(ndr_err); + DEBUG(0,("can't repack the KDC signature: %s\n", + nt_errstr(status))); +- talloc_free(tmp_ctx); +- return status; ++ goto out; + } + ndr_err = ndr_push_struct_blob( + srv_sig_blob, pac_data_raw, srv_sig_wipe, +@@ -336,8 +331,7 @@ NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx, + status = ndr_map_error2ntstatus(ndr_err); + DEBUG(0,("can't repack the SRV signature: %s\n", + nt_errstr(status))); +- talloc_free(tmp_ctx); +- return status; ++ goto out; + } + + /* push out the whole structure, but now with zero'ed signatures */ +@@ -348,8 +342,7 @@ NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx, + status = ndr_map_error2ntstatus(ndr_err); + DEBUG(0,("can't repack the RAW PAC: %s\n", + nt_errstr(status))); +- talloc_free(tmp_ctx); +- return status; ++ goto out; + } + + if (service_keyblock) { +@@ -360,8 +353,8 @@ NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx, + if (ret) { + DEBUG(5, ("PAC Decode: Failed to verify the service " + "signature: %s\n", error_message(ret))); +- talloc_free(tmp_ctx); +- return NT_STATUS_ACCESS_DENIED; ++ status = NT_STATUS_ACCESS_DENIED; ++ goto out; + } + + if (krbtgt_keyblock) { +@@ -371,8 +364,8 @@ NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx, + if (ret) { + DEBUG(1, ("PAC Decode: Failed to verify the KDC signature: %s\n", + smb_get_krb5_error_message(context, ret, tmp_ctx))); +- talloc_free(tmp_ctx); +- return NT_STATUS_ACCESS_DENIED; ++ status = NT_STATUS_ACCESS_DENIED; ++ goto out; + } + } + } +@@ -388,8 +381,8 @@ NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx, + nt_time_string(tmp_ctx, logon_name->logon_time))); + DEBUG(2, ("PAC Decode: Ticket: %s\n", + nt_time_string(tmp_ctx, tgs_authtime_nttime))); +- talloc_free(tmp_ctx); +- return NT_STATUS_ACCESS_DENIED; ++ status = NT_STATUS_ACCESS_DENIED; ++ goto out; + } + } + +@@ -401,8 +394,8 @@ NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx, + if (ret) { + DEBUG(2, ("Could not unparse name from ticket to match with name from PAC: [%s]:%s\n", + logon_name->account_name, error_message(ret))); +- talloc_free(tmp_ctx); +- return NT_STATUS_INVALID_PARAMETER; ++ status = NT_STATUS_INVALID_PARAMETER; ++ goto out; + } + + bool_ret = strcmp(client_principal_string, logon_name->account_name) == 0; +@@ -413,8 +406,8 @@ NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx, + logon_name->account_name, + client_principal_string)); + SAFE_FREE(client_principal_string); +- talloc_free(tmp_ctx); +- return NT_STATUS_ACCESS_DENIED; ++ status = NT_STATUS_ACCESS_DENIED; ++ goto out; + } + SAFE_FREE(client_principal_string); + +@@ -435,10 +428,15 @@ NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx, + } + + if (pac_data_out) { +- *pac_data_out = talloc_steal(mem_ctx, pac_data); ++ *pac_data_out = talloc_move(mem_ctx, &pac_data); + } + +- return NT_STATUS_OK; ++ status = NT_STATUS_OK; ++ ++ out: ++ ++ TALLOC_FREE(tmp_ctx); ++ return status; + } + + NTSTATUS kerberos_pac_logon_info(TALLOC_CTX *mem_ctx, +-- +2.49.0 + + +From ffdb675281389635e34b6f06d68222db5f2e83a5 Mon Sep 17 00:00:00 2001 +From: Ralph Boehme +Date: Thu, 9 Jan 2025 08:57:17 +0100 +Subject: [PATCH 09/24] dbwrap: check for option "tdb_hash_size:DBNAME.tdb" in + db_open() + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=15767 + +Signed-off-by: Ralph Boehme +Reviewed-by: Stefan Metzmacher +(cherry picked from commit 7eb135c42d530a16e80e165d9e8e99d920797f12) +--- + source3/lib/dbwrap/dbwrap_open.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/source3/lib/dbwrap/dbwrap_open.c b/source3/lib/dbwrap/dbwrap_open.c +index 52c8a94aeff..91556f22819 100644 +--- a/source3/lib/dbwrap/dbwrap_open.c ++++ b/source3/lib/dbwrap/dbwrap_open.c +@@ -80,6 +80,11 @@ struct db_context *db_open(TALLOC_CTX *mem_ctx, + base = name; + } + ++ hash_size = lp_parm_int(GLOBAL_SECTION_SNUM, ++ "tdb_hash_size", ++ base, ++ hash_size); ++ + if (tdb_flags & TDB_CLEAR_IF_FIRST) { + bool try_readonly = false; + +-- +2.49.0 + + +From fd7331e9e50c130d98b490c3cc1d8fa77ec575a1 Mon Sep 17 00:00:00 2001 +From: Ralph Boehme +Date: Thu, 9 Jan 2025 12:27:43 +0100 +Subject: [PATCH 10/24] smbtorture: add test "open-brlock-deadlock" + +smbtorture reproducer for bug 15767. As it needs a very specific setup that +can't easily be done in selftest, the test is only executed when manually called +with + + --option=torture:open_brlock_deadlock_timemout=SEC + +To prepare the setup for the test set: + + tdb_hash_size:locking.tdb = 1 + tdb_hash_size:brlock.tdb = 1 + +and remove both tdb from disk which is needed so the TDBs get recreated with the +new hash_size. + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=15767 + +Signed-off-by: Ralph Boehme +Reviewed-by: Stefan Metzmacher +(cherry picked from commit 7c60498cee7dca5770d4d1f623c472d585ae9cae) +--- + source4/torture/smb2/lock.c | 283 ++++++++++++++++++++++++++++++++++++ + 1 file changed, 283 insertions(+) + +diff --git a/source4/torture/smb2/lock.c b/source4/torture/smb2/lock.c +index eac0d557fc3..e5cf61a471a 100644 +--- a/source4/torture/smb2/lock.c ++++ b/source4/torture/smb2/lock.c +@@ -3465,6 +3465,288 @@ done: + return ret; + } + ++struct open_brlock_deadlock_state { ++ bool stop; ++ bool ok; ++ struct torture_context *tctx; ++ struct smb2_tree *tree1; ++ struct smb2_tree *tree2; ++ struct smb2_create cr; ++ struct smb2_request *cr_req; ++ struct smb2_close cl; ++ struct smb2_request *cl_req; ++ ++ struct smb2_lock_element el; ++ struct smb2_lock lock; ++ struct smb2_request *lock_req; ++}; ++ ++static void test_open_brlock_deadlock_loop_opened(struct smb2_request *req); ++ ++static void test_open_brlock_deadlock_loop_open( ++ struct open_brlock_deadlock_state *state) ++{ ++ if (state->stop) { ++ return; ++ } ++ ++ state->cr_req = smb2_create_send(state->tree1, &state->cr); ++ torture_assert_goto(state->tctx, state->cr_req != NULL, state->ok, failed, ++ "smb2_create_send failed\n"); ++ ++ state->cr_req->async.fn = test_open_brlock_deadlock_loop_opened; ++ state->cr_req->async.private_data = state; ++ return; ++failed: ++ state->stop = true; ++} ++ ++static void test_open_brlock_deadlock_loop_close( ++ struct open_brlock_deadlock_state *state); ++ ++static void test_open_brlock_deadlock_loop_opened(struct smb2_request *req) ++{ ++ struct open_brlock_deadlock_state *state = req->async.private_data; ++ TALLOC_CTX *frame = talloc_stackframe(); ++ NTSTATUS status; ++ ++ status = smb2_create_recv(req, frame, &state->cr); ++ torture_assert_ntstatus_ok_goto(state->tctx, status, ++ state->ok, failed, __location__); ++ state->cr_req = NULL; ++ ++ TALLOC_FREE(frame); ++ test_open_brlock_deadlock_loop_close(state); ++ return; ++ ++failed: ++ state->stop = true; ++ TALLOC_FREE(frame); ++} ++ ++static void test_open_brlock_deadlock_loop_closed(struct smb2_request *req); ++ ++static void test_open_brlock_deadlock_loop_close( ++ struct open_brlock_deadlock_state *state) ++{ ++ if (state->stop) { ++ return; ++ } ++ ++ state->cl.in.file = state->cr.out.file; ++ state->cl_req = smb2_close_send(state->tree1, &state->cl); ++ torture_assert_goto(state->tctx, state->cl_req != NULL, state->ok, failed, ++ "smb2_create_send failed\n"); ++ ++ state->cl_req->async.fn = test_open_brlock_deadlock_loop_closed; ++ state->cl_req->async.private_data = state; ++ return; ++failed: ++ state->stop = true; ++} ++ ++static void test_open_brlock_deadlock_loop_closed(struct smb2_request *req) ++{ ++ struct open_brlock_deadlock_state *state = req->async.private_data; ++ TALLOC_CTX *frame = talloc_stackframe(); ++ NTSTATUS status; ++ ++ status = smb2_close_recv(req, &state->cl); ++ torture_assert_ntstatus_ok_goto(state->tctx, status, ++ state->ok, failed, __location__); ++ state->cl_req = NULL; ++ ++ TALLOC_FREE(frame); ++ test_open_brlock_deadlock_loop_open(state); ++ return; ++ ++failed: ++ state->stop = true; ++ TALLOC_FREE(frame); ++} ++ ++static void test_open_brlock_deadlock_loop_locked(struct smb2_request *req); ++ ++static void test_open_brlock_deadlock_loop_lock( ++ struct open_brlock_deadlock_state *state) ++{ ++ if (state->stop) { ++ return; ++ } ++ ++ state->el.flags = SMB2_LOCK_FLAG_EXCLUSIVE; ++ ++ state->lock_req = smb2_lock_send(state->tree2, &state->lock); ++ torture_assert_goto(state->tctx, state->lock_req != NULL, ++ state->ok, failed, ++ "smb2_create_send failed\n"); ++ ++ state->lock_req->async.fn = test_open_brlock_deadlock_loop_locked; ++ state->lock_req->async.private_data = state; ++ return; ++failed: ++ state->stop = true; ++} ++ ++static void test_open_brlock_deadlock_loop_unlock( ++ struct open_brlock_deadlock_state *state); ++ ++static void test_open_brlock_deadlock_loop_locked(struct smb2_request *req) ++{ ++ struct open_brlock_deadlock_state *state = req->async.private_data; ++ TALLOC_CTX *frame = talloc_stackframe(); ++ NTSTATUS status; ++ ++ status = smb2_lock_recv(req, &state->lock); ++ torture_assert_ntstatus_ok_goto(state->tctx, status, ++ state->ok, failed, __location__); ++ state->lock_req = NULL; ++ ++ TALLOC_FREE(frame); ++ test_open_brlock_deadlock_loop_unlock(state); ++ return; ++ ++failed: ++ state->stop = true; ++ TALLOC_FREE(frame); ++} ++ ++static void test_open_brlock_deadlock_loop_unlocked(struct smb2_request *req); ++ ++static void test_open_brlock_deadlock_loop_unlock( ++ struct open_brlock_deadlock_state *state) ++{ ++ if (state->stop) { ++ return; ++ } ++ ++ state->el.flags = SMB2_LOCK_FLAG_UNLOCK; ++ ++ state->lock_req = smb2_lock_send(state->tree2, &state->lock); ++ torture_assert_goto(state->tctx, state->lock_req != NULL, ++ state->ok, failed, ++ "smb2_create_send failed\n"); ++ ++ state->lock_req->async.fn = test_open_brlock_deadlock_loop_unlocked; ++ state->lock_req->async.private_data = state; ++ return; ++failed: ++ state->stop = true; ++} ++ ++static void test_open_brlock_deadlock_loop_unlocked(struct smb2_request *req) ++{ ++ struct open_brlock_deadlock_state *state = req->async.private_data; ++ TALLOC_CTX *frame = talloc_stackframe(); ++ NTSTATUS status; ++ ++ status = smb2_lock_recv(req, &state->lock); ++ torture_assert_ntstatus_ok_goto(state->tctx, status, ++ state->ok, failed, __location__); ++ state->lock_req = NULL; ++ ++ TALLOC_FREE(frame); ++ test_open_brlock_deadlock_loop_lock(state); ++ return; ++ ++failed: ++ state->stop = true; ++ TALLOC_FREE(frame); ++} ++ ++ ++static void test_open_brlock_deadlock_timeout(struct tevent_context *ev, ++ struct tevent_timer *te, ++ struct timeval current_time, ++ void *private_data) ++{ ++ struct open_brlock_deadlock_state *state = private_data; ++ state->stop = true; ++} ++ ++static bool test_open_brlock_deadlock(struct torture_context *tctx, ++ struct smb2_tree *tree1, ++ struct smb2_tree *tree2) ++{ ++ int timeout_sec = torture_setting_int(tctx, "open_brlock_deadlock_timemout", 0); ++ const char *fname1 = BASEDIR "\\test_open_brlock_deadlock1.txt"; ++ const char *fname2 = BASEDIR "\\test_open_brlock_deadlock2.txt"; ++ struct open_brlock_deadlock_state state; ++ struct smb2_handle h = {}; ++ uint8_t buf[200]; ++ struct tevent_timer *te = NULL; ++ NTSTATUS status; ++ bool ret = true; ++ ++ if (timeout_sec == 0) { ++ torture_skip_goto(tctx, done, "Test skipped, pass '--option=torture:open_brlock_deadlock_timemout=SEC' to run\n"); ++ } ++ ++ state = (struct open_brlock_deadlock_state) { ++ .tctx = tctx, ++ .tree1 = tree1, ++ .tree2 = tree2, ++ }; ++ ++ ret = smb2_util_setup_dir(tctx, tree1, BASEDIR); ++ torture_assert_goto(tctx, ret, ret, done, ++ "smb2_util_setup_dir failed"); ++ ++ status = torture_smb2_testfile(tree1, fname1, &h); ++ torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ++ "torture_smb2_testfile failed"); ++ status = smb2_util_close(tree1, h); ++ torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ++ "smb2_util_close failed"); ++ ++ status = torture_smb2_testfile(tree2, fname2, &h); ++ torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ++ "torture_smb2_testfile failed"); ++ ++ status = smb2_util_write(tree2, h, buf, 0, ARRAY_SIZE(buf)); ++ torture_assert_ntstatus_ok_goto(tctx, status, ret, done, ++ "smb2_util_write failed"); ++ ++ state.cr = (struct smb2_create) { ++ .in.desired_access = SEC_FILE_READ_DATA, ++ .in.share_access = NTCREATEX_SHARE_ACCESS_MASK, ++ .in.create_disposition = NTCREATEX_DISP_OPEN, ++ .in.fname = fname1, ++ }; ++ ++ state.el = (struct smb2_lock_element) { ++ .length = 1, ++ .offset = 0, ++ }; ++ state.lock = (struct smb2_lock) { ++ .in.locks = &state.el, ++ .in.lock_count = 1, ++ .in.file.handle = h, ++ }; ++ ++ te = tevent_add_timer(tctx->ev, ++ tctx, ++ timeval_current_ofs(timeout_sec, 0), ++ test_open_brlock_deadlock_timeout, ++ &state); ++ torture_assert_goto(tctx, te != NULL, ret, done, __location__); ++ ++ test_open_brlock_deadlock_loop_open(&state); ++ test_open_brlock_deadlock_loop_lock(&state); ++ ++ while (!state.stop) { ++ int rc = tevent_loop_once(tctx->ev); ++ torture_assert_int_equal(tctx, rc, 0, "tevent_loop_once"); ++ } ++ ++done: ++ if (!smb2_util_handle_empty(h)) { ++ smb2_util_close(tree2, h); ++ } ++ smb2_deltree(tree1, BASEDIR); ++ return ret; ++} ++ + /* basic testing of SMB2 locking + */ + struct torture_suite *torture_smb2_lock_init(TALLOC_CTX *ctx) +@@ -3506,6 +3788,7 @@ struct torture_suite *torture_smb2_lock_init(TALLOC_CTX *ctx) + torture_suite_add_1smb2_test(suite, "replay_smb3_specification_multi", + test_replay_smb3_specification_multi); + torture_suite_add_1smb2_test(suite, "ctdb-delrec-deadlock", test_deadlock); ++ torture_suite_add_2smb2_test(suite, "open-brlock-deadlock", test_open_brlock_deadlock); + + suite->description = talloc_strdup(suite, "SMB2-LOCK tests"); + +-- +2.49.0 + + +From fe9563bc0140fbbb2aa5a6342a1948984c59043a Mon Sep 17 00:00:00 2001 +From: Stefan Metzmacher +Date: Mon, 6 Jan 2025 15:59:27 +0100 +Subject: [PATCH 11/24] s3/brlock: split out brl_get_locks_readonly_parse() + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=15767 + +Pair-Programmed-With: Ralph Boehme +Signed-off-by: Ralph Boehme +Signed-off-by: Stefan Metzmacher +(cherry picked from commit 94e7cbcc32b73e4d56e7209e04d22d4270a6eb5b) +--- + source3/locking/brlock.c | 43 ++++++++++++++++++++++++++-------------- + 1 file changed, 28 insertions(+), 15 deletions(-) + +diff --git a/source3/locking/brlock.c b/source3/locking/brlock.c +index b0295174954..c75b83c048d 100644 +--- a/source3/locking/brlock.c ++++ b/source3/locking/brlock.c +@@ -1768,29 +1768,18 @@ static void brl_get_locks_readonly_parser(TDB_DATA key, TDB_DATA data, + *state->br_lock = br_lck; + } + +-struct byte_range_lock *brl_get_locks_readonly(files_struct *fsp) ++static struct byte_range_lock *brl_get_locks_readonly_parse(TALLOC_CTX *mem_ctx, ++ files_struct *fsp) + { + struct byte_range_lock *br_lock = NULL; + struct brl_get_locks_readonly_state state; + NTSTATUS status; + +- DEBUG(10, ("seqnum=%d, fsp->brlock_seqnum=%d\n", +- dbwrap_get_seqnum(brlock_db), fsp->brlock_seqnum)); +- +- if ((fsp->brlock_rec != NULL) +- && (dbwrap_get_seqnum(brlock_db) == fsp->brlock_seqnum)) { +- /* +- * We have cached the brlock_rec and the database did not +- * change. +- */ +- return fsp->brlock_rec; +- } +- + /* + * Parse the record fresh from the database + */ + +- state.mem_ctx = fsp; ++ state.mem_ctx = mem_ctx; + state.br_lock = &br_lock; + + status = dbwrap_parse_record( +@@ -1803,7 +1792,7 @@ struct byte_range_lock *brl_get_locks_readonly(files_struct *fsp) + /* + * No locks on this file. Return an empty br_lock. + */ +- br_lock = talloc_zero(fsp, struct byte_range_lock); ++ br_lock = talloc_zero(mem_ctx, struct byte_range_lock); + if (br_lock == NULL) { + return NULL; + } +@@ -1821,6 +1810,30 @@ struct byte_range_lock *brl_get_locks_readonly(files_struct *fsp) + br_lock->modified = false; + br_lock->record = NULL; + ++ return br_lock; ++} ++ ++struct byte_range_lock *brl_get_locks_readonly(files_struct *fsp) ++{ ++ struct byte_range_lock *br_lock = NULL; ++ ++ DEBUG(10, ("seqnum=%d, fsp->brlock_seqnum=%d\n", ++ dbwrap_get_seqnum(brlock_db), fsp->brlock_seqnum)); ++ ++ if ((fsp->brlock_rec != NULL) ++ && (dbwrap_get_seqnum(brlock_db) == fsp->brlock_seqnum)) { ++ /* ++ * We have cached the brlock_rec and the database did not ++ * change. ++ */ ++ return fsp->brlock_rec; ++ } ++ ++ br_lock = brl_get_locks_readonly_parse(fsp, fsp); ++ if (br_lock == NULL) { ++ return NULL; ++ } ++ + /* + * Cache the brlock struct, invalidated when the dbwrap_seqnum + * changes. See beginning of this routine. +-- +2.49.0 + + +From 2c3e6fed2b1fc6e854293446ed74b9e98900815e Mon Sep 17 00:00:00 2001 +From: Stefan Metzmacher +Date: Mon, 6 Jan 2025 17:07:11 +0100 +Subject: [PATCH 12/24] s3/brlock: add brl_req_set() + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=15767 + +Signed-off-by: Stefan Metzmacher +Reviewed-by: Ralph Boehme +(cherry picked from commit c9c04c7d75dee0c3e6e843b581624a3852042057) +--- + source3/locking/brlock.c | 9 +++++++++ + source3/locking/proto.h | 3 +++ + 2 files changed, 12 insertions(+) + +diff --git a/source3/locking/brlock.c b/source3/locking/brlock.c +index c75b83c048d..51d9dc2e599 100644 +--- a/source3/locking/brlock.c ++++ b/source3/locking/brlock.c +@@ -84,6 +84,15 @@ struct files_struct *brl_fsp(struct byte_range_lock *brl) + return brl->fsp; + } + ++ ++void brl_req_set(struct byte_range_lock *br_lck, ++ TALLOC_CTX *req_mem_ctx, ++ const struct GUID *req_guid) ++{ ++ br_lck->req_mem_ctx = req_mem_ctx; ++ br_lck->req_guid = req_guid; ++} ++ + TALLOC_CTX *brl_req_mem_ctx(const struct byte_range_lock *brl) + { + if (brl->req_mem_ctx == NULL) { +diff --git a/source3/locking/proto.h b/source3/locking/proto.h +index 7fc177d7aa6..3413596baed 100644 +--- a/source3/locking/proto.h ++++ b/source3/locking/proto.h +@@ -32,6 +32,9 @@ void brl_shutdown(void); + + unsigned int brl_num_locks(const struct byte_range_lock *brl); + struct files_struct *brl_fsp(struct byte_range_lock *brl); ++void brl_req_set(struct byte_range_lock *br_lck, ++ TALLOC_CTX *req_mem_ctx, ++ const struct GUID *req_guid); + TALLOC_CTX *brl_req_mem_ctx(const struct byte_range_lock *brl); + const struct GUID *brl_req_guid(const struct byte_range_lock *brl); + +-- +2.49.0 + + +From 1a3c7565c112ba2bf6342c93f74b1888fb1dcdfc Mon Sep 17 00:00:00 2001 +From: Ralph Boehme +Date: Sat, 1 Feb 2025 10:37:40 +0100 +Subject: [PATCH 13/24] s3/brlock: add share_mode_do_locked_brl() + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=15767 + +Pair-Programmed-With: Stefan Metzmacher +Signed-off-by: Ralph Boehme +Signed-off-by: Stefan Metzmacher +(cherry picked from commit e17fb732c89f8b34de00904383044de3c4f85bd0) +--- + source3/locking/brlock.c | 103 +++++++++++++++++++++++++++++++++++++++ + source3/locking/proto.h | 8 +++ + 2 files changed, 111 insertions(+) + +diff --git a/source3/locking/brlock.c b/source3/locking/brlock.c +index 51d9dc2e599..fcbce52f9c1 100644 +--- a/source3/locking/brlock.c ++++ b/source3/locking/brlock.c +@@ -34,6 +34,7 @@ + #include "serverid.h" + #include "messages.h" + #include "util_tdb.h" ++#include "source3/locking/share_mode_lock.h" + + #undef DBGC_CLASS + #define DBGC_CLASS DBGC_LOCKING +@@ -1927,3 +1928,105 @@ done: + talloc_free(frame); + return ret; + } ++ ++struct share_mode_do_locked_brl_state { ++ share_mode_do_locked_brl_fn_t cb; ++ void *cb_data; ++ struct files_struct *fsp; ++ NTSTATUS status; ++}; ++ ++static void share_mode_do_locked_brl_fn(struct share_mode_lock *lck, ++ void *private_data) ++{ ++ struct share_mode_do_locked_brl_state *state = private_data; ++ struct byte_range_lock *br_lck = NULL; ++ TDB_DATA key = make_tdb_data((uint8_t *)&state->fsp->file_id, ++ sizeof(state->fsp->file_id)); ++ ++ if (lp_locking(state->fsp->conn->params) && ++ state->fsp->fsp_flags.can_lock) ++ { ++ br_lck = brl_get_locks_readonly_parse(talloc_tos(), ++ state->fsp); ++ if (br_lck == NULL) { ++ state->status = NT_STATUS_NO_MEMORY; ++ return; ++ } ++ } ++ ++ state->cb(lck, br_lck, state->cb_data); ++ ++ if (br_lck == NULL || !br_lck->modified) { ++ TALLOC_FREE(br_lck); ++ return; ++ } ++ ++ br_lck->record = dbwrap_fetch_locked(brlock_db, br_lck, key); ++ if (br_lck->record == NULL) { ++ DBG_ERR("Could not lock byte range lock entry for '%s'\n", ++ fsp_str_dbg(state->fsp)); ++ TALLOC_FREE(br_lck); ++ state->status = NT_STATUS_INTERNAL_DB_ERROR; ++ return; ++ } ++ ++ byte_range_lock_flush(br_lck); ++ share_mode_wakeup_waiters(br_lck->fsp->file_id); ++ TALLOC_FREE(br_lck); ++} ++ ++/* ++ * Run cb with a glock'ed locking.tdb record, providing both a share_mode_lock ++ * and a br_lck object. An initial read-only, but upgradable, br_lck object is ++ * fetched from brlock.tdb while holding the glock on the locking.tdb record. ++ * ++ * This function only ever hold one low-level TDB chainlock at a time on either ++ * locking.tdb or brlock.tdb, so it can't run afoul any lock order violations. ++ * ++ * Note that br_lck argument in the callback might be NULL in case lp_locking() ++ * is disabled, the fsp doesn't allow locking or is a directory, so either ++ * the caller or the callback have to check for this. ++ */ ++NTSTATUS share_mode_do_locked_brl(files_struct *fsp, ++ share_mode_do_locked_brl_fn_t cb, ++ void *cb_data) ++{ ++ static bool recursion_guard; ++ TALLOC_CTX *frame = NULL; ++ struct share_mode_do_locked_brl_state state = { ++ .fsp = fsp, ++ .cb = cb, ++ .cb_data = cb_data, ++ }; ++ NTSTATUS status; ++ ++ SMB_ASSERT(!recursion_guard); ++ ++ /* silently return ok on print files as we don't do locking there */ ++ if (fsp->print_file) { ++ return NT_STATUS_OK; ++ } ++ ++ frame = talloc_stackframe(); ++ ++ recursion_guard = true; ++ status = share_mode_do_locked_vfs_allowed( ++ fsp->file_id, ++ share_mode_do_locked_brl_fn, ++ &state); ++ recursion_guard = false; ++ if (!NT_STATUS_IS_OK(status)) { ++ DBG_ERR("share_mode_do_locked_vfs_allowed() failed for %s - %s\n", ++ fsp_str_dbg(fsp), nt_errstr(status)); ++ TALLOC_FREE(frame); ++ return status; ++ } ++ if (!NT_STATUS_IS_OK(state.status)) { ++ TALLOC_FREE(frame); ++ return state.status; ++ } ++ ++ TALLOC_FREE(frame); ++ return NT_STATUS_OK; ++} +diff --git a/source3/locking/proto.h b/source3/locking/proto.h +index 3413596baed..c9d769ba53f 100644 +--- a/source3/locking/proto.h ++++ b/source3/locking/proto.h +@@ -87,6 +87,14 @@ struct byte_range_lock *brl_get_locks_for_locking(TALLOC_CTX *mem_ctx, + files_struct *fsp, + TALLOC_CTX *req_mem_ctx, + const struct GUID *req_guid); ++struct share_mode_lock; ++typedef void (*share_mode_do_locked_brl_fn_t)( ++ struct share_mode_lock *lck, ++ struct byte_range_lock *br_lck, /* br_lck can be NULL */ ++ void *private_data); ++NTSTATUS share_mode_do_locked_brl(files_struct *fsp, ++ share_mode_do_locked_brl_fn_t fn, ++ void *private_data); + struct byte_range_lock *brl_get_locks(TALLOC_CTX *mem_ctx, + files_struct *fsp); + struct byte_range_lock *brl_get_locks_readonly(files_struct *fsp); +-- +2.49.0 + + +From f29fdf9d8da34db5b129f2ccd00a597dcfe68e55 Mon Sep 17 00:00:00 2001 +From: Ralph Boehme +Date: Wed, 8 Jan 2025 15:43:04 +0100 +Subject: [PATCH 14/24] s3/brlock: don't increment current_lock_count if + do_lock_fn() failed + +Also only assign psmblctx and pblocker_pid if the lock request failed. + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=15767 + +Signed-off-by: Ralph Boehme +Reviewed-by: Stefan Metzmacher +(cherry picked from commit 3a0c6e99de4377f44bc29766b6ceb79040caed9f) +--- + source3/locking/locking.c | 20 +++++++++++--------- + 1 file changed, 11 insertions(+), 9 deletions(-) + +diff --git a/source3/locking/locking.c b/source3/locking/locking.c +index 41b54b14c6b..ea692711627 100644 +--- a/source3/locking/locking.c ++++ b/source3/locking/locking.c +@@ -344,19 +344,21 @@ NTSTATUS do_lock(files_struct *fsp, + nt_errstr(status)); + return status; + } +- +- if (psmblctx != NULL) { +- *psmblctx = state.blocker_smblctx; +- } +- if (pblocker_pid != NULL) { +- *pblocker_pid = state.blocker_pid; ++ if (!NT_STATUS_IS_OK(state.status)) { ++ DBG_DEBUG("do_lock_fn returned %s\n", ++ nt_errstr(state.status)); ++ if (psmblctx != NULL) { ++ *psmblctx = state.blocker_smblctx; ++ } ++ if (pblocker_pid != NULL) { ++ *pblocker_pid = state.blocker_pid; ++ } ++ return state.status; + } + +- DBG_DEBUG("returning status=%s\n", nt_errstr(state.status)); +- + increment_current_lock_count(fsp, lock_flav); + +- return state.status; ++ return NT_STATUS_OK; + } + + /**************************************************************************** +-- +2.49.0 + + +From 68a48f73a8987db27d80d13f2871fd9c057df196 Mon Sep 17 00:00:00 2001 +From: Ralph Boehme +Date: Wed, 29 Jan 2025 06:13:29 +0100 +Subject: [PATCH 15/24] s3/locking: add brl_set_modified() + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=15767 + +Signed-off-by: Ralph Boehme +Reviewed-by: Stefan Metzmacher +(cherry picked from commit 2772f147c9b13cd2160181c4f7905b54ab765054) +--- + source3/locking/brlock.c | 5 +++++ + source3/locking/proto.h | 1 + + 2 files changed, 6 insertions(+) + +diff --git a/source3/locking/brlock.c b/source3/locking/brlock.c +index fcbce52f9c1..0e58339b108 100644 +--- a/source3/locking/brlock.c ++++ b/source3/locking/brlock.c +@@ -2030,3 +2030,8 @@ NTSTATUS share_mode_do_locked_brl(files_struct *fsp, + TALLOC_FREE(frame); + return NT_STATUS_OK; + } ++ ++void brl_set_modified(struct byte_range_lock *br_lck, bool modified) ++{ ++ br_lck->modified = modified; ++} +diff --git a/source3/locking/proto.h b/source3/locking/proto.h +index c9d769ba53f..c74539c8161 100644 +--- a/source3/locking/proto.h ++++ b/source3/locking/proto.h +@@ -99,6 +99,7 @@ struct byte_range_lock *brl_get_locks(TALLOC_CTX *mem_ctx, + files_struct *fsp); + struct byte_range_lock *brl_get_locks_readonly(files_struct *fsp); + bool brl_cleanup_disconnected(struct file_id fid, uint64_t open_persistent_id); ++void brl_set_modified(struct byte_range_lock *br_lck, bool modified); + + /* The following definitions come from locking/locking.c */ + +-- +2.49.0 + + +From 4888165536438d742e46be57087dbc2b29bd190c Mon Sep 17 00:00:00 2001 +From: Ralph Boehme +Date: Wed, 29 Jan 2025 06:13:44 +0100 +Subject: [PATCH 16/24] smbd: use share_mode_do_locked_brl() + +Fix a deadlock that can happen if two clients happen to open and byte-range-lock +two different files whos record in locking.tdb and brlock.tdb happen to sit on +the same hashchain. + +The deadlock was introduced by commit +680c7907325b433856ac1dd916ab63e671fbe4ab. Before, we used share_mode_do_locked() +in do_lock() which meant we acquired a chainlock on locking.tdb before getting a +chainlock on brlock.tdb via brl_get_locks_for_locking(), so the TDB chainlock +order invariant was always uphold. + +The following race between specific client requests lead to the deadlock. + +Client A) issues a byte-range-lock request on a file: + +A1) glock locking.tdb (via _share_mode_do_locked_vfs_allowed()) +A2) chainlock brlock.tdb (via brl_lock()) +A3) attempt to chainlock locking.tdb (via share_mode_g_lock_dump()) +[1] + +Client B) opens a different (!) file: + +B1) glock and chainlock locking.tdb (via _share_mode_entry_prepare_lock()) +B2) attempt to chainlock brlock.tdb (via file_has_brlocks()) +[2] + +The glock from A1 is per record and hence doesn't synchronize with the glock +from B1 as it is for a different file and hence a different record, subsequently +A2 and A3 violate the lock order constraint + +To avoid the chainlock lock order violation in the second client we modify the +br-lock code to not take the brlock.tdb chainlock from step A2 via +br_get_locks() for the whole time we process the request. Instead we just fetch +the br-locks via br_get_locks_readonly(), so when running into +contend_level2_oplocks_begin_default() to check for leases and looking into +locking.tdb we don't hold a brlock.tdb chainlock. + +Or im simpler terms, we only ever take at most one low-level TDB chainlock at a +time: + +Byte-range-lock code calls share_mode_do_locked_brl(..., cb_fn, ...): +1) chainlock locking.tdb +2) glock locking.tdb (via share_mode_do_locked_vfs_allowed()) +3) chainunlock locking.tdb +4) share_mode_do_locked_brl_fn() -> brl_get_locks_readonly_parse(): + a) chainlock brlock.tdb + b) parse record and store in-memory copy + c) chainunlock brlock.tdb +5) run cb_fn() +6) chainlock brlock.tdb: + a) br_lck->record = dbwrap_fetch_locked(brlock_db, ...) + b) store modifed br_lck from 5) via byte_range_lock_flush() +7) chainunlock brlock.tdb +8) chainlock locking.tdb +9) gunlock locking.tdb +10) chainunlock locking.tdb + +All access to brlock.tdb is synchronized correctly via glocks on the locking.tdb +record of the file (step 3)), so operations still appear atomic to clients. + +As a result of using share_mode_do_locked_brl(), the functions do_[un]lock() -> +brl_[un]lock() now loop over the same br_lck object in memory, avoiding +repeatedly fetching and storing the locks per loop. + +[1] +Full SBT: + + #0 0x00007fffa0cecbb0 in __pthread_mutex_lock_full () from /lib64/glibc-hwcaps/power9/libpthread-2.28.so + #1 0x00007fffa0a73cf8 in chain_mutex_lock (m=, m@entry=0x7fff9ae071b0, waitflag=, waitflag@entry=true) at ../../lib/tdb/common/mutex.c:182 + #2 0x00007fffa0a7432c in tdb_mutex_lock (tdb=0x1543ba120, rw=, off=, len=, waitflag=, pret=0x7fffd7df3858) at ../../lib/tdb/common/mutex.c:234 + #3 0x00007fffa0a6812c in fcntl_lock (waitflag=, len=1, off=376608, rw=0, tdb=0x1543ba120) at ../../lib/tdb/common/lock.c:200 + #4 tdb_brlock (tdb=0x1543ba120, rw_type=, offset=, len=1, flags=) at ../../lib/tdb/common/lock.c:200 + #5 0x00007fffa0a68af8 in tdb_nest_lock (flags=, ltype=0, offset=, tdb=0x1543ba120) at ../../lib/tdb/common/lock.c:390 + #6 tdb_nest_lock (tdb=0x1543ba120, offset=, ltype=, flags=) at ../../lib/tdb/common/lock.c:336 + #7 0x00007fffa0a69088 in tdb_lock_list (tdb=0x1543ba120, list=, ltype=, waitflag=) at ../../lib/tdb/common/lock.c:482 + #8 0x00007fffa0a69198 in tdb_lock (tdb=0x1543ba120, list=, ltype=) at ../../lib/tdb/common/lock.c:500 + #9 0x00007fffa0a64b50 in tdb_find_lock_hash (tdb=, tdb@entry=0x1543ba120, key=..., hash=, locktype=, locktype@entry=0, rec=, rec@entry=0x7fffd7df3ab0) at ../../lib/tdb/common/tdb.c:165 + #10 0x00007fffa0a64ed0 in tdb_parse_record (tdb=0x1543ba120, key=..., parser=0x7fffa0e74470 , private_data=0x7fffd7df3b18) at ../../lib/tdb/common/tdb.c:329 + #11 0x00007fffa0e74cbc in db_ctdb_ltdb_parse (db=, private_data=0x7fffd7df3b70, parser=0x7fffa0e76470 , key=...) at ../../source3/lib/dbwrap/dbwrap_ctdb.c:170 + #12 db_ctdb_try_parse_local_record (ctx=ctx@entry=0x1543d4580, key=..., state=state@entry=0x7fffd7df3b70) at ../../source3/lib/dbwrap/dbwrap_ctdb.c:1385 + #13 0x00007fffa0e76024 in db_ctdb_parse_record (db=, key=..., parser=0x7fffa1313910 , private_data=0x7fffd7df3c08) at ../../source3/lib/dbwrap/dbwrap_ctdb.c:1425 + #14 0x00007fffa0884760 in dbwrap_parse_record (db=, key=..., parser=, private_data=) at ../../lib/dbwrap/dbwrap.c:454 + #15 0x00007fffa1313ab4 in dbwrap_watched_parse_record (db=0x1543a7160, key=..., parser=0x7fffa13187d0 , private_data=0x7fffd7df3ce8) at ../../source3/lib/dbwrap/dbwrap_watch.c:783 + #16 0x00007fffa0884760 in dbwrap_parse_record (db=, key=..., parser=, private_data=) at ../../lib/dbwrap/dbwrap.c:454 + #17 0x00007fffa131c004 in g_lock_dump (ctx=, key=..., fn=0x7fffa14f3d70 , private_data=0x7fffd7df3dd8) at ../../source3/lib/g_lock.c:1653 + #18 0x00007fffa14f434c in share_mode_g_lock_dump (key=..., fn=0x7fffa14f3d70 , private_data=0x7fffd7df3dd8) at ../../source3/locking/share_mode_lock.c:96 + #19 0x00007fffa14f8d44 in fsp_update_share_mode_flags (fsp=0x15433c550) at ../../source3/locking/share_mode_lock.c:1181 + #20 file_has_read_lease (fsp=0x15433c550) at ../../source3/locking/share_mode_lock.c:1207 + #21 0x00007fffa15ccc98 in contend_level2_oplocks_begin_default (type=, fsp=0x15433c550) at ../../source3/smbd/smb2_oplock.c:1282 + #22 smbd_contend_level2_oplocks_begin (fsp=0x15433c550, type=) at ../../source3/smbd/smb2_oplock.c:1338 + #23 0x00007fffa0dd0b54 in contend_level2_oplocks_begin (fsp=, type=) at ../../source3/lib/smbd_shim.c:72 + #24 0x00007fffa14ecfd0 in brl_lock_windows_default (br_lck=0x154421330, plock=0x7fffd7df4250) at ../../source3/locking/brlock.c:457 + #25 0x00007fffa150b70c in vfswrap_brl_lock_windows (handle=, br_lck=, plock=) at ../../source3/modules/vfs_default.c:3424 + #26 0x00007fffa1561910 in smb_vfs_call_brl_lock_windows (handle=, br_lck=, plock=) at ../../source3/smbd/vfs.c:2686 + #27 0x00007fff9c0a7350 in smb_time_audit_brl_lock_windows (handle=, br_lck=0x154421330, plock=0x7fffd7df4250) at ../../source3/modules/vfs_time_audit.c:1740 + #28 0x00007fffa1561910 in smb_vfs_call_brl_lock_windows (handle=, br_lck=, plock=) at ../../source3/smbd/vfs.c:2686 + #29 0x00007fffa14ed410 in brl_lock (br_lck=0x154421330, smblctx=3102281601, pid=..., start=0, size=18446744073709551615, lock_type=, lock_flav=WINDOWS_LOCK, blocker_pid=0x7fffd7df4540, psmblctx=0x7fffd7df4558) at ../../source3/locking/brlock.c:1004 + #30 0x00007fffa14e7b18 in do_lock_fn (lck=, private_data=0x7fffd7df4508) at ../../source3/locking/locking.c:271 + #31 0x00007fffa14fcd94 in _share_mode_do_locked_vfs_allowed (id=..., fn=0x7fffa14e7a60 , private_data=0x7fffd7df4508, location=) at ../../source3/locking/share_mode_lock.c:2927 + #32 0x00007fffa14e918c in do_lock (fsp=0x15433c550, req_mem_ctx=, req_guid=, smblctx=, count=18446744073709551615, offset=0, lock_type=, lock_flav=, pblocker_pid=0x7fffd7df46f0, + psmblctx=0x7fffd7df46d8) at ../../source3/locking/locking.c:335 + #33 0x00007fffa155381c in smbd_do_locks_try (fsp=0x15433c550, num_locks=, locks=0x1543bc310, blocker_idx=0x7fffd7df46d6, blocking_pid=0x7fffd7df46f0, blocking_smblctx=0x7fffd7df46d8) at ../../source3/smbd/blocking.c:46 + #34 0x00007fffa159dc90 in smbd_smb2_lock_try (req=req@entry=0x1543bc080) at ../../source3/smbd/smb2_lock.c:590 + #35 0x00007fffa159ee8c in smbd_smb2_lock_send (in_locks=, in_lock_count=1, in_lock_sequence=, fsp=0x15433c550, smb2req=0x1543532e0, ev=0x154328120, mem_ctx=0x1543532e0) at ../../source3/smbd/smb2_lock.c:488 + #36 smbd_smb2_request_process_lock (req=0x1543532e0) at ../../source3/smbd/smb2_lock.c:150 + #37 0x00007fffa158a368 in smbd_smb2_request_dispatch (req=0x1543532e0) at ../../source3/smbd/smb2_server.c:3515 + #38 0x00007fffa158c540 in smbd_smb2_io_handler (fde_flags=, xconn=0x154313f30) at ../../source3/smbd/smb2_server.c:5112 + #39 smbd_smb2_connection_handler (ev=, fde=, flags=, private_data=) at ../../source3/smbd/smb2_server.c:5150 + #40 0x00007fffa1198b2c in tevent_common_invoke_fd_handler (fde=0x1543670f0, flags=, removed=0x0) at ../../lib/tevent/tevent_fd.c:158 + #41 0x00007fffa11a2b9c in epoll_event_loop (tvalp=0x7fffd7df4b28, epoll_ev=0x1543b4e80) at ../../lib/tevent/tevent_epoll.c:730 + #42 epoll_event_loop_once (ev=, location=) at ../../lib/tevent/tevent_epoll.c:946 + #43 0x00007fffa11a0090 in std_event_loop_once (ev=0x154328120, location=0x7fffa1668db8 "../../source3/smbd/smb2_process.c:2158") at ../../lib/tevent/tevent_standard.c:110 + #44 0x00007fffa119744c in _tevent_loop_once (ev=0x154328120, location=0x7fffa1668db8 "../../source3/smbd/smb2_process.c:2158") at ../../lib/tevent/tevent.c:823 + #45 0x00007fffa1197884 in tevent_common_loop_wait (ev=, location=) at ../../lib/tevent/tevent.c:950 + #46 0x00007fffa119ffc0 in std_event_loop_wait (ev=0x154328120, location=0x7fffa1668db8 "../../source3/smbd/smb2_process.c:2158") at ../../lib/tevent/tevent_standard.c:141 + #47 0x00007fffa1197978 in _tevent_loop_wait (ev=, location=) at ../../lib/tevent/tevent.c:971 + #48 0x00007fffa15737fc in smbd_process (ev_ctx=0x154328120, msg_ctx=, sock_fd=, interactive=) at ../../source3/smbd/smb2_process.c:2158 + #49 0x000000011db5c554 in smbd_accept_connection (ev=0x154328120, fde=, flags=, private_data=) at ../../source3/smbd/server.c:1150 + #50 0x00007fffa1198b2c in tevent_common_invoke_fd_handler (fde=0x1543ac2d0, flags=, removed=0x0) at ../../lib/tevent/tevent_fd.c:158 + #51 0x00007fffa11a2b9c in epoll_event_loop (tvalp=0x7fffd7df4f98, epoll_ev=0x154328350) at ../../lib/tevent/tevent_epoll.c:730 + #52 epoll_event_loop_once (ev=, location=) at ../../lib/tevent/tevent_epoll.c:946 + #53 0x00007fffa11a0090 in std_event_loop_once (ev=0x154328120, location=0x11db60b50 "../../source3/smbd/server.c:1499") at ../../lib/tevent/tevent_standard.c:110 + #54 0x00007fffa119744c in _tevent_loop_once (ev=0x154328120, location=0x11db60b50 "../../source3/smbd/server.c:1499") at ../../lib/tevent/tevent.c:823 + #55 0x00007fffa1197884 in tevent_common_loop_wait (ev=, location=) at ../../lib/tevent/tevent.c:950 + #56 0x00007fffa119ffc0 in std_event_loop_wait (ev=0x154328120, location=0x11db60b50 "../../source3/smbd/server.c:1499") at ../../lib/tevent/tevent_standard.c:141 + #57 0x00007fffa1197978 in _tevent_loop_wait (ev=, location=) at ../../lib/tevent/tevent.c:971 + #58 0x000000011db58c54 in smbd_parent_loop (parent=, ev_ctx=0x154328120) at ../../source3/smbd/server.c:1499 + #59 main (argc=, argv=) at ../../source3/smbd/server.c:2258 + +[2] +Full SBT: + + #0 0x00007fffa0cecbb0 in __pthread_mutex_lock_full () from /lib64/glibc-hwcaps/power9/libpthread-2.28.so + #1 0x00007fffa0a73cf8 in chain_mutex_lock (m=, m@entry=0x7fff9b3a71b0, waitflag=, waitflag@entry=true) at ../../lib/tdb/common/mutex.c:182 + #2 0x00007fffa0a7432c in tdb_mutex_lock (tdb=0x1543c6900, rw=, off=, len=, waitflag=, pret=0x7fffd7df2e28) at ../../lib/tdb/common/mutex.c:234 + #3 0x00007fffa0a6812c in fcntl_lock (waitflag=, len=1, off=376608, rw=0, tdb=0x1543c6900) at ../../lib/tdb/common/lock.c:200 + #4 tdb_brlock (tdb=0x1543c6900, rw_type=, offset=, len=1, flags=) at ../../lib/tdb/common/lock.c:200 + #5 0x00007fffa0a68af8 in tdb_nest_lock (flags=, ltype=0, offset=, tdb=0x1543c6900) at ../../lib/tdb/common/lock.c:390 + #6 tdb_nest_lock (tdb=0x1543c6900, offset=, ltype=, flags=) at ../../lib/tdb/common/lock.c:336 + #7 0x00007fffa0a69088 in tdb_lock_list (tdb=0x1543c6900, list=, ltype=, waitflag=) at ../../lib/tdb/common/lock.c:482 + #8 0x00007fffa0a69198 in tdb_lock (tdb=0x1543c6900, list=, ltype=) at ../../lib/tdb/common/lock.c:500 + #9 0x00007fffa0a64b50 in tdb_find_lock_hash (tdb=, tdb@entry=0x1543c6900, key=..., hash=, locktype=, locktype@entry=0, rec=, rec@entry=0x7fffd7df3080) at ../../lib/tdb/common/tdb.c:165 + #10 0x00007fffa0a64ed0 in tdb_parse_record (tdb=0x1543c6900, key=..., parser=0x7fffa0e74470 , private_data=0x7fffd7df30e8) at ../../lib/tdb/common/tdb.c:329 + #11 0x00007fffa0e74cbc in db_ctdb_ltdb_parse (db=, private_data=0x7fffd7df3140, parser=0x7fffa0e76470 , key=...) at ../../source3/lib/dbwrap/dbwrap_ctdb.c:170 + #12 db_ctdb_try_parse_local_record (ctx=ctx@entry=0x154328fc0, key=..., state=state@entry=0x7fffd7df3140) at ../../source3/lib/dbwrap/dbwrap_ctdb.c:1385 + #13 0x00007fffa0e76024 in db_ctdb_parse_record (db=, key=..., parser=0x7fffa14ec820 , private_data=0x7fffd7df3218) at ../../source3/lib/dbwrap/dbwrap_ctdb.c:1425 + #14 0x00007fffa0884760 in dbwrap_parse_record (db=, key=..., parser=, private_data=) at ../../lib/dbwrap/dbwrap.c:454 + #15 0x00007fffa14ef5bc in brl_get_locks_readonly (fsp=0x1543d01e0) at ../../source3/locking/brlock.c:1884 + #16 0x00007fffa1546968 in file_has_brlocks (fsp=0x1543d01e0) at ../../source3/smbd/open.c:2232 + #17 delay_for_oplock (pgranted=, poplock_type=, first_open_attempt=, create_disposition=1, have_sharing_violation=false, lck=0x7fffd7df3ce8, lease=0x0, oplock_request=0, fsp=0x1543d01e0) at ../../source3/smbd/open.c:2749 + #18 handle_share_mode_lease (pgranted=, poplock_type=, first_open_attempt=, lease=0x0, oplock_request=0, share_access=7, access_mask=131201, create_disposition=1, lck=0x7fffd7df3ce8, fsp=0x1543d01e0) at ../../source3/smbd/open.c:2865 + #19 check_and_store_share_mode (first_open_attempt=, lease=0x0, oplock_request=0, share_access=7, access_mask=131201, create_disposition=1, lck=0x7fffd7df3ce8, req=0x154414800, fsp=0x1543d01e0) at ../../source3/smbd/open.c:3333 + #20 open_ntcreate_lock_add_entry (lck=0x7fffd7df3ce8, keep_locked=0x7fffd7df3ad0, private_data=0x7fffd7df3cc8) at ../../source3/smbd/open.c:3688 + #21 0x00007fffa14f6248 in share_mode_entry_prepare_lock_fn (glck=0x7fffd7df35b8, cb_private=0x7fffd7df3a88) at ../../source3/locking/share_mode_lock.c:2978 + #22 0x00007fffa1317680 in g_lock_lock_cb_run_and_store (cb_state=cb_state@entry=0x7fffd7df35b8) at ../../source3/lib/g_lock.c:597 + #23 0x00007fffa1319df8 in g_lock_lock_simple_fn (rec=0x7fffd7df3798, value=..., private_data=0x7fffd7df39a0) at ../../source3/lib/g_lock.c:1212 + #24 0x00007fffa13160e0 in dbwrap_watched_do_locked_fn (backend_rec=, backend_value=..., private_data=0x7fffd7df3768) at ../../source3/lib/dbwrap/dbwrap_watch.c:458 + #25 0x00007fffa0884e48 in dbwrap_do_locked (db=, key=..., fn=0x7fffa1316080 , private_data=0x7fffd7df3768) at ../../lib/dbwrap/dbwrap.c:602 + #26 0x00007fffa1315274 in dbwrap_watched_do_locked (db=0x1543a7160, key=..., fn=0x7fffa1319ca0 , private_data=0x7fffd7df39a0) at ../../source3/lib/dbwrap/dbwrap_watch.c:480 + #27 0x00007fffa0884d60 in dbwrap_do_locked (db=, key=..., fn=, private_data=) at ../../lib/dbwrap/dbwrap.c:582 + #28 0x00007fffa131b458 in g_lock_lock (ctx=0x1543cc630, key=..., type=, timeout=..., cb_fn=0x7fffa14f6190 , cb_private=0x7fffd7df3a88) at ../../source3/lib/g_lock.c:1267 + #29 0x00007fffa14fd060 in _share_mode_entry_prepare_lock (prepare_state=0x7fffd7df3cc8, id=..., servicepath=, smb_fname=, old_write_time=, fn=, private_data=0x7fffd7df3cc8, location=0x7fffa165b880 "../../source3/smbd/open.c:4292") at ../../source3/locking/share_mode_lock.c:3033 + #30 0x00007fffa15491e0 in open_file_ntcreate (conn=conn@entry=0x154382050, req=req@entry=0x154414800, access_mask=, access_mask@entry=131201, share_access=share_access@entry=7, create_disposition=create_disposition@entry=1, create_options=create_options@entry=0, new_dos_attributes=, new_dos_attributes@entry=128, oplock_request=oplock_request@entry=0, lease=, lease@entry=0x0, private_flags=, private_flags@entry=0, parent_dir_fname=, smb_fname_atname=, pinfo=, pinfo@entry=0x7fffd7df3f1c, fsp=, fsp@entry=0x1543d01e0) at ../../source3/smbd/open.c:4286 + #31 0x00007fffa154b94c in create_file_unixpath (conn=conn@entry=0x154382050, req=req@entry=0x154414800, dirfsp=dirfsp@entry=0x15439a7f0, smb_fname=smb_fname@entry=0x154416300, access_mask=access_mask@entry=131201, share_access=share_access@entry=7, create_disposition=create_disposition@entry=1, create_options=create_options@entry=0, file_attributes=file_attributes@entry=128, oplock_request=, oplock_request@entry=0, lease=, lease@entry=0x0, allocation_size=allocation_size@entry=0, private_flags=private_flags@entry=0, sd=sd@entry=0x0, ea_list=ea_list@entry=0x0, result=result@entry=0x7fffd7df4168, pinfo=pinfo@entry=0x7fffd7df4160) at ../../source3/smbd/open.c:6290 + #32 0x00007fffa154dfac in create_file_default (conn=0x154382050, req=0x154414800, dirfsp=0x15439a7f0, smb_fname=0x154416300, access_mask=, share_access=, create_disposition=, create_options=, file_attributes=128, oplock_request=0, lease=0x0, allocation_size=0, private_flags=0, sd=0x0, ea_list=0x0, result=0x1544144e8, pinfo=0x1544144fc, in_context_blobs=0x7fffd7df4798, out_context_blobs=0x154414710) at ../../source3/smbd/open.c:6609 + #33 0x00007fffa150972c in vfswrap_create_file (handle=, req=, dirfsp=, smb_fname=, access_mask=, share_access=, create_disposition=, create_options=, file_attributes=128, oplock_request=0, lease=0x0, allocation_size=0, private_flags=0, sd=0x0, ea_list=0x0, result=0x1544144e8, pinfo=0x1544144fc, in_context_blobs=0x7fffd7df4798, out_context_blobs=0x154414710) at ../../source3/modules/vfs_default.c:776 + #34 0x00007fffa1559cbc in smb_vfs_call_create_file (handle=, req=, dirfsp=, smb_fname=, access_mask=, share_access=, create_disposition=, create_options=, file_attributes=128, oplock_request=0, lease=0x0, allocation_size=0, private_flags=0, sd=0x0, ea_list=0x0, result=0x1544144e8, pinfo=0x1544144fc, in_context_blobs=0x7fffd7df4798, out_context_blobs=0x154414710) at ../../source3/smbd/vfs.c:1560 + #35 0x00007fff9c0a9ec4 in smb_time_audit_create_file (handle=0x154426820, req=0x154414800, dirfsp=0x15439a7f0, fname=0x154416300, access_mask=, share_access=, create_disposition=, create_options=, file_attributes=128, oplock_request=0, lease=0x0, allocation_size=0, private_flags=0, sd=0x0, ea_list=0x0, result_fsp=0x1544144e8, pinfo=0x1544144fc, in_context_blobs=0x7fffd7df4798, out_context_blobs=0x154414710) at ../../source3/modules/vfs_time_audit.c:634 + #36 0x00007fffa1559cbc in smb_vfs_call_create_file (handle=, req=, dirfsp=, smb_fname=, access_mask=, share_access=, create_disposition=, create_options=, file_attributes=128, oplock_request=0, lease=0x0, allocation_size=0, private_flags=0, sd=0x0, ea_list=0x0, result=0x1544144e8, pinfo=0x1544144fc, in_context_blobs=0x7fffd7df4798, out_context_blobs=0x154414710) at ../../source3/smbd/vfs.c:1560 + #37 0x00007fffa1597aa8 in smbd_smb2_create_send (in_context_blobs=..., in_name=0x154413ca0, in_create_options=, in_create_disposition=, in_share_access=, in_file_attributes=, in_desired_access=, in_impersonation_level=, in_oplock_level=, smb2req=0x154413770, ev=0x154328120, mem_ctx=0x154413770) at ../../source3/smbd/smb2_create.c:1115 + #38 smbd_smb2_request_process_create (smb2req=0x154413770) at ../../source3/smbd/smb2_create.c:291 + #39 0x00007fffa158a628 in smbd_smb2_request_dispatch (req=0x154413770) at ../../source3/smbd/smb2_server.c:3485 + #40 0x00007fffa158c540 in smbd_smb2_io_handler (fde_flags=, xconn=0x154313f30) at ../../source3/smbd/smb2_server.c:5112 + #41 smbd_smb2_connection_handler (ev=, fde=, flags=, private_data=) at ../../source3/smbd/smb2_server.c:5150 + #42 0x00007fffa1198b2c in tevent_common_invoke_fd_handler (fde=0x15435add0, flags=, removed=0x0) at ../../lib/tevent/tevent_fd.c:158 + #43 0x00007fffa11a2b9c in epoll_event_loop (tvalp=0x7fffd7df4b28, epoll_ev=0x1543b4e80) at ../../lib/tevent/tevent_epoll.c:730 + #44 epoll_event_loop_once (ev=, location=) at ../../lib/tevent/tevent_epoll.c:946 + #45 0x00007fffa11a0090 in std_event_loop_once (ev=0x154328120, location=0x7fffa1668db8 "../../source3/smbd/smb2_process.c:2158") at ../../lib/tevent/tevent_standard.c:110 + #46 0x00007fffa119744c in _tevent_loop_once (ev=0x154328120, location=0x7fffa1668db8 "../../source3/smbd/smb2_process.c:2158") at ../../lib/tevent/tevent.c:823 + #47 0x00007fffa1197884 in tevent_common_loop_wait (ev=, location=) at ../../lib/tevent/tevent.c:950 + #48 0x00007fffa119ffc0 in std_event_loop_wait (ev=0x154328120, location=0x7fffa1668db8 "../../source3/smbd/smb2_process.c:2158") at ../../lib/tevent/tevent_standard.c:141 + #49 0x00007fffa1197978 in _tevent_loop_wait (ev=, location=) at ../../lib/tevent/tevent.c:971 + #50 0x00007fffa15737fc in smbd_process (ev_ctx=0x154328120, msg_ctx=, sock_fd=, interactive=) at ../../source3/smbd/smb2_process.c:2158 + #51 0x000000011db5c554 in smbd_accept_connection (ev=0x154328120, fde=, flags=, private_data=) at ../../source3/smbd/server.c:1150 + #52 0x00007fffa1198b2c in tevent_common_invoke_fd_handler (fde=0x1543ac2d0, flags=, removed=0x0) at ../../lib/tevent/tevent_fd.c:158 + #53 0x00007fffa11a2b9c in epoll_event_loop (tvalp=0x7fffd7df4f98, epoll_ev=0x154328350) at ../../lib/tevent/tevent_epoll.c:730 + #54 epoll_event_loop_once (ev=, location=) at ../../lib/tevent/tevent_epoll.c:946 + #55 0x00007fffa11a0090 in std_event_loop_once (ev=0x154328120, location=0x11db60b50 "../../source3/smbd/server.c:1499") at ../../lib/tevent/tevent_standard.c:110 + #56 0x00007fffa119744c in _tevent_loop_once (ev=0x154328120, location=0x11db60b50 "../../source3/smbd/server.c:1499") at ../../lib/tevent/tevent.c:823 + #57 0x00007fffa1197884 in tevent_common_loop_wait (ev=, location=) at ../../lib/tevent/tevent.c:950 + #58 0x00007fffa119ffc0 in std_event_loop_wait (ev=0x154328120, location=0x11db60b50 "../../source3/smbd/server.c:1499") at ../../lib/tevent/tevent_standard.c:141 + #59 0x00007fffa1197978 in _tevent_loop_wait (ev=, location=) at ../../lib/tevent/tevent.c:971 + #60 0x000000011db58c54 in smbd_parent_loop (parent=, ev_ctx=0x154328120) at ../../source3/smbd/server.c:1499 + #61 main (argc=, argv=) at ../../source3/smbd/server.c:2258 + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=15767 + +Pair-Programmed-With: Stefan Metzmacher +Signed-off-by: Ralph Boehme +Signed-off-by: Stefan Metzmacher +(backported from commit 2eef298ff4c5baf15c7d29c65fb021dbed5b0a93) +[slow@samba.org: changed argument of share_mode_watch_send()] +[slow@samba.org: small context change in vfs_fruit] +--- + source3/locking/locking.c | 109 +++++-------------- + source3/locking/proto.h | 4 +- + source3/modules/vfs_fruit.c | 91 +++++++++++----- + source3/smbd/blocking.c | 202 +++++++++++++++++++----------------- + source3/smbd/proto.h | 19 ++-- + source3/smbd/smb2_lock.c | 77 +++++++++----- + source3/smbd/smb2_reply.c | 53 ++++++---- + 7 files changed, 293 insertions(+), 262 deletions(-) + +diff --git a/source3/locking/locking.c b/source3/locking/locking.c +index ea692711627..ffb89378cb5 100644 +--- a/source3/locking/locking.c ++++ b/source3/locking/locking.c +@@ -241,52 +241,7 @@ static void decrement_current_lock_count(files_struct *fsp, + Utility function called by locking requests. + ****************************************************************************/ + +-struct do_lock_state { +- struct files_struct *fsp; +- TALLOC_CTX *req_mem_ctx; +- const struct GUID *req_guid; +- uint64_t smblctx; +- uint64_t count; +- uint64_t offset; +- enum brl_type lock_type; +- enum brl_flavour lock_flav; +- +- struct server_id blocker_pid; +- uint64_t blocker_smblctx; +- NTSTATUS status; +-}; +- +-static void do_lock_fn( +- struct share_mode_lock *lck, +- void *private_data) +-{ +- struct do_lock_state *state = private_data; +- struct byte_range_lock *br_lck = NULL; +- +- br_lck = brl_get_locks_for_locking(talloc_tos(), +- state->fsp, +- state->req_mem_ctx, +- state->req_guid); +- if (br_lck == NULL) { +- state->status = NT_STATUS_NO_MEMORY; +- return; +- } +- +- state->status = brl_lock( +- br_lck, +- state->smblctx, +- messaging_server_id(state->fsp->conn->sconn->msg_ctx), +- state->offset, +- state->count, +- state->lock_type, +- state->lock_flav, +- &state->blocker_pid, +- &state->blocker_smblctx); +- +- TALLOC_FREE(br_lck); +-} +- +-NTSTATUS do_lock(files_struct *fsp, ++NTSTATUS do_lock(struct byte_range_lock *br_lck, + TALLOC_CTX *req_mem_ctx, + const struct GUID *req_guid, + uint64_t smblctx, +@@ -297,22 +252,13 @@ NTSTATUS do_lock(files_struct *fsp, + struct server_id *pblocker_pid, + uint64_t *psmblctx) + { +- struct do_lock_state state = { +- .fsp = fsp, +- .req_mem_ctx = req_mem_ctx, +- .req_guid = req_guid, +- .smblctx = smblctx, +- .count = count, +- .offset = offset, +- .lock_type = lock_type, +- .lock_flav = lock_flav, +- }; ++ files_struct *fsp = brl_fsp(br_lck); ++ struct server_id blocker_pid; ++ uint64_t blocker_smblctx; + NTSTATUS status; + +- /* silently return ok on print files as we don't do locking there */ +- if (fsp->print_file) { +- return NT_STATUS_OK; +- } ++ SMB_ASSERT(req_mem_ctx != NULL); ++ SMB_ASSERT(req_guid != NULL); + + if (!fsp->fsp_flags.can_lock) { + if (fsp->fsp_flags.is_directory) { +@@ -336,25 +282,27 @@ NTSTATUS do_lock(files_struct *fsp, + fsp_fnum_dbg(fsp), + fsp_str_dbg(fsp)); + +- status = share_mode_do_locked_vfs_allowed(fsp->file_id, +- do_lock_fn, +- &state); +- if (!NT_STATUS_IS_OK(status)) { +- DBG_DEBUG("share_mode_do_locked returned %s\n", +- nt_errstr(status)); +- return status; +- } +- if (!NT_STATUS_IS_OK(state.status)) { +- DBG_DEBUG("do_lock_fn returned %s\n", +- nt_errstr(state.status)); ++ brl_req_set(br_lck, req_mem_ctx, req_guid); ++ status = brl_lock(br_lck, ++ smblctx, ++ messaging_server_id(fsp->conn->sconn->msg_ctx), ++ offset, ++ count, ++ lock_type, ++ lock_flav, ++ &blocker_pid, ++ &blocker_smblctx); ++ brl_req_set(br_lck, NULL, NULL); ++ if (!NT_STATUS_IS_OK(status)) { ++ DBG_DEBUG("brl_lock failed: %s\n", nt_errstr(status)); + if (psmblctx != NULL) { +- *psmblctx = state.blocker_smblctx; ++ *psmblctx = blocker_smblctx; + } + if (pblocker_pid != NULL) { +- *pblocker_pid = state.blocker_pid; ++ *pblocker_pid = blocker_pid; + } +- return state.status; +- } ++ return status; ++ } + + increment_current_lock_count(fsp, lock_flav); + +@@ -365,14 +313,14 @@ NTSTATUS do_lock(files_struct *fsp, + Utility function called by unlocking requests. + ****************************************************************************/ + +-NTSTATUS do_unlock(files_struct *fsp, ++NTSTATUS do_unlock(struct byte_range_lock *br_lck, + uint64_t smblctx, + uint64_t count, + uint64_t offset, + enum brl_flavour lock_flav) + { ++ files_struct *fsp = brl_fsp(br_lck); + bool ok = False; +- struct byte_range_lock *br_lck = NULL; + + if (!fsp->fsp_flags.can_lock) { + return fsp->fsp_flags.is_directory ? +@@ -391,11 +339,6 @@ NTSTATUS do_unlock(files_struct *fsp, + fsp_fnum_dbg(fsp), + fsp_str_dbg(fsp)); + +- br_lck = brl_get_locks(talloc_tos(), fsp); +- if (!br_lck) { +- return NT_STATUS_NO_MEMORY; +- } +- + ok = brl_unlock(br_lck, + smblctx, + messaging_server_id(fsp->conn->sconn->msg_ctx), +@@ -403,8 +346,6 @@ NTSTATUS do_unlock(files_struct *fsp, + count, + lock_flav); + +- TALLOC_FREE(br_lck); +- + if (!ok) { + DEBUG(10,("do_unlock: returning ERRlock.\n" )); + return NT_STATUS_RANGE_NOT_LOCKED; +diff --git a/source3/locking/proto.h b/source3/locking/proto.h +index c74539c8161..e332abf34ec 100644 +--- a/source3/locking/proto.h ++++ b/source3/locking/proto.h +@@ -120,7 +120,7 @@ NTSTATUS query_lock(files_struct *fsp, + uint64_t *poffset, + enum brl_type *plock_type, + enum brl_flavour lock_flav); +-NTSTATUS do_lock(files_struct *fsp, ++NTSTATUS do_lock(struct byte_range_lock *br_lck, + TALLOC_CTX *req_mem_ctx, + const struct GUID *req_guid, + uint64_t smblctx, +@@ -130,7 +130,7 @@ NTSTATUS do_lock(files_struct *fsp, + enum brl_flavour lock_flav, + struct server_id *pblocker_pid, + uint64_t *psmblctx); +-NTSTATUS do_unlock(files_struct *fsp, ++NTSTATUS do_unlock(struct byte_range_lock *br_lck, + uint64_t smblctx, + uint64_t count, + uint64_t offset, +diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c +index b63fff7f95d..e85c26c9dc0 100644 +--- a/source3/modules/vfs_fruit.c ++++ b/source3/modules/vfs_fruit.c +@@ -629,11 +629,21 @@ static bool test_netatalk_lock(files_struct *fsp, off_t in_offset) + return false; + } + +-static NTSTATUS fruit_check_access(vfs_handle_struct *handle, +- files_struct *fsp, +- uint32_t access_mask, +- uint32_t share_mode) ++struct check_access_state { ++ NTSTATUS status; ++ files_struct *fsp; ++ uint32_t access_mask; ++ uint32_t share_mode; ++}; ++ ++static void fruit_check_access(struct share_mode_lock *lck, ++ struct byte_range_lock *br_lck, ++ void *private_data) + { ++ struct check_access_state *state = private_data; ++ files_struct *fsp = state->fsp; ++ uint32_t access_mask = state->access_mask; ++ uint32_t share_mode = state->share_mode; + NTSTATUS status = NT_STATUS_OK; + off_t off; + bool share_for_read = (share_mode & FILE_SHARE_READ); +@@ -647,6 +657,14 @@ static NTSTATUS fruit_check_access(vfs_handle_struct *handle, + /* FIXME: hardcoded data fork, add resource fork */ + enum apple_fork fork_type = APPLE_FORK_DATA; + ++ /* ++ * The caller has checked fsp->fsp_flags.can_lock and lp_locking so ++ * br_lck has to be there! ++ */ ++ SMB_ASSERT(br_lck != NULL); ++ ++ state->status = NT_STATUS_OK; ++ + DBG_DEBUG("fruit_check_access: %s, am: %s/%s, sm: 0x%x\n", + fsp_str_dbg(fsp), + access_mask & FILE_READ_DATA ? "READ" :"-", +@@ -654,7 +672,7 @@ static NTSTATUS fruit_check_access(vfs_handle_struct *handle, + share_mode); + + if (fsp_get_io_fd(fsp) == -1) { +- return NT_STATUS_OK; ++ return; + } + + /* Read NetATalk opens and deny modes on the file. */ +@@ -677,22 +695,26 @@ static NTSTATUS fruit_check_access(vfs_handle_struct *handle, + /* If there are any conflicts - sharing violation. */ + if ((access_mask & FILE_READ_DATA) && + netatalk_already_open_with_deny_read) { +- return NT_STATUS_SHARING_VIOLATION; ++ state->status = NT_STATUS_SHARING_VIOLATION; ++ return; + } + + if (!share_for_read && + netatalk_already_open_for_reading) { +- return NT_STATUS_SHARING_VIOLATION; ++ state->status = NT_STATUS_SHARING_VIOLATION; ++ return; + } + + if ((access_mask & FILE_WRITE_DATA) && + netatalk_already_open_with_deny_write) { +- return NT_STATUS_SHARING_VIOLATION; ++ state->status = NT_STATUS_SHARING_VIOLATION; ++ return; + } + + if (!share_for_write && + netatalk_already_open_for_writing) { +- return NT_STATUS_SHARING_VIOLATION; ++ state->status = NT_STATUS_SHARING_VIOLATION; ++ return; + } + + if (!(access_mask & FILE_READ_DATA)) { +@@ -700,15 +722,16 @@ static NTSTATUS fruit_check_access(vfs_handle_struct *handle, + * Nothing we can do here, we need read access + * to set locks. + */ +- return NT_STATUS_OK; ++ return; + } + + /* Set NetAtalk locks matching our access */ + if (access_mask & FILE_READ_DATA) { + off = access_to_netatalk_brl(fork_type, FILE_READ_DATA); + req_guid.time_hi_and_version = __LINE__; ++ + status = do_lock( +- fsp, ++ br_lck, + talloc_tos(), + &req_guid, + fsp->op->global->open_persistent_id, +@@ -718,17 +741,18 @@ static NTSTATUS fruit_check_access(vfs_handle_struct *handle, + POSIX_LOCK, + NULL, + NULL); +- + if (!NT_STATUS_IS_OK(status)) { +- return status; ++ state->status = status; ++ return; + } + } + + if (!share_for_read) { + off = denymode_to_netatalk_brl(fork_type, DENY_READ); + req_guid.time_hi_and_version = __LINE__; ++ + status = do_lock( +- fsp, ++ br_lck, + talloc_tos(), + &req_guid, + fsp->op->global->open_persistent_id, +@@ -738,17 +762,18 @@ static NTSTATUS fruit_check_access(vfs_handle_struct *handle, + POSIX_LOCK, + NULL, + NULL); +- + if (!NT_STATUS_IS_OK(status)) { +- return status; ++ state->status = status; ++ return; + } + } + + if (access_mask & FILE_WRITE_DATA) { + off = access_to_netatalk_brl(fork_type, FILE_WRITE_DATA); + req_guid.time_hi_and_version = __LINE__; ++ + status = do_lock( +- fsp, ++ br_lck, + talloc_tos(), + &req_guid, + fsp->op->global->open_persistent_id, +@@ -758,17 +783,18 @@ static NTSTATUS fruit_check_access(vfs_handle_struct *handle, + POSIX_LOCK, + NULL, + NULL); +- + if (!NT_STATUS_IS_OK(status)) { +- return status; ++ state->status = status; ++ return; + } + } + + if (!share_for_write) { + off = denymode_to_netatalk_brl(fork_type, DENY_WRITE); + req_guid.time_hi_and_version = __LINE__; ++ + status = do_lock( +- fsp, ++ br_lck, + talloc_tos(), + &req_guid, + fsp->op->global->open_persistent_id, +@@ -778,13 +804,11 @@ static NTSTATUS fruit_check_access(vfs_handle_struct *handle, + POSIX_LOCK, + NULL, + NULL); +- + if (!NT_STATUS_IS_OK(status)) { +- return status; ++ state->status = status; ++ return; + } + } +- +- return NT_STATUS_OK; + } + + static NTSTATUS check_aapl(vfs_handle_struct *handle, +@@ -4376,16 +4400,27 @@ static NTSTATUS fruit_create_file(vfs_handle_struct *handle, + } + + if ((config->locking == FRUIT_LOCKING_NETATALK) && ++ lp_locking(fsp->conn->params) && ++ fsp->fsp_flags.can_lock && + (fsp->op != NULL) && + !fsp->fsp_flags.is_pathref) + { +- status = fruit_check_access( +- handle, *result, +- access_mask, +- share_access); ++ struct check_access_state state = (struct check_access_state) { ++ .fsp = fsp, ++ .access_mask = access_mask, ++ .share_mode = share_access, ++ }; ++ ++ status = share_mode_do_locked_brl(fsp, ++ fruit_check_access, ++ &state); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } ++ if (!NT_STATUS_IS_OK(state.status)) { ++ status = state.status; ++ goto fail; ++ } + } + + return status; +diff --git a/source3/smbd/blocking.c b/source3/smbd/blocking.c +index 8b41288bfbf..c9d9aff98b7 100644 +--- a/source3/smbd/blocking.c ++++ b/source3/smbd/blocking.c +@@ -29,31 +29,27 @@ + #undef DBGC_CLASS + #define DBGC_CLASS DBGC_LOCKING + +-NTSTATUS smbd_do_locks_try( +- struct files_struct *fsp, +- uint16_t num_locks, +- struct smbd_lock_element *locks, +- uint16_t *blocker_idx, +- struct server_id *blocking_pid, +- uint64_t *blocking_smblctx) ++NTSTATUS smbd_do_locks_try(struct byte_range_lock *br_lck, ++ struct smbd_do_locks_state *state) + { +- NTSTATUS status = NT_STATUS_OK; ++ bool unlock_ok; + uint16_t i; ++ NTSTATUS status = NT_STATUS_OK; + +- for (i=0; inum_locks; i++) { ++ struct smbd_lock_element *e = &state->locks[i]; + + status = do_lock( +- fsp, +- locks, /* req_mem_ctx */ ++ br_lck, ++ state->locks, /* req_mem_ctx */ + &e->req_guid, + e->smblctx, + e->count, + e->offset, + e->brltype, + e->lock_flav, +- blocking_pid, +- blocking_smblctx); ++ &state->blocking_pid, ++ &state->blocking_smblctx); + if (!NT_STATUS_IS_OK(status)) { + break; + } +@@ -63,18 +59,35 @@ NTSTATUS smbd_do_locks_try( + return NT_STATUS_OK; + } + +- *blocker_idx = i; ++ state->blocker_idx = i; ++ unlock_ok = true; + + /* + * Undo the locks we successfully got + */ + for (i = i-1; i != UINT16_MAX; i--) { +- struct smbd_lock_element *e = &locks[i]; +- do_unlock(fsp, +- e->smblctx, +- e->count, +- e->offset, +- e->lock_flav); ++ struct smbd_lock_element *e = &state->locks[i]; ++ NTSTATUS ulstatus; ++ ++ ulstatus = do_unlock(br_lck, ++ e->smblctx, ++ e->count, ++ e->offset, ++ e->lock_flav); ++ if (!NT_STATUS_IS_OK(ulstatus)) { ++ DBG_DEBUG("Failed to undo lock flavour %s lock " ++ "type %s start=%"PRIu64" len=%"PRIu64" " ++ "requested for file [%s]\n", ++ lock_flav_name(e->lock_flav), ++ lock_type_name(e->brltype), ++ e->offset, ++ e->count, ++ fsp_str_dbg(brl_fsp(br_lck))); ++ unlock_ok = false; ++ } ++ } ++ if (unlock_ok) { ++ brl_set_modified(br_lck, false); + } + + return status; +@@ -118,13 +131,6 @@ static void smbd_smb1_do_locks_try(struct tevent_req *req); + static void smbd_smb1_do_locks_retry(struct tevent_req *subreq); + static void smbd_smb1_blocked_locks_cleanup( + struct tevent_req *req, enum tevent_req_state req_state); +-static NTSTATUS smbd_smb1_do_locks_check( +- struct files_struct *fsp, +- uint16_t num_locks, +- struct smbd_lock_element *locks, +- uint16_t *blocker_idx, +- struct server_id *blocking_pid, +- uint64_t *blocking_smblctx); + + static void smbd_smb1_do_locks_setup_timeout( + struct smbd_smb1_do_locks_state *state, +@@ -378,18 +384,35 @@ static NTSTATUS smbd_smb1_do_locks_check_blocked( + return NT_STATUS_OK; + } + +-static NTSTATUS smbd_smb1_do_locks_check( +- struct files_struct *fsp, +- uint16_t num_locks, +- struct smbd_lock_element *locks, +- uint16_t *blocker_idx, +- struct server_id *blocking_pid, +- uint64_t *blocking_smblctx) ++static void smbd_smb1_do_locks_try_fn(struct share_mode_lock *lck, ++ struct byte_range_lock *br_lck, ++ void *private_data) + { ++ struct tevent_req *req = talloc_get_type_abort( ++ private_data, struct tevent_req); ++ struct smbd_smb1_do_locks_state *state = tevent_req_data( ++ req, struct smbd_smb1_do_locks_state); ++ struct smbd_do_locks_state brl_state; ++ struct files_struct *fsp = state->fsp; + struct tevent_req **blocked = fsp->blocked_smb1_lock_reqs; + size_t num_blocked = talloc_array_length(blocked); +- NTSTATUS status; ++ struct timeval endtime = { 0 }; ++ struct tevent_req *subreq = NULL; + size_t bi; ++ NTSTATUS status; ++ bool ok; ++ bool expired; ++ ++ /* ++ * The caller has checked fsp->fsp_flags.can_lock and lp_locking so ++ * br_lck has to be there! ++ */ ++ SMB_ASSERT(br_lck != NULL); ++ ++ brl_state = (struct smbd_do_locks_state) { ++ .num_locks = state->num_locks, ++ .locks = state->locks, ++ }; + + /* + * We check the pending/blocked requests +@@ -404,8 +427,8 @@ static NTSTATUS smbd_smb1_do_locks_check( + tevent_req_data(blocked[bi], + struct smbd_smb1_do_locks_state); + +- if (blocked_state->locks == locks) { +- SMB_ASSERT(blocked_state->num_locks == num_locks); ++ if (blocked_state->locks == state->locks) { ++ SMB_ASSERT(blocked_state->num_locks == state->num_locks); + + /* + * We found ourself... +@@ -416,61 +439,24 @@ static NTSTATUS smbd_smb1_do_locks_check( + status = smbd_smb1_do_locks_check_blocked( + blocked_state->num_locks, + blocked_state->locks, +- num_locks, +- locks, +- blocker_idx, +- blocking_smblctx); ++ state->num_locks, ++ state->locks, ++ &brl_state.blocker_idx, ++ &brl_state.blocking_smblctx); + if (!NT_STATUS_IS_OK(status)) { +- *blocking_pid = messaging_server_id( +- fsp->conn->sconn->msg_ctx); +- return status; ++ brl_state.blocking_pid = messaging_server_id( ++ fsp->conn->sconn->msg_ctx); ++ goto check_retry; + } + } + +- status = smbd_do_locks_try( +- fsp, +- num_locks, +- locks, +- blocker_idx, +- blocking_pid, +- blocking_smblctx); +- if (!NT_STATUS_IS_OK(status)) { +- return status; +- } +- +- return NT_STATUS_OK; +-} +- +-static void smbd_smb1_do_locks_try(struct tevent_req *req) +-{ +- struct smbd_smb1_do_locks_state *state = tevent_req_data( +- req, struct smbd_smb1_do_locks_state); +- struct files_struct *fsp = state->fsp; +- struct share_mode_lock *lck; +- struct timeval endtime = { 0 }; +- struct server_id blocking_pid = { 0 }; +- uint64_t blocking_smblctx = 0; +- struct tevent_req *subreq = NULL; +- NTSTATUS status; +- bool ok; +- bool expired; +- +- lck = get_existing_share_mode_lock(state, fsp->file_id); +- if (tevent_req_nomem(lck, req)) { +- DBG_DEBUG("Could not get share mode lock\n"); +- return; +- } +- +- status = smbd_smb1_do_locks_check( +- fsp, +- state->num_locks, +- state->locks, +- &state->blocker, +- &blocking_pid, +- &blocking_smblctx); ++ status = smbd_do_locks_try(br_lck, &brl_state); + if (NT_STATUS_IS_OK(status)) { + goto done; + } ++ ++ state->blocker = brl_state.blocker_idx; ++ + if (NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) { + /* + * We got NT_STATUS_RETRY, +@@ -494,8 +480,8 @@ static void smbd_smb1_do_locks_try(struct tevent_req *req) + * locking.tdb may cause retries. + */ + +- if (blocking_smblctx != UINT64_MAX) { +- SMB_ASSERT(blocking_smblctx == 0); ++ if (brl_state.blocking_smblctx != UINT64_MAX) { ++ SMB_ASSERT(brl_state.blocking_smblctx == 0); + goto setup_retry; + } + +@@ -516,6 +502,8 @@ static void smbd_smb1_do_locks_try(struct tevent_req *req) + endtime = timeval_current_ofs_msec(state->retry_msecs); + goto setup_retry; + } ++ ++check_retry: + if (!ERROR_WAS_LOCK_DENIED(status)) { + goto done; + } +@@ -529,7 +517,7 @@ static void smbd_smb1_do_locks_try(struct tevent_req *req) + smbd_smb1_do_locks_setup_timeout(state, &state->locks[state->blocker]); + DBG_DEBUG("timeout=%"PRIu32", blocking_smblctx=%"PRIu64"\n", + state->timeout, +- blocking_smblctx); ++ brl_state.blocking_smblctx); + + /* + * The client specified timeout expired +@@ -554,7 +542,7 @@ static void smbd_smb1_do_locks_try(struct tevent_req *req) + + endtime = state->endtime; + +- if (blocking_smblctx == UINT64_MAX) { ++ if (brl_state.blocking_smblctx == UINT64_MAX) { + struct timeval tmp; + + smbd_smb1_do_locks_update_polling_msecs(state); +@@ -568,11 +556,11 @@ static void smbd_smb1_do_locks_try(struct tevent_req *req) + + setup_retry: + subreq = share_mode_watch_send( +- state, state->ev, lck, blocking_pid); ++ state, state->ev, lck, brl_state.blocking_pid); + if (tevent_req_nomem(subreq, req)) { ++ status = NT_STATUS_NO_MEMORY; + goto done; + } +- TALLOC_FREE(lck); + tevent_req_set_callback(subreq, smbd_smb1_do_locks_retry, req); + + if (timeval_is_zero(&endtime)) { +@@ -586,10 +574,38 @@ setup_retry: + } + return; + done: +- TALLOC_FREE(lck); + smbd_smb1_brl_finish_by_req(req, status); + } + ++static void smbd_smb1_do_locks_try(struct tevent_req *req) ++{ ++ struct smbd_smb1_do_locks_state *state = tevent_req_data( ++ req, struct smbd_smb1_do_locks_state); ++ NTSTATUS status; ++ ++ if (!state->fsp->fsp_flags.can_lock) { ++ if (state->fsp->fsp_flags.is_directory) { ++ return smbd_smb1_brl_finish_by_req(req, ++ NT_STATUS_INVALID_DEVICE_REQUEST); ++ } ++ return smbd_smb1_brl_finish_by_req(req, ++ NT_STATUS_INVALID_HANDLE); ++ } ++ ++ if (!lp_locking(state->fsp->conn->params)) { ++ return smbd_smb1_brl_finish_by_req(req, NT_STATUS_OK); ++ } ++ ++ status = share_mode_do_locked_brl(state->fsp, ++ smbd_smb1_do_locks_try_fn, ++ req); ++ if (!NT_STATUS_IS_OK(status)) { ++ smbd_smb1_brl_finish_by_req(req, status); ++ return; ++ } ++ return; ++} ++ + static void smbd_smb1_do_locks_retry(struct tevent_req *subreq) + { + struct tevent_req *req = tevent_req_callback_data( +diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h +index 90387aeb6c3..5faf651b007 100644 +--- a/source3/smbd/proto.h ++++ b/source3/smbd/proto.h +@@ -86,13 +86,18 @@ ssize_t pwrite_fsync_recv(struct tevent_req *req, int *perr); + + /* The following definitions come from smbd/blocking.c */ + +-NTSTATUS smbd_do_locks_try( +- struct files_struct *fsp, +- uint16_t num_locks, +- struct smbd_lock_element *locks, +- uint16_t *blocker_idx, +- struct server_id *blocking_pid, +- uint64_t *blocking_smblctx); ++struct smbd_do_locks_state { ++ uint16_t num_locks; ++ struct smbd_lock_element *locks; ++ NTSTATUS status; ++ uint16_t blocker_idx; ++ struct server_id blocking_pid; ++ uint64_t blocking_smblctx; ++}; ++ ++NTSTATUS smbd_do_locks_try(struct byte_range_lock *br_lck, ++ struct smbd_do_locks_state *state); ++ + struct tevent_req *smbd_smb1_do_locks_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, +diff --git a/source3/smbd/smb2_lock.c b/source3/smbd/smb2_lock.c +index c9d810f71ba..14f912bf2da 100644 +--- a/source3/smbd/smb2_lock.c ++++ b/source3/smbd/smb2_lock.c +@@ -569,33 +569,32 @@ static void smbd_smb2_lock_update_polling_msecs( + state->polling_msecs += v_min; + } + +-static void smbd_smb2_lock_try(struct tevent_req *req) ++static void smbd_do_locks_try_fn(struct share_mode_lock *lck, ++ struct byte_range_lock *br_lck, ++ void *private_data) + { ++ struct tevent_req *req = talloc_get_type_abort( ++ private_data, struct tevent_req); + struct smbd_smb2_lock_state *state = tevent_req_data( + req, struct smbd_smb2_lock_state); +- struct share_mode_lock *lck = NULL; +- uint16_t blocker_idx; +- struct server_id blocking_pid = { 0 }; +- uint64_t blocking_smblctx; +- NTSTATUS status; ++ struct smbd_do_locks_state brl_state; + struct tevent_req *subreq = NULL; + struct timeval endtime = { 0 }; ++ NTSTATUS status; + +- lck = get_existing_share_mode_lock( +- talloc_tos(), state->fsp->file_id); +- if (tevent_req_nomem(lck, req)) { +- return; +- } ++ /* ++ * The caller has checked fsp->fsp_flags.can_lock and lp_locking so ++ * br_lck has to be there! ++ */ ++ SMB_ASSERT(br_lck != NULL); + +- status = smbd_do_locks_try( +- state->fsp, +- state->lock_count, +- state->locks, +- &blocker_idx, +- &blocking_pid, +- &blocking_smblctx); ++ brl_state = (struct smbd_do_locks_state) { ++ .num_locks = state->lock_count, ++ .locks = state->locks, ++ }; ++ ++ status = smbd_do_locks_try(br_lck, &brl_state); + if (NT_STATUS_IS_OK(status)) { +- TALLOC_FREE(lck); + tevent_req_done(req); + return; + } +@@ -622,8 +621,8 @@ static void smbd_smb2_lock_try(struct tevent_req *req) + * locking.tdb may cause retries. + */ + +- if (blocking_smblctx != UINT64_MAX) { +- SMB_ASSERT(blocking_smblctx == 0); ++ if (brl_state.blocking_smblctx != UINT64_MAX) { ++ SMB_ASSERT(brl_state.blocking_smblctx == 0); + goto setup_retry; + } + +@@ -658,7 +657,6 @@ static void smbd_smb2_lock_try(struct tevent_req *req) + status = NT_STATUS_LOCK_NOT_GRANTED; + } + if (!NT_STATUS_EQUAL(status, NT_STATUS_LOCK_NOT_GRANTED)) { +- TALLOC_FREE(lck); + tevent_req_nterror(req, status); + return; + } +@@ -670,12 +668,11 @@ static void smbd_smb2_lock_try(struct tevent_req *req) + state->retry_msecs = 0; + + if (!state->blocking) { +- TALLOC_FREE(lck); + tevent_req_nterror(req, status); + return; + } + +- if (blocking_smblctx == UINT64_MAX) { ++ if (brl_state.blocking_smblctx == UINT64_MAX) { + smbd_smb2_lock_update_polling_msecs(state); + + DBG_DEBUG("Blocked on a posix lock. Retry in %"PRIu32" msecs\n", +@@ -688,8 +685,7 @@ setup_retry: + DBG_DEBUG("Watching share mode lock\n"); + + subreq = share_mode_watch_send( +- state, state->ev, lck, blocking_pid); +- TALLOC_FREE(lck); ++ state, state->ev, lck, brl_state.blocking_pid); + if (tevent_req_nomem(subreq, req)) { + return; + } +@@ -708,6 +704,35 @@ setup_retry: + } + } + ++static void smbd_smb2_lock_try(struct tevent_req *req) ++{ ++ struct smbd_smb2_lock_state *state = tevent_req_data( ++ req, struct smbd_smb2_lock_state); ++ NTSTATUS status; ++ ++ if (!state->fsp->fsp_flags.can_lock) { ++ if (state->fsp->fsp_flags.is_directory) { ++ tevent_req_nterror(req, ++ NT_STATUS_INVALID_DEVICE_REQUEST); ++ return; ++ } ++ tevent_req_nterror(req, NT_STATUS_INVALID_HANDLE); ++ return; ++ } ++ ++ if (!lp_locking(state->fsp->conn->params)) { ++ return tevent_req_done(req); ++ } ++ ++ status = share_mode_do_locked_brl(state->fsp, ++ smbd_do_locks_try_fn, ++ req); ++ if (!NT_STATUS_IS_OK(status)) { ++ tevent_req_nterror(req, status); ++ return; ++ } ++} ++ + static void smbd_smb2_lock_retry(struct tevent_req *subreq) + { + struct tevent_req *req = tevent_req_callback_data( +diff --git a/source3/smbd/smb2_reply.c b/source3/smbd/smb2_reply.c +index 2b65fb30d76..3de36a7d673 100644 +--- a/source3/smbd/smb2_reply.c ++++ b/source3/smbd/smb2_reply.c +@@ -2122,23 +2122,22 @@ uint64_t get_lock_offset(const uint8_t *data, int data_offset, + return offset; + } + +-struct smbd_do_unlocking_state { +- struct files_struct *fsp; +- uint16_t num_ulocks; +- struct smbd_lock_element *ulocks; +- NTSTATUS status; +-}; +- +-static void smbd_do_unlocking_fn( +- struct share_mode_lock *lck, +- void *private_data) ++static void smbd_do_unlocking_fn(struct share_mode_lock *lck, ++ struct byte_range_lock *br_lck, ++ void *private_data) + { +- struct smbd_do_unlocking_state *state = private_data; +- struct files_struct *fsp = state->fsp; ++ struct smbd_do_locks_state *state = private_data; ++ struct files_struct *fsp = brl_fsp(br_lck); + uint16_t i; + +- for (i = 0; i < state->num_ulocks; i++) { +- struct smbd_lock_element *e = &state->ulocks[i]; ++ /* ++ * The caller has checked fsp->fsp_flags.can_lock and lp_locking so ++ * br_lck has to be there! ++ */ ++ SMB_ASSERT(br_lck != NULL); ++ ++ for (i = 0; i < state->num_locks; i++) { ++ struct smbd_lock_element *e = &state->locks[i]; + + DBG_DEBUG("unlock start=%"PRIu64", len=%"PRIu64" for " + "pid %"PRIu64", file %s\n", +@@ -2154,7 +2153,7 @@ static void smbd_do_unlocking_fn( + } + + state->status = do_unlock( +- fsp, e->smblctx, e->count, e->offset, e->lock_flav); ++ br_lck, e->smblctx, e->count, e->offset, e->lock_flav); + + DBG_DEBUG("do_unlock returned %s\n", + nt_errstr(state->status)); +@@ -2172,20 +2171,30 @@ NTSTATUS smbd_do_unlocking(struct smb_request *req, + uint16_t num_ulocks, + struct smbd_lock_element *ulocks) + { +- struct smbd_do_unlocking_state state = { +- .fsp = fsp, +- .num_ulocks = num_ulocks, +- .ulocks = ulocks, ++ struct smbd_do_locks_state state = { ++ .num_locks = num_ulocks, ++ .locks = ulocks, + }; + NTSTATUS status; + + DBG_NOTICE("%s num_ulocks=%"PRIu16"\n", fsp_fnum_dbg(fsp), num_ulocks); + +- status = share_mode_do_locked_vfs_allowed( +- fsp->file_id, smbd_do_unlocking_fn, &state); ++ if (!fsp->fsp_flags.can_lock) { ++ if (fsp->fsp_flags.is_directory) { ++ return NT_STATUS_INVALID_DEVICE_REQUEST; ++ } ++ return NT_STATUS_INVALID_HANDLE; ++ } ++ ++ if (!lp_locking(fsp->conn->params)) { ++ return NT_STATUS_OK; ++ } + ++ status = share_mode_do_locked_brl(fsp, ++ smbd_do_unlocking_fn, ++ &state); + if (!NT_STATUS_IS_OK(status)) { +- DBG_DEBUG("share_mode_do_locked_vfs_allowed failed: %s\n", ++ DBG_DEBUG("share_mode_do_locked_brl failed: %s\n", + nt_errstr(status)); + return status; + } +-- +2.49.0 + + +From 45211424de82b03e187369286034b7c49136ba5f Mon Sep 17 00:00:00 2001 +From: Stefan Metzmacher +Date: Wed, 8 Jan 2025 12:51:37 +0100 +Subject: [PATCH 17/24] s3/brlock: remove brl_get_locks_for_locking() + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=15767 + +Pair-Programmed-With: Ralph Boehme +Signed-off-by: Ralph Boehme +Signed-off-by: Stefan Metzmacher +(cherry picked from commit 0c4c430c50e15d591a0d871a5f3e59e8be0d0a83) +--- + source3/locking/brlock.c | 19 ------------------- + source3/locking/proto.h | 4 ---- + 2 files changed, 23 deletions(-) + +diff --git a/source3/locking/brlock.c b/source3/locking/brlock.c +index 0e58339b108..b9decbc9e2a 100644 +--- a/source3/locking/brlock.c ++++ b/source3/locking/brlock.c +@@ -1733,25 +1733,6 @@ struct byte_range_lock *brl_get_locks(TALLOC_CTX *mem_ctx, files_struct *fsp) + return br_lck; + } + +-struct byte_range_lock *brl_get_locks_for_locking(TALLOC_CTX *mem_ctx, +- files_struct *fsp, +- TALLOC_CTX *req_mem_ctx, +- const struct GUID *req_guid) +-{ +- struct byte_range_lock *br_lck = NULL; +- +- br_lck = brl_get_locks(mem_ctx, fsp); +- if (br_lck == NULL) { +- return NULL; +- } +- SMB_ASSERT(req_mem_ctx != NULL); +- br_lck->req_mem_ctx = req_mem_ctx; +- SMB_ASSERT(req_guid != NULL); +- br_lck->req_guid = req_guid; +- +- return br_lck; +-} +- + struct brl_get_locks_readonly_state { + TALLOC_CTX *mem_ctx; + struct byte_range_lock **br_lock; +diff --git a/source3/locking/proto.h b/source3/locking/proto.h +index e332abf34ec..44b43c1b1e2 100644 +--- a/source3/locking/proto.h ++++ b/source3/locking/proto.h +@@ -83,10 +83,6 @@ int brl_forall(void (*fn)(struct file_id id, struct server_id pid, + br_off start, br_off size, + void *private_data), + void *private_data); +-struct byte_range_lock *brl_get_locks_for_locking(TALLOC_CTX *mem_ctx, +- files_struct *fsp, +- TALLOC_CTX *req_mem_ctx, +- const struct GUID *req_guid); + struct share_mode_lock; + typedef void (*share_mode_do_locked_brl_fn_t)( + struct share_mode_lock *lck, +-- +2.49.0 + + +From ef4e89916360c8d7f2372c8508c8a79dfd995c47 Mon Sep 17 00:00:00 2001 +From: Ralph Boehme +Date: Mon, 27 Jan 2025 15:22:26 +0100 +Subject: [PATCH 18/24] smbd: call locking_close_file() while still holding a + glock on the locking.tdb record + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=15767 + +Signed-off-by: Ralph Boehme +Reviewed-by: Stefan Metzmacher +(cherry picked from commit 4d680b6c17ee7674b9686aec2b69038f89e1989a) +--- + source3/smbd/close.c | 26 +++++++++++++++++++------- + 1 file changed, 19 insertions(+), 7 deletions(-) + +diff --git a/source3/smbd/close.c b/source3/smbd/close.c +index 1b027a319a4..964c3530e8d 100644 +--- a/source3/smbd/close.c ++++ b/source3/smbd/close.c +@@ -303,6 +303,17 @@ static void close_share_mode_lock_prepare(struct share_mode_lock *lck, + */ + *keep_locked = false; + ++ if (fsp->current_lock_count > 0) { ++ /* ++ * Remove the byte-range locks under the glock ++ */ ++ *keep_locked = true; ++ } ++ ++ if (fh_get_refcount(fsp->fh) > 1) { ++ return; ++ } ++ + if (fsp->oplock_type != NO_OPLOCK) { + ok = remove_share_oplock(lck, fsp); + if (!ok) { +@@ -453,6 +464,12 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp, + return status; + } + ++ locking_close_file(fsp, close_type); ++ ++ if (fh_get_refcount(fsp->fh) > 1) { ++ goto done; ++ } ++ + /* Remove the oplock before potentially deleting the file. */ + if (fsp->oplock_type != NO_OPLOCK) { + release_file_oplock(fsp); +@@ -890,13 +907,8 @@ static NTSTATUS close_normal_file(struct smb_request *req, files_struct *fsp, + the same handle we only have one share mode. Ensure we only remove + the share mode on the last close. */ + +- if (fh_get_refcount(fsp->fh) == 1) { +- /* Should we return on error here... ? */ +- tmp = close_remove_share_mode(fsp, close_type); +- status = ntstatus_keeperror(status, tmp); +- } +- +- locking_close_file(fsp, close_type); ++ tmp = close_remove_share_mode(fsp, close_type); ++ status = ntstatus_keeperror(status, tmp); + + /* + * Ensure pending modtime is set before closing underlying fd. +-- +2.49.0 + + +From 96d3bacbd967b355c91bc1d9e583edb1cdd3bd19 Mon Sep 17 00:00:00 2001 +From: Ralph Boehme +Date: Thu, 30 Jan 2025 17:35:26 +0100 +Subject: [PATCH 19/24] s3/locking: prepare brl_locktest() for upgradable + read-only locks + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=15767 + +Signed-off-by: Ralph Boehme +Reviewed-by: Stefan Metzmacher +(cherry picked from commit 8f9387ceb5c94c7db92ab342e33c64b858c301b1) +--- + source3/locking/brlock.c | 5 +++-- + source3/locking/locking.c | 4 ++-- + source3/locking/proto.h | 3 ++- + 3 files changed, 7 insertions(+), 5 deletions(-) + +diff --git a/source3/locking/brlock.c b/source3/locking/brlock.c +index b9decbc9e2a..92621c41577 100644 +--- a/source3/locking/brlock.c ++++ b/source3/locking/brlock.c +@@ -1192,7 +1192,8 @@ bool brl_unlock(struct byte_range_lock *br_lck, + ****************************************************************************/ + + bool brl_locktest(struct byte_range_lock *br_lck, +- const struct lock_struct *rw_probe) ++ const struct lock_struct *rw_probe, ++ bool upgradable) + { + bool ret = True; + unsigned int i; +@@ -1205,7 +1206,7 @@ bool brl_locktest(struct byte_range_lock *br_lck, + * Our own locks don't conflict. + */ + if (brl_conflict_other(&locks[i], rw_probe)) { +- if (br_lck->record == NULL) { ++ if (!upgradable) { + /* readonly */ + return false; + } +diff --git a/source3/locking/locking.c b/source3/locking/locking.c +index ffb89378cb5..993d3a96591 100644 +--- a/source3/locking/locking.c ++++ b/source3/locking/locking.c +@@ -145,7 +145,7 @@ bool strict_lock_check_default(files_struct *fsp, struct lock_struct *plock) + if (!br_lck) { + return true; + } +- ret = brl_locktest(br_lck, plock); ++ ret = brl_locktest(br_lck, plock, false); + + if (!ret) { + /* +@@ -156,7 +156,7 @@ bool strict_lock_check_default(files_struct *fsp, struct lock_struct *plock) + if (br_lck == NULL) { + return true; + } +- ret = brl_locktest(br_lck, plock); ++ ret = brl_locktest(br_lck, plock, true); + TALLOC_FREE(br_lck); + } + +diff --git a/source3/locking/proto.h b/source3/locking/proto.h +index 44b43c1b1e2..44808171f1a 100644 +--- a/source3/locking/proto.h ++++ b/source3/locking/proto.h +@@ -66,7 +66,8 @@ bool brl_unlock(struct byte_range_lock *br_lck, + bool brl_unlock_windows_default(struct byte_range_lock *br_lck, + const struct lock_struct *plock); + bool brl_locktest(struct byte_range_lock *br_lck, +- const struct lock_struct *rw_probe); ++ const struct lock_struct *rw_probe, ++ bool upgradable); + NTSTATUS brl_lockquery(struct byte_range_lock *br_lck, + uint64_t *psmblctx, + struct server_id pid, +-- +2.49.0 + + +From b88988bcc9ce1fa0b8152e0eb89cb496f675fbc1 Mon Sep 17 00:00:00 2001 +From: Ralph Boehme +Date: Wed, 2 Apr 2025 12:43:15 +0200 +Subject: [PATCH 20/24] smbd: check can_lock in strict_lock_check_default() + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=15767 + +Pair-Programmed-With: Stefan Metzmacher +Signed-off-by: Ralph Boehme +Signed-off-by: Stefan Metzmacher +(cherry picked from commit 678f28c1af7c160ffdcb0e4baa0a7d4b9906f2e5) +--- + source3/locking/locking.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/source3/locking/locking.c b/source3/locking/locking.c +index 993d3a96591..ce2ccaccd11 100644 +--- a/source3/locking/locking.c ++++ b/source3/locking/locking.c +@@ -117,7 +117,10 @@ bool strict_lock_check_default(files_struct *fsp, struct lock_struct *plock) + return True; + } + +- if (!lp_locking(fsp->conn->params) || !strict_locking) { ++ if (!lp_locking(fsp->conn->params) || ++ !strict_locking || ++ !fsp->fsp_flags.can_lock) ++ { + return True; + } + +-- +2.49.0 + + +From e5d8f12b8ecfcd52ec8dc32147b1f2c6d8115c37 Mon Sep 17 00:00:00 2001 +From: Ralph Boehme +Date: Thu, 30 Jan 2025 07:40:32 +0100 +Subject: [PATCH 21/24] smbd: use share_mode_do_locked_brl() in + strict_lock_check_default() + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=15767 + +Signed-off-by: Ralph Boehme +Reviewed-by: Stefan Metzmacher +(cherry picked from commit 56bb20c87a733ab8f7efedd881ea0ecaf51b2ba8) +--- + source3/locking/locking.c | 43 +++++++++++++++++++++++++++++++++------ + 1 file changed, 37 insertions(+), 6 deletions(-) + +diff --git a/source3/locking/locking.c b/source3/locking/locking.c +index ce2ccaccd11..ddaa5405f02 100644 +--- a/source3/locking/locking.c ++++ b/source3/locking/locking.c +@@ -107,10 +107,32 @@ void init_strict_lock_struct(files_struct *fsp, + }; + } + ++struct strict_lock_check_state { ++ bool ret; ++ files_struct *fsp; ++ struct lock_struct *plock; ++}; ++ ++static void strict_lock_check_default_fn(struct share_mode_lock *lck, ++ struct byte_range_lock *br_lck, ++ void *private_data) ++{ ++ struct strict_lock_check_state *state = private_data; ++ ++ /* ++ * The caller has checked fsp->fsp_flags.can_lock and lp_locking so ++ * br_lck has to be there! ++ */ ++ SMB_ASSERT(br_lck != NULL); ++ ++ state->ret = brl_locktest(br_lck, state->plock, true); ++} ++ + bool strict_lock_check_default(files_struct *fsp, struct lock_struct *plock) + { + struct byte_range_lock *br_lck; + int strict_locking = lp_strict_locking(fsp->conn->params); ++ NTSTATUS status; + bool ret = False; + + if (plock->size == 0) { +@@ -149,18 +171,27 @@ bool strict_lock_check_default(files_struct *fsp, struct lock_struct *plock) + return true; + } + ret = brl_locktest(br_lck, plock, false); +- + if (!ret) { + /* + * We got a lock conflict. Retry with rw locks to enable + * autocleanup. This is the slow path anyway. + */ +- br_lck = brl_get_locks(talloc_tos(), fsp); +- if (br_lck == NULL) { +- return true; ++ ++ struct strict_lock_check_state state = ++ (struct strict_lock_check_state) { ++ .fsp = fsp, ++ .plock = plock, ++ }; ++ ++ status = share_mode_do_locked_brl(fsp, ++ strict_lock_check_default_fn, ++ &state); ++ if (!NT_STATUS_IS_OK(status)) { ++ DBG_ERR("share_mode_do_locked_brl [%s] failed: %s\n", ++ fsp_str_dbg(fsp), nt_errstr(status)); ++ state.ret = false; + } +- ret = brl_locktest(br_lck, plock, true); +- TALLOC_FREE(br_lck); ++ ret = state.ret; + } + + DBG_DEBUG("flavour = %s brl start=%" PRIu64 " " +-- +2.49.0 + + +From c26eded8448722126d73f3113da6b27378185475 Mon Sep 17 00:00:00 2001 +From: Ralph Boehme +Date: Tue, 28 Jan 2025 11:19:05 +0100 +Subject: [PATCH 22/24] smbd: use share_mode_do_locked_brl() in + vfs_default_durable_disconnect() + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=15767 + +Signed-off-by: Ralph Boehme +Reviewed-by: Stefan Metzmacher +(backported from commit 393379fc9c726eb781fd1bfb3a70ea2802739aff) +[slow@samba.org: conflict due to removed delayed write time handling] +--- + source3/locking/brlock.c | 14 +---- + source3/locking/proto.h | 9 ++- + source3/smbd/durable.c | 117 ++++++++++++++++++++++++--------------- + 3 files changed, 82 insertions(+), 58 deletions(-) + +diff --git a/source3/locking/brlock.c b/source3/locking/brlock.c +index 92621c41577..4dcae87691b 100644 +--- a/source3/locking/brlock.c ++++ b/source3/locking/brlock.c +@@ -1368,14 +1368,14 @@ void brl_close_fnum(struct byte_range_lock *br_lck) + } + } + +-bool brl_mark_disconnected(struct files_struct *fsp) ++bool brl_mark_disconnected(struct files_struct *fsp, ++ struct byte_range_lock *br_lck) + { + uint32_t tid = fsp->conn->cnum; + uint64_t smblctx; + uint64_t fnum = fsp->fnum; + unsigned int i; + struct server_id self = messaging_server_id(fsp->conn->sconn->msg_ctx); +- struct byte_range_lock *br_lck = NULL; + + if (fsp->op == NULL) { + return false; +@@ -1391,11 +1391,6 @@ bool brl_mark_disconnected(struct files_struct *fsp) + return true; + } + +- br_lck = brl_get_locks(talloc_tos(), fsp); +- if (br_lck == NULL) { +- return false; +- } +- + for (i=0; i < br_lck->num_locks; i++) { + struct lock_struct *lock = &br_lck->lock_data[i]; + +@@ -1405,22 +1400,18 @@ bool brl_mark_disconnected(struct files_struct *fsp) + */ + + if (lock->context.smblctx != smblctx) { +- TALLOC_FREE(br_lck); + return false; + } + + if (lock->context.tid != tid) { +- TALLOC_FREE(br_lck); + return false; + } + + if (!server_id_equal(&lock->context.pid, &self)) { +- TALLOC_FREE(br_lck); + return false; + } + + if (lock->fnum != fnum) { +- TALLOC_FREE(br_lck); + return false; + } + +@@ -1430,7 +1421,6 @@ bool brl_mark_disconnected(struct files_struct *fsp) + } + + br_lck->modified = true; +- TALLOC_FREE(br_lck); + return true; + } + +diff --git a/source3/locking/proto.h b/source3/locking/proto.h +index 44808171f1a..d3b4e02bc26 100644 +--- a/source3/locking/proto.h ++++ b/source3/locking/proto.h +@@ -75,7 +75,14 @@ NTSTATUS brl_lockquery(struct byte_range_lock *br_lck, + br_off *psize, + enum brl_type *plock_type, + enum brl_flavour lock_flav); +-bool brl_mark_disconnected(struct files_struct *fsp); ++ ++struct brl_connectstate { ++ bool ok; ++ struct files_struct *fsp; ++}; ++ ++bool brl_mark_disconnected(struct files_struct *fsp, ++ struct byte_range_lock *br_lck); + bool brl_reconnect_disconnected(struct files_struct *fsp); + void brl_close_fnum(struct byte_range_lock *br_lck); + int brl_forall(void (*fn)(struct file_id id, struct server_id pid, +diff --git a/source3/smbd/durable.c b/source3/smbd/durable.c +index bd0c9f58e24..d315cb21ccc 100644 +--- a/source3/smbd/durable.c ++++ b/source3/smbd/durable.c +@@ -133,6 +133,63 @@ NTSTATUS vfs_default_durable_cookie(struct files_struct *fsp, + return NT_STATUS_OK; + } + ++struct durable_disconnect_state { ++ NTSTATUS status; ++ struct files_struct *fsp; ++}; ++ ++static void default_durable_disconnect_fn(struct share_mode_lock *lck, ++ struct byte_range_lock *br_lck, ++ void *private_data) ++{ ++ struct durable_disconnect_state *state = private_data; ++ struct files_struct *fsp = state->fsp; ++ struct smb_file_time ft; ++ bool ok; ++ ++ /* Ensure any pending write time updates are done. */ ++ if (fsp->update_write_time_event) { ++ fsp_flush_write_time_update(fsp); ++ } ++ ++ ++ init_smb_file_time(&ft); ++ ++ if (fsp->fsp_flags.write_time_forced) { ++ NTTIME mtime = share_mode_changed_write_time(lck); ++ ft.mtime = nt_time_to_full_timespec(mtime); ++ } else if (fsp->fsp_flags.update_write_time_on_close) { ++ if (is_omit_timespec(&fsp->close_write_time)) { ++ ft.mtime = timespec_current(); ++ } else { ++ ft.mtime = fsp->close_write_time; ++ } ++ } ++ ++ if (!is_omit_timespec(&ft.mtime)) { ++ round_timespec(fsp->conn->ts_res, &ft.mtime); ++ file_ntimes(fsp->conn, fsp, &ft); ++ } ++ ++ ok = mark_share_mode_disconnected(lck, fsp); ++ if (!ok) { ++ state->status = NT_STATUS_UNSUCCESSFUL; ++ return; ++ } ++ ++ if (br_lck == NULL) { ++ state->status = NT_STATUS_OK; ++ return; ++ } ++ ++ ok = brl_mark_disconnected(fsp, br_lck); ++ if (!ok) { ++ state->status = NT_STATUS_UNSUCCESSFUL; ++ return; ++ } ++ state->status = NT_STATUS_OK; ++} ++ + NTSTATUS vfs_default_durable_disconnect(struct files_struct *fsp, + const DATA_BLOB old_cookie, + TALLOC_CTX *mem_ctx, +@@ -143,8 +200,7 @@ NTSTATUS vfs_default_durable_disconnect(struct files_struct *fsp, + enum ndr_err_code ndr_err; + struct vfs_default_durable_cookie cookie; + DATA_BLOB new_cookie_blob = data_blob_null; +- struct share_mode_lock *lck; +- bool ok; ++ struct durable_disconnect_state state; + + *new_cookie = data_blob_null; + +@@ -198,52 +254,23 @@ NTSTATUS vfs_default_durable_disconnect(struct files_struct *fsp, + return NT_STATUS_NOT_SUPPORTED; + } + +- /* Ensure any pending write time updates are done. */ +- if (fsp->update_write_time_event) { +- fsp_flush_write_time_update(fsp); +- } ++ state = (struct durable_disconnect_state) { ++ .fsp = fsp, ++ }; + +- /* +- * The above checks are done in mark_share_mode_disconnected() too +- * but we want to avoid getting the lock if possible +- */ +- lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id); +- if (lck != NULL) { +- struct smb_file_time ft; +- +- init_smb_file_time(&ft); +- +- if (fsp->fsp_flags.write_time_forced) { +- NTTIME mtime = share_mode_changed_write_time(lck); +- ft.mtime = nt_time_to_full_timespec(mtime); +- } else if (fsp->fsp_flags.update_write_time_on_close) { +- if (is_omit_timespec(&fsp->close_write_time)) { +- ft.mtime = timespec_current(); +- } else { +- ft.mtime = fsp->close_write_time; +- } +- } +- +- if (!is_omit_timespec(&ft.mtime)) { +- round_timespec(conn->ts_res, &ft.mtime); +- file_ntimes(conn, fsp, &ft); +- } +- +- ok = mark_share_mode_disconnected(lck, fsp); +- if (!ok) { +- TALLOC_FREE(lck); +- } +- } +- if (lck != NULL) { +- ok = brl_mark_disconnected(fsp); +- if (!ok) { +- TALLOC_FREE(lck); +- } ++ status = share_mode_do_locked_brl(fsp, ++ default_durable_disconnect_fn, ++ &state); ++ if (!NT_STATUS_IS_OK(status)) { ++ DBG_ERR("share_mode_do_locked_brl [%s] failed: %s\n", ++ fsp_str_dbg(fsp), nt_errstr(status)); ++ return status; + } +- if (lck == NULL) { +- return NT_STATUS_NOT_SUPPORTED; ++ if (!NT_STATUS_IS_OK(state.status)) { ++ DBG_ERR("default_durable_disconnect_fn [%s] failed: %s\n", ++ fsp_str_dbg(fsp), nt_errstr(state.status)); ++ return state.status; + } +- TALLOC_FREE(lck); + + status = vfs_stat_fsp(fsp); + if (!NT_STATUS_IS_OK(status)) { +-- +2.49.0 + + +From 33afc50416b09f2c025975fd7a98686f046005ae Mon Sep 17 00:00:00 2001 +From: Ralph Boehme +Date: Wed, 2 Apr 2025 14:52:03 +0200 +Subject: [PATCH 23/24] smbd: use share_mode_do_locked_brl() in + vfs_default_durable_reconnect() + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=15767 + +Signed-off-by: Ralph Boehme +Reviewed-by: Stefan Metzmacher +(backported from commit dc03a06ffcc79d0818ae4a36fe3f2df705144138) +[slow@samba.org: conflict due to removed delayed write time handling] +[slow@samba.org: conflict due to filename_convert_dirfsp_rel()] +--- + source3/locking/brlock.c | 15 +- + source3/locking/proto.h | 3 +- + source3/smbd/durable.c | 439 ++++++++++++++++++++------------------- + 3 files changed, 234 insertions(+), 223 deletions(-) + +diff --git a/source3/locking/brlock.c b/source3/locking/brlock.c +index 4dcae87691b..edc5c34ebc3 100644 +--- a/source3/locking/brlock.c ++++ b/source3/locking/brlock.c +@@ -1424,14 +1424,14 @@ bool brl_mark_disconnected(struct files_struct *fsp, + return true; + } + +-bool brl_reconnect_disconnected(struct files_struct *fsp) ++bool brl_reconnect_disconnected(struct files_struct *fsp, ++ struct byte_range_lock *br_lck) + { + uint32_t tid = fsp->conn->cnum; + uint64_t smblctx; + uint64_t fnum = fsp->fnum; + unsigned int i; + struct server_id self = messaging_server_id(fsp->conn->sconn->msg_ctx); +- struct byte_range_lock *br_lck = NULL; + + if (fsp->op == NULL) { + return false; +@@ -1449,13 +1449,7 @@ bool brl_reconnect_disconnected(struct files_struct *fsp) + * them instead. + */ + +- br_lck = brl_get_locks(talloc_tos(), fsp); +- if (br_lck == NULL) { +- return false; +- } +- + if (br_lck->num_locks == 0) { +- TALLOC_FREE(br_lck); + return true; + } + +@@ -1468,22 +1462,18 @@ bool brl_reconnect_disconnected(struct files_struct *fsp) + */ + + if (lock->context.smblctx != smblctx) { +- TALLOC_FREE(br_lck); + return false; + } + + if (lock->context.tid != TID_FIELD_INVALID) { +- TALLOC_FREE(br_lck); + return false; + } + + if (!server_id_is_disconnected(&lock->context.pid)) { +- TALLOC_FREE(br_lck); + return false; + } + + if (lock->fnum != FNUM_FIELD_INVALID) { +- TALLOC_FREE(br_lck); + return false; + } + +@@ -1494,7 +1484,6 @@ bool brl_reconnect_disconnected(struct files_struct *fsp) + + fsp->current_lock_count = br_lck->num_locks; + br_lck->modified = true; +- TALLOC_FREE(br_lck); + return true; + } + +diff --git a/source3/locking/proto.h b/source3/locking/proto.h +index d3b4e02bc26..37d382833fc 100644 +--- a/source3/locking/proto.h ++++ b/source3/locking/proto.h +@@ -83,7 +83,8 @@ struct brl_connectstate { + + bool brl_mark_disconnected(struct files_struct *fsp, + struct byte_range_lock *br_lck); +-bool brl_reconnect_disconnected(struct files_struct *fsp); ++bool brl_reconnect_disconnected(struct files_struct *fsp, ++ struct byte_range_lock *br_lck); + void brl_close_fnum(struct byte_range_lock *br_lck); + int brl_forall(void (*fn)(struct file_id id, struct server_id pid, + enum brl_type lock_type, +diff --git a/source3/smbd/durable.c b/source3/smbd/durable.c +index d315cb21ccc..82777d3b81b 100644 +--- a/source3/smbd/durable.c ++++ b/source3/smbd/durable.c +@@ -563,121 +563,42 @@ static bool durable_reconnect_fn( + return false; /* Look at potential other entries */ + } + +-NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn, +- struct smb_request *smb1req, +- struct smbXsrv_open *op, +- const DATA_BLOB old_cookie, +- TALLOC_CTX *mem_ctx, +- files_struct **result, +- DATA_BLOB *new_cookie) ++struct vfs_default_durable_reconnect_state { ++ NTSTATUS status; ++ TALLOC_CTX *mem_ctx; ++ struct smb_request *smb1req; ++ struct smbXsrv_open *op; ++ struct vfs_default_durable_cookie cookie; ++ struct files_struct *fsp; ++ DATA_BLOB new_cookie_blob; ++}; ++ ++static void vfs_default_durable_reconnect_fn(struct share_mode_lock *lck, ++ struct byte_range_lock *br_lck, ++ void *private_data) + { ++ struct vfs_default_durable_reconnect_state *state = private_data; + const struct loadparm_substitution *lp_sub = + loadparm_s3_global_substitution(); +- struct share_mode_lock *lck; ++ struct files_struct *fsp = state->fsp; + 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; +- int ret; ++ struct durable_reconnect_state rstate = { .op = state->op, .e = &e, }; + struct vfs_open_how how = { .flags = 0, }; + struct file_id file_id; +- struct smb_filename *smb_fname = NULL; +- enum ndr_err_code ndr_err; +- struct vfs_default_durable_cookie cookie; +- DATA_BLOB new_cookie_blob = data_blob_null; + bool have_share_mode_entry = false; +- +- *result = NULL; +- *new_cookie = data_blob_null; +- +- if (!lp_durable_handles(SNUM(conn))) { +- return NT_STATUS_NOT_SUPPORTED; +- } +- +- /* +- * the checks for kernel oplocks +- * and similar things are done +- * in the vfs_default_durable_cookie() +- * call below. +- */ +- +- ndr_err = ndr_pull_struct_blob_all( +- &old_cookie, +- talloc_tos(), +- &cookie, +- (ndr_pull_flags_fn_t)ndr_pull_vfs_default_durable_cookie); +- if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { +- status = ndr_map_error2ntstatus(ndr_err); +- return status; +- } +- +- if (strcmp(cookie.magic, VFS_DEFAULT_DURABLE_COOKIE_MAGIC) != 0) { +- return NT_STATUS_INVALID_PARAMETER; +- } +- +- if (cookie.version != VFS_DEFAULT_DURABLE_COOKIE_VERSION) { +- return NT_STATUS_INVALID_PARAMETER; +- } +- +- if (!cookie.allow_reconnect) { +- return NT_STATUS_OBJECT_NAME_NOT_FOUND; +- } +- +- if (strcmp(cookie.servicepath, conn->connectpath) != 0) { +- return NT_STATUS_OBJECT_NAME_NOT_FOUND; +- } +- +- /* Create an smb_filename with stream_name == NULL. */ +- smb_fname = synthetic_smb_fname(talloc_tos(), +- cookie.base_name, +- NULL, +- NULL, +- 0, +- 0); +- if (smb_fname == NULL) { +- return NT_STATUS_NO_MEMORY; +- } +- +- ret = SMB_VFS_LSTAT(conn, smb_fname); +- if (ret == -1) { +- status = map_nt_error_from_unix_common(errno); +- DEBUG(1, ("Unable to lstat stream: %s => %s\n", +- smb_fname_str_dbg(smb_fname), +- nt_errstr(status))); +- return status; +- } +- +- if (!S_ISREG(smb_fname->st.st_ex_mode)) { +- return NT_STATUS_OBJECT_NAME_NOT_FOUND; +- } +- +- file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st); +- if (!file_id_equal(&cookie.id, &file_id)) { +- return NT_STATUS_OBJECT_NAME_NOT_FOUND; +- } +- +- /* +- * 1. check entry in locking.tdb +- */ +- +- lck = get_existing_share_mode_lock(mem_ctx, file_id); +- if (lck == NULL) { +- DEBUG(5, ("vfs_default_durable_reconnect: share-mode lock " +- "not obtained from db\n")); +- return NT_STATUS_OBJECT_NAME_NOT_FOUND; +- } ++ int ret; ++ bool ok; + + 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; ++ state->status = NT_STATUS_INTERNAL_DB_ERROR; + goto fail; + } + + if (e.pid.pid == 0) { + DBG_WARNING("Did not find a unique valid share mode entry\n"); +- status = NT_STATUS_OBJECT_NAME_NOT_FOUND; ++ state->status = NT_STATUS_OBJECT_NAME_NOT_FOUND; + goto fail; + } + +@@ -685,69 +606,36 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn, + DEBUG(5, ("vfs_default_durable_reconnect: denying durable " + "reconnect for handle that was not marked " + "disconnected (e.g. smbd or cluster node died)\n")); +- status = NT_STATUS_OBJECT_NAME_NOT_FOUND; ++ state->status = NT_STATUS_OBJECT_NAME_NOT_FOUND; + goto fail; + } + +- if (e.share_file_id != op->global->open_persistent_id) { ++ if (e.share_file_id != state->op->global->open_persistent_id) { + DBG_INFO("denying durable " + "share_file_id changed %"PRIu64" != %"PRIu64" " + "(e.g. another client had opened the file)\n", + e.share_file_id, +- op->global->open_persistent_id); +- status = NT_STATUS_OBJECT_NAME_NOT_FOUND; ++ state->op->global->open_persistent_id); ++ state->status = NT_STATUS_OBJECT_NAME_NOT_FOUND; + goto fail; + } + + if ((e.access_mask & (FILE_WRITE_DATA|FILE_APPEND_DATA)) && +- !CAN_WRITE(conn)) ++ !CAN_WRITE(fsp->conn)) + { + DEBUG(5, ("vfs_default_durable_reconnect: denying durable " + "share[%s] is not writeable anymore\n", +- lp_servicename(talloc_tos(), lp_sub, SNUM(conn)))); +- status = NT_STATUS_OBJECT_NAME_NOT_FOUND; +- goto fail; +- } +- +- /* +- * 2. proceed with opening file +- */ +- +- status = fsp_new(conn, conn, &fsp); +- if (!NT_STATUS_IS_OK(status)) { +- DEBUG(0, ("vfs_default_durable_reconnect: failed to create " +- "new fsp: %s\n", nt_errstr(status))); ++ lp_servicename(talloc_tos(), lp_sub, SNUM(fsp->conn)))); ++ state->status = NT_STATUS_OBJECT_NAME_NOT_FOUND; + goto fail; + } + + fh_set_private_options(fsp->fh, e.private_options); +- fsp->file_id = file_id; +- fsp->file_pid = smb1req->smbpid; +- fsp->vuid = smb1req->vuid; + fsp->open_time = e.time; + fsp->access_mask = e.access_mask; + fsp->fsp_flags.can_read = ((fsp->access_mask & FILE_READ_DATA) != 0); + fsp->fsp_flags.can_write = ((fsp->access_mask & (FILE_WRITE_DATA|FILE_APPEND_DATA)) != 0); +- fsp->fnum = op->local_id; +- fsp_set_gen_id(fsp); + +- /* +- * TODO: +- * Do we need to store the modified flag in the DB? +- */ +- fsp->fsp_flags.modified = false; +- /* +- * no durables for directories +- */ +- fsp->fsp_flags.is_directory = false; +- /* +- * For normal files, can_lock == !is_directory +- */ +- fsp->fsp_flags.can_lock = true; +- /* +- * We do not support aio write behind for smb2 +- */ +- fsp->fsp_flags.aio_write_behind = false; + fsp->oplock_type = e.op_type; + + if (fsp->oplock_type == LEASE_OPLOCK) { +@@ -760,21 +648,21 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn, + */ + if (!GUID_equal(fsp_client_guid(fsp), + &e.client_guid)) { +- status = NT_STATUS_OBJECT_NAME_NOT_FOUND; ++ state->status = NT_STATUS_OBJECT_NAME_NOT_FOUND; + goto fail; + } + +- status = leases_db_get( ++ state->status = leases_db_get( + &e.client_guid, + &e.lease_key, +- &file_id, ++ &fsp->file_id, + ¤t_state, /* current_state */ + NULL, /* breaking */ + NULL, /* breaking_to_requested */ + NULL, /* breaking_to_required */ + &lease_version, /* lease_version */ + &epoch); /* epoch */ +- if (!NT_STATUS_IS_OK(status)) { ++ if (!NT_STATUS_IS_OK(state->status)) { + goto fail; + } + +@@ -785,53 +673,46 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn, + lease_version, + epoch); + if (fsp->lease == NULL) { +- status = NT_STATUS_NO_MEMORY; ++ state->status = NT_STATUS_NO_MEMORY; + goto fail; + } + } + +- fsp->initial_allocation_size = cookie.initial_allocation_size; +- fh_set_position_information(fsp->fh, cookie.position_information); ++ fsp->initial_allocation_size = state->cookie.initial_allocation_size; ++ fh_set_position_information(fsp->fh, state->cookie.position_information); + fsp->fsp_flags.update_write_time_triggered = +- cookie.update_write_time_triggered; ++ state->cookie.update_write_time_triggered; + fsp->fsp_flags.update_write_time_on_close = +- cookie.update_write_time_on_close; +- fsp->fsp_flags.write_time_forced = cookie.write_time_forced; ++ state->cookie.update_write_time_on_close; ++ fsp->fsp_flags.write_time_forced = state->cookie.write_time_forced; + fsp->close_write_time = nt_time_to_full_timespec( +- cookie.close_write_time); ++ state->cookie.close_write_time); + +- status = fsp_set_smb_fname(fsp, smb_fname); +- if (!NT_STATUS_IS_OK(status)) { +- DEBUG(0, ("vfs_default_durable_reconnect: " +- "fsp_set_smb_fname failed: %s\n", +- nt_errstr(status))); +- goto fail; +- } +- +- op->compat = fsp; +- fsp->op = op; ++ state->op->compat = fsp; ++ fsp->op = state->op; + + ok = reset_share_mode_entry( + lck, + e.pid, + e.share_file_id, +- messaging_server_id(conn->sconn->msg_ctx), +- smb1req->mid, ++ messaging_server_id(fsp->conn->sconn->msg_ctx), ++ state->smb1req->mid, + fh_get_gen_id(fsp->fh)); + if (!ok) { + DBG_DEBUG("Could not set new share_mode_entry values\n"); +- status = NT_STATUS_INTERNAL_ERROR; ++ state->status = NT_STATUS_INTERNAL_ERROR; + goto fail; + } + have_share_mode_entry = true; + +- ok = brl_reconnect_disconnected(fsp); +- if (!ok) { +- status = NT_STATUS_INTERNAL_ERROR; +- DEBUG(1, ("vfs_default_durable_reconnect: " +- "failed to reopen brlocks: %s\n", +- nt_errstr(status))); +- goto fail; ++ if (br_lck != NULL) { ++ ok = brl_reconnect_disconnected(fsp, br_lck); ++ if (!ok) { ++ state->status = NT_STATUS_INTERNAL_ERROR; ++ DBG_ERR("failed to reopen brlocks: %s\n", ++ nt_errstr(state->status)); ++ goto fail; ++ } + } + + /* +@@ -845,10 +726,9 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn, + how.flags = O_RDONLY; + } + +- status = fd_openat(conn->cwd_fsp, fsp->fsp_name, fsp, &how); +- if (!NT_STATUS_IS_OK(status)) { +- DEBUG(1, ("vfs_default_durable_reconnect: failed to open " +- "file: %s\n", nt_errstr(status))); ++ state->status = fd_openat(fsp->conn->cwd_fsp, fsp->fsp_name, fsp, &how); ++ if (!NT_STATUS_IS_OK(state->status)) { ++ DBG_ERR("failed to open file: %s\n", nt_errstr(state->status)); + goto fail; + } + +@@ -863,70 +743,66 @@ NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn, + + ret = SMB_VFS_FSTAT(fsp, &fsp->fsp_name->st); + if (ret == -1) { +- status = map_nt_error_from_unix_common(errno); +- DEBUG(1, ("Unable to fstat stream: %s => %s\n", +- smb_fname_str_dbg(smb_fname), +- nt_errstr(status))); ++ state->status = map_nt_error_from_unix_common(errno); ++ DBG_ERR("Unable to fstat stream: %s => %s\n", ++ fsp_str_dbg(fsp), ++ nt_errstr(state->status)); + goto fail; + } + + if (!S_ISREG(fsp->fsp_name->st.st_ex_mode)) { +- status = NT_STATUS_OBJECT_NAME_NOT_FOUND; ++ state->status = NT_STATUS_OBJECT_NAME_NOT_FOUND; + goto fail; + } + +- file_id = vfs_file_id_from_sbuf(conn, &fsp->fsp_name->st); +- if (!file_id_equal(&cookie.id, &file_id)) { +- status = NT_STATUS_OBJECT_NAME_NOT_FOUND; ++ file_id = vfs_file_id_from_sbuf(fsp->conn, &fsp->fsp_name->st); ++ if (!file_id_equal(&state->cookie.id, &file_id)) { ++ state->status = NT_STATUS_OBJECT_NAME_NOT_FOUND; + goto fail; + } + + (void)fdos_mode(fsp); + +- ok = vfs_default_durable_reconnect_check_stat(&cookie.stat_info, ++ ok = vfs_default_durable_reconnect_check_stat(&state->cookie.stat_info, + &fsp->fsp_name->st, + fsp_str_dbg(fsp)); + if (!ok) { +- status = NT_STATUS_OBJECT_NAME_NOT_FOUND; ++ state->status = NT_STATUS_OBJECT_NAME_NOT_FOUND; + goto fail; + } + +- status = set_file_oplock(fsp); +- if (!NT_STATUS_IS_OK(status)) { ++ state->status = set_file_oplock(fsp); ++ if (!NT_STATUS_IS_OK(state->status)) { + goto fail; + } + +- status = vfs_default_durable_cookie(fsp, mem_ctx, &new_cookie_blob); +- if (!NT_STATUS_IS_OK(status)) { +- DEBUG(1, ("vfs_default_durable_reconnect: " +- "vfs_default_durable_cookie - %s\n", +- nt_errstr(status))); ++ state->status = vfs_default_durable_cookie(fsp, ++ state->mem_ctx, ++ &state->new_cookie_blob); ++ if (!NT_STATUS_IS_OK(state->status)) { ++ DBG_ERR("vfs_default_durable_cookie - %s\n", ++ nt_errstr(state->status)); + goto fail; + } + +- smb1req->chain_fsp = fsp; +- smb1req->smb2req->compat_chain_fsp = fsp; +- +- DEBUG(10, ("vfs_default_durable_reconnect: opened file '%s'\n", +- fsp_str_dbg(fsp))); ++ state->smb1req->chain_fsp = fsp; ++ state->smb1req->smb2req->compat_chain_fsp = fsp; + +- TALLOC_FREE(lck); ++ DBG_DEBUG("opened file '%s'\n", fsp_str_dbg(fsp)); + + fsp->fsp_flags.is_fsa = true; + +- *result = fsp; +- *new_cookie = new_cookie_blob; +- +- return NT_STATUS_OK; ++ state->status = NT_STATUS_OK; ++ return; + + fail: +- if (fsp != NULL && have_share_mode_entry) { ++ if (have_share_mode_entry) { + /* + * Something is screwed up, delete the sharemode entry. + */ + del_share_mode(lck, fsp); + } +- if (fsp != NULL && fsp_get_pathref_fd(fsp) != -1) { ++ if (fsp_get_pathref_fd(fsp) != -1) { + NTSTATUS close_status; + close_status = fd_close(fsp); + if (!NT_STATUS_IS_OK(close_status)) { +@@ -934,11 +810,156 @@ fail: + nt_errstr(close_status)); + } + } +- TALLOC_FREE(lck); +- if (fsp != NULL) { +- op->compat = NULL; +- fsp->op = NULL; +- file_free(smb1req, fsp); ++ state->op->compat = NULL; ++ fsp->op = NULL; ++} ++ ++NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn, ++ struct smb_request *smb1req, ++ struct smbXsrv_open *op, ++ const DATA_BLOB old_cookie, ++ TALLOC_CTX *mem_ctx, ++ files_struct **result, ++ DATA_BLOB *new_cookie) ++{ ++ struct vfs_default_durable_reconnect_state state; ++ struct smb_filename *smb_fname = NULL; ++ struct file_id file_id; ++ NTSTATUS status; ++ enum ndr_err_code ndr_err; ++ int ret; ++ ++ *result = NULL; ++ *new_cookie = data_blob_null; ++ ++ if (!lp_durable_handles(SNUM(conn))) { ++ return NT_STATUS_NOT_SUPPORTED; ++ } ++ ++ state = (struct vfs_default_durable_reconnect_state) { ++ .mem_ctx = mem_ctx, ++ .smb1req = smb1req, ++ .op = op, ++ }; ++ ++ /* ++ * the checks for kernel oplocks ++ * and similar things are done ++ * in the vfs_default_durable_cookie() ++ * call below. ++ */ ++ ++ ndr_err = ndr_pull_struct_blob_all( ++ &old_cookie, ++ talloc_tos(), ++ &state.cookie, ++ (ndr_pull_flags_fn_t)ndr_pull_vfs_default_durable_cookie); ++ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { ++ status = ndr_map_error2ntstatus(ndr_err); ++ return status; + } +- return status; ++ ++ if (strcmp(state.cookie.magic, VFS_DEFAULT_DURABLE_COOKIE_MAGIC) != 0) { ++ return NT_STATUS_INVALID_PARAMETER; ++ } ++ ++ if (state.cookie.version != VFS_DEFAULT_DURABLE_COOKIE_VERSION) { ++ return NT_STATUS_INVALID_PARAMETER; ++ } ++ ++ if (!state.cookie.allow_reconnect) { ++ return NT_STATUS_OBJECT_NAME_NOT_FOUND; ++ } ++ ++ if (strcmp(state.cookie.servicepath, conn->connectpath) != 0) { ++ return NT_STATUS_OBJECT_NAME_NOT_FOUND; ++ } ++ ++ /* Create an smb_filename with stream_name == NULL. */ ++ smb_fname = synthetic_smb_fname(talloc_tos(), ++ state.cookie.base_name, ++ NULL, ++ NULL, ++ 0, ++ 0); ++ if (smb_fname == NULL) { ++ return NT_STATUS_NO_MEMORY; ++ } ++ ++ ret = SMB_VFS_LSTAT(conn, smb_fname); ++ if (ret == -1) { ++ status = map_nt_error_from_unix_common(errno); ++ DEBUG(1, ("Unable to lstat stream: %s => %s\n", ++ smb_fname_str_dbg(smb_fname), ++ nt_errstr(status))); ++ return status; ++ } ++ if (!S_ISREG(smb_fname->st.st_ex_mode)) { ++ return NT_STATUS_OBJECT_NAME_NOT_FOUND; ++ } ++ ++ file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st); ++ if (!file_id_equal(&state.cookie.id, &file_id)) { ++ return NT_STATUS_OBJECT_NAME_NOT_FOUND; ++ } ++ ++ status = fsp_new(conn, conn, &state.fsp); ++ if (!NT_STATUS_IS_OK(status)) { ++ DBG_ERR("failed to create new fsp: %s\n", ++ nt_errstr(status)); ++ return status; ++ } ++ state.fsp->file_id = file_id; ++ state.fsp->file_pid = smb1req->smbpid; ++ state.fsp->vuid = smb1req->vuid; ++ state.fsp->fnum = op->local_id; ++ fsp_set_gen_id(state.fsp); ++ ++ status = fsp_set_smb_fname(state.fsp, smb_fname); ++ if (!NT_STATUS_IS_OK(status)) { ++ DBG_ERR("fsp_set_smb_fname failed: %s\n", ++ nt_errstr(status)); ++ file_free(smb1req, state.fsp); ++ return status; ++ } ++ ++ /* ++ * TODO: ++ * Do we need to store the modified flag in the DB? ++ */ ++ state.fsp->fsp_flags.modified = false; ++ /* ++ * no durables for directories ++ */ ++ state.fsp->fsp_flags.is_directory = false; ++ /* ++ * For normal files, can_lock == !is_directory ++ */ ++ state.fsp->fsp_flags.can_lock = true; ++ /* ++ * We do not support aio write behind for smb2 ++ */ ++ state.fsp->fsp_flags.aio_write_behind = false; ++ ++ status = share_mode_do_locked_brl(state.fsp, ++ vfs_default_durable_reconnect_fn, ++ &state); ++ if (!NT_STATUS_IS_OK(status)) { ++ DBG_ERR("share_mode_do_locked_brl [%s] failed: %s\n", ++ smb_fname_str_dbg(smb_fname), nt_errstr(status)); ++ file_free(smb1req, state.fsp); ++ return status; ++ } ++ if (!NT_STATUS_IS_OK(state.status)) { ++ DBG_ERR("default_durable_reconnect_fn [%s] failed: %s\n", ++ smb_fname_str_dbg(smb_fname), ++ nt_errstr(state.status)); ++ file_free(smb1req, state.fsp); ++ return state.status; ++ } ++ ++ *result = state.fsp; ++ *new_cookie = state.new_cookie_blob; ++ ++ return NT_STATUS_OK; + } +-- +2.49.0 + + +From 810c9268742b761d3e32aedb624d343bdf51d467 Mon Sep 17 00:00:00 2001 +From: Ralph Boehme +Date: Tue, 28 Jan 2025 14:48:39 +0100 +Subject: [PATCH 24/24] s3:rpc_server/srvsvc: use brl_get_locks_readonly() + instead of brl_get_locks() + +No need to keep the record locked longer then needed. + +BUG: https://bugzilla.samba.org/show_bug.cgi?id=15767 + +Signed-off-by: Ralph Boehme +Reviewed-by: Stefan Metzmacher +(cherry picked from commit c36cc2b6720a2cfe54ce52a500dc499418e27e34) +--- + source3/rpc_server/srvsvc/srv_srvsvc_nt.c | 12 +++++++++--- + 1 file changed, 9 insertions(+), 3 deletions(-) + +diff --git a/source3/rpc_server/srvsvc/srv_srvsvc_nt.c b/source3/rpc_server/srvsvc/srv_srvsvc_nt.c +index 1129576f751..b19baf9e625 100644 +--- a/source3/rpc_server/srvsvc/srv_srvsvc_nt.c ++++ b/source3/rpc_server/srvsvc/srv_srvsvc_nt.c +@@ -192,17 +192,23 @@ static WERROR net_enum_files(TALLOC_CTX *ctx, + /* need to count the number of locks on a file */ + + for (i=0; i<(*ctr3)->count; i++) { +- struct files_struct fsp = { .file_id = f_enum_cnt.fids[i], }; ++ struct files_struct *fsp = NULL; + struct byte_range_lock *brl = NULL; + +- brl = brl_get_locks(ctx, &fsp); ++ fsp = talloc_zero(talloc_tos(), struct files_struct); ++ if (fsp == NULL) { ++ return WERR_NOT_ENOUGH_MEMORY; ++ } ++ fsp->file_id = f_enum_cnt.fids[i]; ++ ++ brl = brl_get_locks_readonly(fsp); + if (brl == NULL) { + continue; + } + + (*ctr3)->array[i].num_locks = brl_num_locks(brl); +- + TALLOC_FREE(brl); ++ TALLOC_FREE(fsp); + } + + return WERR_OK; +-- +2.49.0 diff --git a/samba.spec b/samba.spec index a14a184..72e8c28 100644 --- a/samba.spec +++ b/samba.spec @@ -2,7 +2,7 @@ ## (rpmautospec version 0.6.5) ## RPMAUTOSPEC: autorelease, autochangelog %define autorelease(e:s:pb:n) %{?-p:0.}%{lua: - release_number = 3; + release_number = 5; base_release_number = tonumber(rpm.expand("%{?-b*}%{!?-b:1}")); print(release_number + base_release_number - 1); }%{?-e:.%{-e*}}%{?-s:.%{-s*}}%{!?-n:%{?dist}} @@ -4009,6 +4009,12 @@ fi %changelog ## START: Generated by rpmautospec +* Wed Apr 16 2025 Pavel Filipenský - 0:4.21.3-104 +- resolves: RHEL-75586 - Fix deadlock between two smbd processes + +* Sun Mar 30 2025 Pavel Filipenský - 0:4.21.3-103 +- resolves: RHEL-85339 - Fix winbindd memory leak + * Mon Feb 17 2025 Pavel Filipenský - 0:4.21.3-102 - resolves: RHEL-73183 - Fix keytab generation