]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
smbd: make create-replay cache disk backed
authorRalph Boehme <slow@samba.org>
Sat, 26 Jul 2025 13:05:45 +0000 (15:05 +0200)
committerRalph Boehme <slow@samba.org>
Tue, 5 Aug 2025 14:52:34 +0000 (14:52 +0000)
Signed-off-by: Ralph Boehme <slow@samba.org>
Reviewed-by: Stefan Metzmacher <metze@samba.org>
selftest/knownfail.d/samba3.smb2.replay [deleted file]
source3/smbd/smb2_create.c
source3/smbd/smbXsrv_open.c
source3/smbd/smbXsrv_open.h

diff --git a/selftest/knownfail.d/samba3.smb2.replay b/selftest/knownfail.d/samba3.smb2.replay
deleted file mode 100644 (file)
index e2c3d90..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-^samba3.smb2.replay.durable-reconnect-replay1\(nt4_dc\)
-^samba3.smb2.replay.durable-reconnect-replay3\(nt4_dc\)
-^samba3.smb2.replay.replay-twice-durable\(nt4_dc\)
index f6b49a0f96bbb4f312a82b87c83f99422a63bd54..f8f63e85287c56e50266eb3ba1a3bd4342b2c881 100644 (file)
@@ -682,6 +682,7 @@ struct smbd_smb2_create_state {
        struct deferred_open_record *open_rec;
        files_struct *result;
        bool replay_operation;
+       bool replay_reconnect;
        uint8_t in_oplock_level;
        uint32_t in_create_disposition;
        uint32_t in_create_options;
@@ -1078,7 +1079,8 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
         * there is nothing else to do), durable_reconnect or
         * new open.
         */
-       if (state->replay_operation) {
+       if (state->replay_operation && !state->replay_reconnect) {
+               SMB_ASSERT(state->op != NULL);
                state->result = state->op->compat;
                state->result->op = state->op;
                state->update_open = false;
@@ -1093,11 +1095,17 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
                return req;
        }
 
-       if (state->do_durable_reconnect) {
+       if (state->do_durable_reconnect || state->replay_reconnect) {
                DATA_BLOB new_cookie = data_blob_null;
                NTTIME now = timeval_to_nttime(&smb2req->request_time);
                const struct smb2_lease_key *lease_key = NULL;
 
+               /*
+                * Assert a replay on a multichannel connection doesn't end up
+                * here.
+                */
+               SMB_ASSERT(state->op == NULL);
+
                if (state->lease_ptr != NULL) {
                        lease_key = &state->lease_ptr->lease_key;
                }
@@ -1168,7 +1176,11 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
 
                state->update_open = true;
 
-               state->info = FILE_WAS_OPENED;
+               if (!state->replay_reconnect) {
+                       state->info = FILE_WAS_OPENED;
+               } else {
+                       state->info = state->op->global->create_action;
+               }
 
                smbd_smb2_create_after_exec(req);
                if (!tevent_req_is_in_progress(req)) {
@@ -1457,6 +1469,7 @@ static void smbd_smb2_cc_before_exec_dhc2q(struct tevent_req *req)
                                                  *state->create_guid,
                                                  state->fname,
                                                  now,
+                                                 &state->persistent_id,
                                                  &state->op);
        if (NT_STATUS_EQUAL(status, NT_STATUS_FWP_RESERVED)) {
                /*
@@ -1475,6 +1488,8 @@ static void smbd_smb2_cc_before_exec_dhc2q(struct tevent_req *req)
        } else if (NT_STATUS_EQUAL(status, NT_STATUS_FILE_NOT_AVAILABLE)) {
                tevent_req_nterror(req, status);
                return;
+       } else if (NT_STATUS_EQUAL(status, NT_STATUS_HANDLE_NO_LONGER_VALID)) {
+               state->replay_reconnect = true;
        } else if (tevent_req_nterror(req, status)) {
                DBG_WARNING("smb2srv_open_lookup_replay_cache "
                            "failed: %s\n", nt_errstr(status));
@@ -1665,7 +1680,7 @@ static void smbd_smb2_create_before_exec(struct tevent_req *req)
                 * established open carries a lease with the
                 * same lease key.
                 */
-               if (state->replay_operation) {
+               if (state->replay_operation && !state->replay_reconnect) {
                        struct smb2_lease *op_ls =
                                &state->op->compat->lease->lease;
                        int op_oplock = state->op->compat->oplock_type;
index fabd8aff889e28312da0b8b8b1d287a4fe9831a8..275b332d005f8a67ec8f6c6191f51bb5f5c298e3 100644 (file)
@@ -40,7 +40,6 @@
 struct smbXsrv_open_table {
        struct {
                struct idr_context *idr;
-               struct db_context *replay_cache_db_ctx;
                uint32_t lowest_id;
                uint32_t highest_id;
                uint32_t max_opens;
@@ -138,11 +137,6 @@ static NTSTATUS smbXsrv_open_table_init(struct smbXsrv_connection *conn,
                TALLOC_FREE(table);
                return NT_STATUS_NO_MEMORY;
        }
-       table->local.replay_cache_db_ctx = db_open_rbt(table);
-       if (table->local.replay_cache_db_ctx == NULL) {
-               TALLOC_FREE(table);
-               return NT_STATUS_NO_MEMORY;
-       }
        table->local.lowest_id = lowest_id;
        table->local.highest_id = highest_id;
        table->local.max_opens = max_opens;
@@ -711,7 +705,7 @@ static TDB_DATA smbXsrv_open_replay_cache_key(
 static NTSTATUS smbXsrv_open_set_replay_cache(struct smbXsrv_open *op)
 {
        struct GUID_txt_buf buf;
-       struct db_context *db = op->table->local.replay_cache_db_ctx;
+       struct db_context *db = op->table->global.db_ctx;
        struct smbXsrv_open_global_key_buf open_key_buf;
        NTSTATUS status;
        struct smbXsrv_open_replay_cache_key_buf key_buf;
@@ -765,7 +759,7 @@ NTSTATUS smbXsrv_open_purge_replay_cache(struct smbXsrv_client *client,
                                            create_guid,
                                            &key_buf);
 
-       status = dbwrap_purge(client->open_table->local.replay_cache_db_ctx, key);
+       status = dbwrap_purge(client->open_table->global.db_ctx, key);
        return status;
 }
 
@@ -782,7 +776,7 @@ static NTSTATUS smbXsrv_open_clear_replay_cache(struct smbXsrv_open *op)
                return NT_STATUS_OK;
        }
 
-       db = op->table->local.replay_cache_db_ctx;
+       db = op->table->global.db_ctx;
 
        if (!(op->flags & SMBXSRV_OPEN_HAVE_REPLAY_CACHE)) {
                return NT_STATUS_OK;
@@ -930,12 +924,6 @@ NTSTATUS smbXsrv_open_close(struct smbXsrv_open *op, NTTIME now)
                global->open_global_id, &key_buf);
        int ret;
 
-       error = smbXsrv_open_clear_replay_cache(op);
-       if (!NT_STATUS_IS_OK(error)) {
-               DBG_ERR("smbXsrv_open_clear_replay_cache failed: %s\n",
-                       nt_errstr(error));
-       }
-
        if (op->table == NULL) {
                return error;
        }
@@ -963,6 +951,17 @@ NTSTATUS smbXsrv_open_close(struct smbXsrv_open *op, NTTIME now)
                error = state.status;
        }
 
+       if (!op->global->durable) {
+               /*
+                * If this is not a Durable Handle, remove the Replay-Cache entry.
+                */
+               error = smbXsrv_open_clear_replay_cache(op);
+               if (!NT_STATUS_IS_OK(error)) {
+                       DBG_ERR("smbXsrv_open_clear_replay_cache failed: %s\n",
+                               nt_errstr(error));
+               }
+       }
+
        ret = idr_remove(table->local.idr, op->local_id);
        SMB_ASSERT(ret == 0);
 
@@ -1125,12 +1124,13 @@ NTSTATUS smb2srv_open_lookup_replay_cache(struct smbXsrv_connection *conn,
                                          struct GUID create_guid,
                                          const char *name,
                                          NTTIME now,
+                                         uint64_t *persistent_id,
                                          struct smbXsrv_open **_open)
 {
        TALLOC_CTX *frame = talloc_stackframe();
        NTSTATUS status;
        struct smbXsrv_open_table *table = conn->client->open_table;
-       struct db_context *db = table->local.replay_cache_db_ctx;
+       struct db_context *db = table->global.db_ctx;
        struct GUID_txt_buf _create_guid_buf;
        const char *create_guid_str = GUID_buf_string(&create_guid, &_create_guid_buf);
        struct db_record *db_rec = NULL;
@@ -1238,9 +1238,10 @@ NTSTATUS smb2srv_open_lookup_replay_cache(struct smbXsrv_connection *conn,
                return NT_STATUS_OK;
        }
 
-       DBG_DEBUG("No local open\n");
+       DBG_DEBUG("No local open, triggering reconnect\n");
+       *persistent_id = open_global_id;
        TALLOC_FREE(frame);
-       return status;
+       return NT_STATUS_HANDLE_NO_LONGER_VALID;
 }
 
 struct smb2srv_open_recreate_state {
index d6d0ed35105021a3541c338e2d058b2c91e151fc..c09aa0d9b96c1f623962a8c0f27040261de96eb4 100644 (file)
@@ -58,6 +58,7 @@ NTSTATUS smb2srv_open_lookup_replay_cache(struct smbXsrv_connection *conn,
                                          struct GUID create_guid,
                                          const char *name,
                                          NTTIME now,
+                                         uint64_t *persistent_id,
                                          struct smbXsrv_open **_open);
 struct smb2_lease_key;
 NTSTATUS smb2srv_open_recreate(struct smbXsrv_connection *conn,