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

Reply via email to