The branch, master has been updated
via 9cdc1eaa09a smbtorture: add test "smb.lease.two-leases"
via f2ea2ead517 smbd: set fsp->is_sparse in
vfs_default_durable_reconnect()
via e3874ec57fb smbd: remove redundant initialisation
via 331fd3d49f8 smbd: pass fsp to
vfs_default_durable_reconnect_check_stat()
via dccff4b290a s3/locking: simplify reset_share_mode_entry()
from d6ee9b04f2c s3-winbindd: make sure we always have
WINBINDD_CACHE_VERSION in winbindd_cache.tdb
https://git.samba.org/?p=samba.git;a=shortlog;h=master
- Log -----------------------------------------------------------------
commit 9cdc1eaa09a07d21feefc21772e65f7e325838c1
Author: Ralph Boehme <[email protected]>
Date: Sat Dec 6 12:22:00 2025 +0100
smbtorture: add test "smb.lease.two-leases"
A test that shows that for redispatched deferred opens even though
delay_for_oplock_fn() might end up calling send_break_message(), the
following
check in process_oplock_break_message() avoids sending break messages to the
client:
process_oplock_break_message():
....
if (breaking) {
break_to &= breaking_to_required;
if (breaking_to_required != break_to) {
/*
* Note we don't increment the epoch
* here, which might be a bug in
* Windows too...
*/
breaking_to_required = break_to;
}
break_needed = false;
}
...
Signed-off-by: Ralph Boehme <[email protected]>
Reviewed-by: Volker Lendecke <[email protected]>
Autobuild-User(master): Volker Lendecke <[email protected]>
Autobuild-Date(master): Mon Dec 8 11:24:35 UTC 2025 on atb-devel-224
commit f2ea2ead517f413d1e848abca279aa0ddc9ae664
Author: Ralph Boehme <[email protected]>
Date: Thu Dec 6 18:15:00 2018 +0100
smbd: set fsp->is_sparse in vfs_default_durable_reconnect()
Signed-off-by: Ralph Boehme <[email protected]>
Reviewed-by: Volker Lendecke <[email protected]>
commit e3874ec57fbb07b1169e6d9706db3a49ac6e7a87
Author: Ralph Boehme <[email protected]>
Date: Sun Nov 30 17:12:09 2025 +0100
smbd: remove redundant initialisation
cookie.allow_reconnect is already set to false by ZERO_STRUCT(cookie).
Signed-off-by: Ralph Boehme <[email protected]>
Reviewed-by: Volker Lendecke <[email protected]>
commit 331fd3d49f8b35b7e25df2d2f94893c69a9e5d1e
Author: Ralph Boehme <[email protected]>
Date: Tue Oct 28 12:44:56 2025 +0100
smbd: pass fsp to vfs_default_durable_reconnect_check_stat()
No change in behaviour.
Signed-off-by: Ralph Boehme <[email protected]>
Reviewed-by: Volker Lendecke <[email protected]>
commit dccff4b290ac05a74e79a13a1117a33f7d06110f
Author: Ralph Boehme <[email protected]>
Date: Mon Oct 27 15:53:46 2025 +0100
s3/locking: simplify reset_share_mode_entry()
Since 3df388b8f148c00a3ef331d393cea976fb9340b3 we're using the open
global_id as
share_file_id in the share_mode_entry and we're also not changing it in
mark_share_mode_disconnected().
Iow, old_share_file_id is equal to new_share_file_id which and both are
equal to
open_global_id.
Hence replace the two old_share_file_id and new_share_file_id arguments
with the
single open_global_id argument.
Signed-off-by: Ralph Boehme <[email protected]>
Reviewed-by: Volker Lendecke <[email protected]>
-----------------------------------------------------------------------
Summary of changes:
source3/locking/share_mode_lock.c | 22 ++--
source3/locking/share_mode_lock.h | 5 +-
source3/smbd/durable.c | 44 ++++----
source4/torture/smb2/lease.c | 224 ++++++++++++++++++++++++++++++++++++++
4 files changed, 258 insertions(+), 37 deletions(-)
Changeset truncated at 500 lines:
diff --git a/source3/locking/share_mode_lock.c
b/source3/locking/share_mode_lock.c
index 66a7c1b6c04..66af151c7f5 100644
--- a/source3/locking/share_mode_lock.c
+++ b/source3/locking/share_mode_lock.c
@@ -2741,8 +2741,7 @@ bool mark_share_mode_disconnected(struct share_mode_lock
*lck,
messaging_server_id(fsp->conn->sconn->msg_ctx),
fsp->op->global->open_persistent_id,
disconnected_pid,
- UINT64_MAX,
- fsp->op->global->open_persistent_id);
+ UINT64_MAX);
return ok;
}
@@ -2750,10 +2749,9 @@ bool mark_share_mode_disconnected(struct share_mode_lock
*lck,
bool reset_share_mode_entry(
struct share_mode_lock *lck,
struct server_id old_pid,
- uint64_t old_share_file_id,
+ uint64_t open_global_id,
struct server_id new_pid,
- uint64_t new_mid,
- uint64_t new_share_file_id)
+ uint64_t new_mid)
{
struct file_id id = share_mode_lock_file_id(lck);
struct share_mode_data *d = NULL;
@@ -2801,7 +2799,7 @@ bool reset_share_mode_entry(
ltdb->share_entries,
ltdb->num_share_entries,
new_pid,
- new_share_file_id,
+ open_global_id,
&e,
&found);
if (found) {
@@ -2810,7 +2808,7 @@ bool reset_share_mode_entry(
file_id_str_buf(id, &id_buf),
ltdb->num_share_entries,
server_id_str_buf(new_pid, &pid_buf2),
- new_share_file_id);
+ open_global_id);
goto done;
}
@@ -2818,7 +2816,7 @@ bool reset_share_mode_entry(
ltdb->share_entries,
ltdb->num_share_entries,
old_pid,
- old_share_file_id,
+ open_global_id,
&e,
&found);
if (!found) {
@@ -2827,7 +2825,7 @@ bool reset_share_mode_entry(
file_id_str_buf(id, &id_buf),
ltdb->num_share_entries,
server_id_str_buf(old_pid, &pid_buf1),
- old_share_file_id);
+ open_global_id);
goto done;
}
DBG_DEBUG("%s - num_share_modes=%zu "
@@ -2836,17 +2834,17 @@ bool reset_share_mode_entry(
file_id_str_buf(id, &id_buf),
ltdb->num_share_entries,
server_id_str_buf(old_pid, &pid_buf1),
- old_share_file_id,
+ open_global_id,
old_idx,
server_id_str_buf(new_pid, &pid_buf2),
- new_share_file_id,
+ open_global_id,
new_idx);
e.pid = new_pid;
if (new_mid != UINT64_MAX) {
e.op_mid = new_mid;
}
- e.share_file_id = new_share_file_id;
+ e.share_file_id = open_global_id;
ok = share_mode_entry_put(&e, &e_buf);
if (!ok) {
diff --git a/source3/locking/share_mode_lock.h
b/source3/locking/share_mode_lock.h
index 82668cd977a..b4f2848d81b 100644
--- a/source3/locking/share_mode_lock.h
+++ b/source3/locking/share_mode_lock.h
@@ -61,10 +61,9 @@ bool set_share_mode(
bool reset_share_mode_entry(
struct share_mode_lock *lck,
struct server_id old_pid,
- uint64_t old_share_file_id,
+ uint64_t open_global_id,
struct server_id new_pid,
- uint64_t new_mid,
- uint64_t new_share_file_id);
+ uint64_t new_mid);
bool mark_share_mode_disconnected(
struct share_mode_lock *lck, struct files_struct *fsp);
diff --git a/source3/smbd/durable.c b/source3/smbd/durable.c
index fe6dd00b64a..108e9642287 100644
--- a/source3/smbd/durable.c
+++ b/source3/smbd/durable.c
@@ -92,7 +92,6 @@ NTSTATUS vfs_default_durable_cookie(struct files_struct *fsp,
}
ZERO_STRUCT(cookie);
- cookie.allow_reconnect = false;
cookie.id = fsp->file_id;
cookie.servicepath = conn->connectpath;
cookie.base_name = fsp->fsp_name->base_name;
@@ -311,9 +310,9 @@ NTSTATUS vfs_default_durable_disconnect(struct files_struct
*fsp,
*/
static bool vfs_default_durable_reconnect_check_stat(
struct vfs_default_durable_stat *cookie_st,
- SMB_STRUCT_STAT *fsp_st,
- const char *name)
+ struct files_struct *fsp)
{
+ SMB_STRUCT_STAT *fsp_st = &fsp->fsp_name->st;
int ret;
if (cookie_st->st_ex_mode != fsp_st->st_ex_mode) {
@@ -321,7 +320,7 @@ static bool vfs_default_durable_reconnect_check_stat(
"stat_ex.%s differs: "
"cookie:%llu != stat:%llu, "
"denying durable reconnect\n",
- name,
+ fsp_str_dbg(fsp),
"st_ex_mode",
(unsigned long long)cookie_st->st_ex_mode,
(unsigned long long)fsp_st->st_ex_mode));
@@ -333,7 +332,7 @@ static bool vfs_default_durable_reconnect_check_stat(
"stat_ex.%s differs: "
"cookie:%llu != stat:%llu, "
"denying durable reconnect\n",
- name,
+ fsp_str_dbg(fsp),
"st_ex_nlink",
(unsigned long long)cookie_st->st_ex_nlink,
(unsigned long long)fsp_st->st_ex_nlink));
@@ -345,7 +344,7 @@ static bool vfs_default_durable_reconnect_check_stat(
"stat_ex.%s differs: "
"cookie:%llu != stat:%llu, "
"denying durable reconnect\n",
- name,
+ fsp_str_dbg(fsp),
"st_ex_uid",
(unsigned long long)cookie_st->st_ex_uid,
(unsigned long long)fsp_st->st_ex_uid));
@@ -357,7 +356,7 @@ static bool vfs_default_durable_reconnect_check_stat(
"stat_ex.%s differs: "
"cookie:%llu != stat:%llu, "
"denying durable reconnect\n",
- name,
+ fsp_str_dbg(fsp),
"st_ex_gid",
(unsigned long long)cookie_st->st_ex_gid,
(unsigned long long)fsp_st->st_ex_gid));
@@ -369,7 +368,7 @@ static bool vfs_default_durable_reconnect_check_stat(
"stat_ex.%s differs: "
"cookie:%llu != stat:%llu, "
"denying durable reconnect\n",
- name,
+ fsp_str_dbg(fsp),
"st_ex_rdev",
(unsigned long long)cookie_st->st_ex_rdev,
(unsigned long long)fsp_st->st_ex_rdev));
@@ -381,7 +380,7 @@ static bool vfs_default_durable_reconnect_check_stat(
"stat_ex.%s differs: "
"cookie:%llu != stat:%llu, "
"denying durable reconnect\n",
- name,
+ fsp_str_dbg(fsp),
"st_ex_size",
(unsigned long long)cookie_st->st_ex_size,
(unsigned long long)fsp_st->st_ex_size));
@@ -399,7 +398,7 @@ static bool vfs_default_durable_reconnect_check_stat(
"stat_ex.%s differs: "
"cookie:'%s' != stat:'%s', "
"denying durable reconnect\n",
- name,
+ fsp_str_dbg(fsp),
"st_ex_atime",
timeval_string(talloc_tos(), &tc, true),
timeval_string(talloc_tos(), &ts, true)));
@@ -417,7 +416,7 @@ static bool vfs_default_durable_reconnect_check_stat(
"stat_ex.%s differs: "
"cookie:'%s' != stat:'%s', "
"denying durable reconnect\n",
- name,
+ fsp_str_dbg(fsp),
"st_ex_mtime",
timeval_string(talloc_tos(), &tc, true),
timeval_string(talloc_tos(), &ts, true)));
@@ -435,7 +434,7 @@ static bool vfs_default_durable_reconnect_check_stat(
"stat_ex.%s differs: "
"cookie:'%s' != stat:'%s', "
"denying durable reconnect\n",
- name,
+ fsp_str_dbg(fsp),
"st_ex_ctime",
timeval_string(talloc_tos(), &tc, true),
timeval_string(talloc_tos(), &ts, true)));
@@ -453,7 +452,7 @@ static bool vfs_default_durable_reconnect_check_stat(
"stat_ex.%s differs: "
"cookie:'%s' != stat:'%s', "
"denying durable reconnect\n",
- name,
+ fsp_str_dbg(fsp),
"st_ex_btime",
timeval_string(talloc_tos(), &tc, true),
timeval_string(talloc_tos(), &ts, true)));
@@ -465,7 +464,7 @@ static bool vfs_default_durable_reconnect_check_stat(
"stat_ex.%s differs: "
"cookie:%llu != stat:%llu, "
"denying durable reconnect\n",
- name,
+ fsp_str_dbg(fsp),
"st_ex_calculated_birthtime",
(unsigned long long)cookie_st->st_ex_iflags,
(unsigned long long)fsp_st->st_ex_iflags));
@@ -477,7 +476,7 @@ static bool vfs_default_durable_reconnect_check_stat(
"stat_ex.%s differs: "
"cookie:%llu != stat:%llu, "
"denying durable reconnect\n",
- name,
+ fsp_str_dbg(fsp),
"st_ex_blksize",
(unsigned long long)cookie_st->st_ex_blksize,
(unsigned long long)fsp_st->st_ex_blksize));
@@ -489,7 +488,7 @@ static bool vfs_default_durable_reconnect_check_stat(
"stat_ex.%s differs: "
"cookie:%llu != stat:%llu, "
"denying durable reconnect\n",
- name,
+ fsp_str_dbg(fsp),
"st_ex_blocks",
(unsigned long long)cookie_st->st_ex_blocks,
(unsigned long long)fsp_st->st_ex_blocks));
@@ -501,7 +500,7 @@ static bool vfs_default_durable_reconnect_check_stat(
"stat_ex.%s differs: "
"cookie:%"PRIu32" != stat:%"PRIu32", "
"denying durable reconnect\n",
- name,
+ fsp_str_dbg(fsp),
"st_ex_flags",
cookie_st->st_ex_flags,
fsp_st->st_ex_flags);
@@ -566,6 +565,7 @@ static void vfs_default_durable_reconnect_fn(struct
share_mode_lock *lck,
struct vfs_open_how how = { .flags = 0, };
struct file_id file_id;
bool have_share_mode_entry = false;
+ uint32_t dosmode;
int ret;
bool ok;
@@ -670,8 +670,7 @@ static void vfs_default_durable_reconnect_fn(struct
share_mode_lock *lck,
e.pid,
e.share_file_id,
messaging_server_id(fsp->conn->sconn->msg_ctx),
- state->smb1req->mid,
- fh_get_gen_id(fsp->fh));
+ state->smb1req->mid);
if (!ok) {
DBG_DEBUG("Could not set new share_mode_entry values\n");
state->status = NT_STATUS_INTERNAL_ERROR;
@@ -735,11 +734,12 @@ static void vfs_default_durable_reconnect_fn(struct
share_mode_lock *lck,
goto fail;
}
- (void)fdos_mode(fsp);
+ dosmode = fdos_mode(fsp);
+ fsp->fsp_flags.is_sparse = (dosmode & FILE_ATTRIBUTE_SPARSE);
+ fsp->fsp_flags.is_sparse |= e.flags & SHARE_ENTRY_FLAG_POSIX_OPEN;
ok = vfs_default_durable_reconnect_check_stat(&state->cookie.stat_info,
- &fsp->fsp_name->st,
- fsp_str_dbg(fsp));
+ fsp);
if (!ok) {
state->status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
goto fail;
diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c
index 5b915907523..5ee82033219 100644
--- a/source4/torture/smb2/lease.c
+++ b/source4/torture/smb2/lease.c
@@ -6188,6 +6188,229 @@ done:
return ret;
}
+/*
+ * Verify a redispatched deferred open doesn't send a lease break to the client
+ *
+ * 1. Client 1: Open file with RH lease
+ * 2. Client 1: Second open with RH lease, different lease key
+ * 3. Client 2: Try to open file with incompatible mode, triggers sharing
+ * violation, triggers H lease breaks and gets deferred.
+ * 4. Client 1: close handle 1, this will trigger a redispatch of the deferred
+ * open from 3.
+ * 5. Check the client doesn't get a lease break.
+ */
+static bool test_two_leases(struct torture_context *tctx,
+ struct smb2_tree *_tree)
+{
+ TALLOC_CTX *mem_ctx = talloc_new(tctx);
+ struct smbcli_options options = _tree->session->transport->options;
+ struct smb2_tree *tree1 = NULL;
+ struct smb2_tree *tree2 = NULL;
+ struct smb2_request *req = NULL;
+ struct smb2_create c;
+ struct smb2_lease ls;
+ uint64_t broken_ls_key;
+ struct smb2_handle h1 = {};
+ struct smb2_handle h2 = {};
+ struct smb2_handle ch1 = {};
+ struct smb2_handle ch2 = {};
+ char *fname = NULL;
+ int rc;
+ NTSTATUS status;
+ bool ret = true;
+
+ fname = talloc_asprintf(mem_ctx, "lease_break-%ld.dat", random());
+ torture_assert_not_null_goto(tctx, fname, ret, done,
+ "talloc_asprintf failed\n");
+
+ options.client_guid = GUID_random();
+ ret = torture_smb2_connection_ext(tctx, 0, &options, &tree1);
+ torture_assert_goto(tctx, ret, ret, done, "torture_smb2_connection_ext
failed\n");
+
+ options.client_guid = GUID_random();
+ ret = torture_smb2_connection_ext(tctx, 0, &options, &tree2);
+ torture_assert_goto(tctx, ret, ret, done,
+ "torture_smb2_connection_ext failed\n");
+
+ torture_reset_lease_break_info(tctx, &lease_break_info);
+ lease_break_info.lease_skip_ack = true;
+ tree1->session->transport->lease.handler = torture_lease_handler;
+ tree1->session->transport->lease.private_data = tree1;
+
+ status = torture_setup_simple_file(tctx, tree1, fname);
+ torture_assert_ntstatus_ok(tctx, status, "setup file failed\n");
+
+ /*
+ * First open with RH lease
+ */
+ smb2_lease_v2_create_share(&c,
+ &ls,
+ false,
+ fname,
+ smb2_util_share_access("R"),
+ 1,
+ NULL,
+ smb2_util_lease_state("RH"),
+ 0);
+ c.in.durable_open_v2 = true;
+ c.in.create_guid = GUID_random();
+ c.in.desired_access = SEC_RIGHTS_FILE_READ;
+
+ status = smb2_create(tree1, mem_ctx, &c);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "smb2_create failed\n");
+ h1 = c.out.file.handle;
+
+ torture_assert_int_equal_goto(
+ tctx,
+ c.out.oplock_level,
+ SMB2_OPLOCK_LEVEL_LEASE,
+ ret, done,
+ "Bad lease level\n");
+ torture_assert_int_equal_goto(
+ tctx,
+ c.out.lease_response_v2.lease_state,
+ smb2_util_lease_state("RH"),
+ ret, done,
+ "Bad lease level\n");
+
+ /*
+ * Second open with RH lease
+ */
+ smb2_lease_v2_create_share(&c,
+ &ls,
+ false,
+ fname,
+ smb2_util_share_access("R"),
+ 2,
+ NULL,
+ smb2_util_lease_state("RH"),
+ 0);
+ c.in.durable_open_v2 = true;
+ c.in.create_guid = GUID_random();
+ c.in.desired_access = SEC_RIGHTS_FILE_READ;
+
+ status = smb2_create(tree1, mem_ctx, &c);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "smb2_create failed\n");
+ h2 = c.out.file.handle;
+
+ torture_assert_int_equal_goto(
+ tctx,
+ c.out.oplock_level,
+ SMB2_OPLOCK_LEVEL_LEASE,
+ ret, done,
+ "Bad lease level\n");
+ torture_assert_int_equal_goto(
+ tctx,
+ c.out.lease_response_v2.lease_state,
+ smb2_util_lease_state("RH"),
+ ret, done,
+ "Bad lease level\n");
+
+ /*
+ * Third open triggering a sharing violation and two H-lease breaks
+ */
+
+ smb2_lease_v2_create_share(&c,
+ &ls,
+ false,
+ fname,
+ smb2_util_share_access(""),
+ 3,
+ NULL,
+ smb2_util_lease_state("RH"),
+ 0);
+ c.in.durable_open_v2 = true;
+ c.in.create_guid = GUID_random();
+
+ req = smb2_create_send(tree2, &c);
+ torture_assert_not_null_goto(tctx, req, ret, done,
+ "smb2_create_send failed\n");
+
+ while (!req->cancel.can_cancel &&
+ (req->state < SMB2_REQUEST_DONE))
+ {
+ rc = tevent_loop_once(req->transport->ev);
+ torture_assert_goto(tctx, rc == 0, ret, done,
+ "tevent_loop_once failed\n");
+ }
+ torture_assert_goto(tctx, req->state < SMB2_REQUEST_DONE,
+ ret, done,
+ "Expected async interim response\n");
+
+ /* Wait for first break */
+ torture_wait_for_lease_break(tctx);
+
+ if (lease_break_info.lease_break.current_lease.lease_key.data[0] == 1) {
+ broken_ls_key = 1;
+ ch1 = h1;
+ ch2 = h2;
+ ZERO_STRUCT(h1);
+ ZERO_STRUCT(h2);
+ } else {
+ broken_ls_key = 2;
+ ch1 = h2;
+ ch2 = h1;
+ ZERO_STRUCT(h1);
+ ZERO_STRUCT(h2);
+ }
+
+ /* Wait for second break */
+ torture_wait_for_lease_break(tctx);
+
+ CHECK_VAL(lease_break_info.count, 2);
+ lease_break_info.count = 1; /* trick CHECK_BREAK_INFO_V2_NOWAIT() */
+ CHECK_BREAK_INFO_V2_NOWAIT(tree1->session->transport,
+ "RH",
+ "R",
+ broken_ls_key,
+ 2);
+
+ torture_reset_lease_break_info(tctx, &lease_break_info);
+ lease_break_info.lease_skip_ack = true;
+
+ status = smb2_util_close(tree1, ch1);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "smb2_create failed\n");
+ ZERO_STRUCT(ch1);
+
+ CHECK_NO_BREAK(tctx);
+
+ status = smb2_util_close(tree1, ch2);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "smb2_create failed\n");
+ ZERO_STRUCT(ch2);
+
+ status = smb2_create_recv(req, tctx, &c);
+ torture_assert_ntstatus_equal_goto(
+ tctx, status, NT_STATUS_OK,
+ ret, done, "smb2_create failed\n");
+
+ status = smb2_util_close(tree2, c.out.file.handle);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "smb2_create failed\n");
+
+done:
+ if (!smb2_util_handle_empty(h1)) {
+ smb2_util_close(tree1, h1);
+ }
--
Samba Shared Repository