From: Ralph Boehme Date: Sat, 26 Jul 2025 13:27:41 +0000 (+0200) Subject: net: handle replay records in `net serverid wipedbs` X-Git-Tag: tdb-1.4.14~9 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ebf53bcaa893914c49417d382a6b6384cbbaee50;p=thirdparty%2Fsamba.git net: handle replay records in `net serverid wipedbs` Signed-off-by: Ralph Boehme Reviewed-by: Stefan Metzmacher --- diff --git a/source3/utils/net_serverid.c b/source3/utils/net_serverid.c index 6b575b806cc..e704829d645 100644 --- a/source3/utils/net_serverid.c +++ b/source3/utils/net_serverid.c @@ -29,6 +29,7 @@ #include "smbd/smbXsrv_open.h" #include "util_tdb.h" #include "librpc/gen_ndr/ndr_open_files.h" +#include "librpc/gen_ndr/ndr_smbXsrv.h" struct wipedbs_record_marker { struct wipedbs_record_marker *prev, *next; @@ -47,6 +48,8 @@ struct wipedbs_server_data { struct wipedbs_state { struct db_context *id2server_data; + struct db_context *open_records_db; + struct db_context *replay_records_db; struct { struct { int total; @@ -58,7 +61,7 @@ struct wipedbs_state { int disconnected; int todelete; int failure; - } session, tcon, open; + } session, tcon, open, replay; int open_timed_out; } stat; struct server_id *server_ids; @@ -233,9 +236,63 @@ done: return ret; } +static int wipedbs_traverse_open_replay(struct db_record *db_rec, + TDB_DATA *rc_open_global_key, + struct wipedbs_state *state) +{ + struct wipedbs_record_marker *rec = NULL; + TDB_DATA tmp; + NTSTATUS status; + int ret = -1; + + state->stat.replay.total++; + + rec = talloc_zero(state, struct wipedbs_record_marker); + if (rec == NULL) { + DBG_ERR("Out of memory!\n"); + goto done; + } + + tmp = dbwrap_record_get_key(db_rec); + rec->key = tdb_data_talloc_copy(rec, tmp); + tmp = dbwrap_record_get_value(db_rec); + rec->val = tdb_data_talloc_copy(rec, tmp); + + rec->desc = talloc_asprintf( + rec, + "replay rec [global key: %s]", + hex_encode_talloc(rec, + rc_open_global_key->dptr, + rc_open_global_key->dsize)); + + if ((rec->key.dptr == NULL) || (rec->val.dptr == NULL) || + (rec->desc == NULL)) + { + DEBUG(0, ("Out of memory!\n")); + goto done; + } + + tmp = make_tdb_data((const void*)&rec, sizeof(rec)); + + status = dbwrap_store(state->replay_records_db, + rec->key, + tmp, + TDB_INSERT); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("Failed to store replay record for %s: %s\n", + rec->desc, nt_errstr(status)); + ret = -1; + goto done; + } + ret = 0; + +done: + return ret; +} + static int wipedbs_traverse_open(struct db_record *db_rec, struct smbXsrv_open_global0 *open, - TDB_DATA *open_global_key, + TDB_DATA *rc_open_global_key, void *wipedbs_state) { struct wipedbs_state *state = @@ -243,9 +300,17 @@ static int wipedbs_traverse_open(struct db_record *db_rec, struct wipedbs_state); struct wipedbs_server_data *sd; struct wipedbs_record_marker *rec; + TDB_DATA val; TDB_DATA tmp; + NTSTATUS status; int ret = -1; + if (rc_open_global_key != NULL) { + return wipedbs_traverse_open_replay(db_rec, + rc_open_global_key, + state); + } + state->stat.open.total++; sd = get_server_data(state, &open->server_id); @@ -324,6 +389,16 @@ static int wipedbs_traverse_open(struct db_record *db_rec, state->open_db = dbwrap_record_get_db(db_rec); DLIST_ADD(sd->open_records, rec); + + val = make_tdb_data((const void*)&rec, sizeof(rec)); + + status = dbwrap_store(state->open_records_db, rec->key, val, TDB_INSERT); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("Failed to store server entry for %s: %s\n", + rec->desc, nt_errstr(status)); + ret = -1; + goto done; + } ret = 0; done: @@ -441,6 +516,7 @@ done: } struct wipedbs_delete_state { + struct wipedbs_state *state; struct wipedbs_record_marker *cur; bool verbose; bool dry_run; @@ -471,58 +547,108 @@ static void wipedbs_delete_fn( if (!state->dry_run) { status = dbwrap_record_delete(rec); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("Failed to delete record <%s> from %s: %s\n", + cur->desc, + dbwrap_name(db), + nt_errstr(status)); + return; + } } - if (!NT_STATUS_IS_OK(status)) { - DBG_ERR("Failed to delete record <%s> from %s: %s\n", - cur->desc, - dbwrap_name(db), - nt_errstr(status)); + state->num += 1; +} + +static void wipedbs_delete_replay_record( + struct wipedbs_delete_state *delete_state) +{ + TALLOC_CTX *mem_ctx = NULL; + struct wipedbs_record_marker *cur = delete_state->cur; + DATA_BLOB blob = data_blob_const(cur->val.dptr, cur->val.dsize); + struct smbXsrv_open_globalB global_blob; + enum ndr_err_code ndr_err; + NTSTATUS status; + + /* Deleting open records, also remove associated replay record */ + + mem_ctx = talloc_new(delete_state->state); + if (mem_ctx == NULL) { + DBG_ERR("talloc_new failed\n"); return; } - state->num += 1; + ndr_err = ndr_pull_struct_blob(&blob, mem_ctx, &global_blob, + (ndr_pull_flags_fn_t)ndr_pull_smbXsrv_open_globalB); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + DBG_ERR("Invalid record in smbXsrv_open_global.tdb:" + "key '%s' ndr_pull_struct_blob - %s\n%s\n", + tdb_data_dbg(cur->key), + ndr_errstr(ndr_err), + tdb_data_dbg(cur->val)); + return; + } + + if (global_blob.version != SMBXSRV_VERSION_0) { + DBG_ERR("Unsupported open record version %u\n", + global_blob.version); + TALLOC_FREE(mem_ctx); + return; + } + + status = smbXsrv_replay_cleanup(&global_blob.info.info0->client_guid, + &global_blob.info.info0->create_guid); + TALLOC_FREE(mem_ctx); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("Replay record cleanup failed\n"); + return; + } } -static int wipedbs_delete_records(struct db_context *db, +static int wipedbs_delete_records(struct wipedbs_state *state, + struct db_context *db, struct wipedbs_record_marker *records, bool dry_run, bool verbose, int *count) { - struct wipedbs_delete_state state = { - .verbose = verbose, .dry_run = dry_run, + struct wipedbs_delete_state delete_state = { + .state = state, .verbose = verbose, .dry_run = dry_run, }; if (db == NULL) { return 0; } - for (state.cur = records; - state.cur != NULL; - state.cur = state.cur->next) { + for (delete_state.cur = records; + delete_state.cur != NULL; + delete_state.cur = delete_state.cur->next) { NTSTATUS status = dbwrap_do_locked( - db, state.cur->key, wipedbs_delete_fn, &state); + db, delete_state.cur->key, wipedbs_delete_fn, &delete_state); if (!NT_STATUS_IS_OK(status)) { DBG_ERR("dbwrap_do_locked failed for record <%s> " "from %s\n", - state.cur->desc, + delete_state.cur->desc, dbwrap_name(db)); } + + if (state->open_db != db) { + continue; + } + wipedbs_delete_replay_record(&delete_state); } if (verbose) { d_printf("Deleted %zu of %zu records from %s\n", - state.num, - state.total, + delete_state.num, + delete_state.total, dbwrap_name(db)); } if (count) { - *count += state.total; + *count += delete_state.total; } - return state.total - state.num; + return delete_state.total - delete_state.num; } static int wipedbs_traverse_server_data(struct db_record *rec, @@ -547,17 +673,17 @@ static int wipedbs_traverse_server_data(struct db_record *rec, return 0; } - ret = wipedbs_delete_records(state->session_db, sd->session_records, + ret = wipedbs_delete_records(state, state->session_db, sd->session_records, dry_run, state->verbose, &state->stat.session.todelete); state->stat.session.failure += ret; - ret = wipedbs_delete_records(state->tcon_db, sd->tcon_records, + ret = wipedbs_delete_records(state, state->tcon_db, sd->tcon_records, dry_run, state->verbose, &state->stat.tcon.todelete); state->stat.tcon.failure += ret; - ret = wipedbs_delete_records(state->open_db, sd->open_records, + ret = wipedbs_delete_records(state, state->open_db, sd->open_records, dry_run, state->verbose, &state->stat.open.todelete); state->stat.open.failure += ret; @@ -565,6 +691,49 @@ static int wipedbs_traverse_server_data(struct db_record *rec, return 0; } +static int wipedbs_traverse_replay_records(struct db_record *rec, + void *wipedbs_state) +{ + struct wipedbs_state *state = talloc_get_type_abort( + wipedbs_state, struct wipedbs_state); + bool dry_run = state->testmode; + TDB_DATA key = dbwrap_record_get_key(rec); + TDB_DATA val = dbwrap_record_get_value(rec); + struct wipedbs_record_marker *m = talloc_get_type_abort( + *(void **)val.dptr, struct wipedbs_record_marker); + TDB_DATA open_key; + bool exists; + NTSTATUS status; + + exists = dbwrap_exists(state->open_db, key); + if (!exists) { + /* + * Already properly removed when removing the + * open record. + */ + return 0; + } + + open_key = m->val; + exists = dbwrap_exists(state->open_records_db, open_key); + if (exists) { + return 0; + } + + state->stat.replay.todelete++; + + if (!dry_run) { + status = dbwrap_purge(state->open_db, key); + if (!NT_STATUS_IS_OK(status)) { + state->stat.replay.failure++; + DBG_ERR("dbwrap_purge failed\n"); + return -1; + } + } + + return 0; +} + static int net_serverid_wipedbs(struct net_context *c, int argc, const char **argv) { @@ -593,6 +762,18 @@ static int net_serverid_wipedbs(struct net_context *c, int argc, goto done; } + state->open_records_db = db_open_rbt(state); + if (state->open_records_db == NULL) { + DBG_ERR("Failed to open temporary database\n"); + goto done; + } + + state->replay_records_db = db_open_rbt(state); + if (state->replay_records_db == NULL) { + DBG_ERR("Failed to open temporary database\n"); + goto done; + } + status = smbXsrv_session_global_traverse(wipedbs_traverse_sessions, state); if (!NT_STATUS_IS_OK(status)) { @@ -622,6 +803,14 @@ static int net_serverid_wipedbs(struct net_context *c, int argc, goto done; } + status = dbwrap_traverse_read(state->replay_records_db, + wipedbs_traverse_replay_records, + state, NULL); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("Failed to replay records: %s\n", nt_errstr(status)); + goto done; + } + d_printf("Found %d serverids, %d alive and %d disconnected\n", state->stat.server.total, state->stat.server.existing, @@ -650,6 +839,13 @@ static int net_serverid_wipedbs(struct net_context *c, int argc, state->stat.open.todelete - state->stat.open.failure, state->stat.open.todelete); + d_printf("Found %d replay records, %d alive, " + "cleaned up %d entries, %d failed\n", + state->stat.replay.total, + state->stat.replay.total - state->stat.replay.todelete, + state->stat.replay.todelete, + state->stat.replay.failure); + ret = 0; done: talloc_free(state);