diff options
author | Volker Lendecke <vl@samba.org> | 2023-01-04 12:18:44 +0100 |
---|---|---|
committer | Jeremy Allison <jra@samba.org> | 2023-01-10 01:23:38 +0000 |
commit | 8ee203467455da0b9f774acbcc44e9b58570d811 (patch) | |
tree | 409cc5f41da09f14ae00e3ede56ea4f1954deaa6 /source3 | |
parent | b73ecb28a7ac5996e1a8c455d15f41f59d9d8765 (diff) | |
download | samba-8ee203467455da0b9f774acbcc44e9b58570d811.tar.gz |
smbd: Use an idtree for local IDs
Volatile file handle IDs are purely per-process, in fact we used a
dbwrap_rbt for this. To get a unique ID we however have the
specialized idtree data structure, we don't need to repeat the
allocation algorithm that already exists there.
Signed-off-by: Volker Lendecke <vl@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
Autobuild-User(master): Jeremy Allison <jra@samba.org>
Autobuild-Date(master): Tue Jan 10 01:23:38 UTC 2023 on sn-devel-184
Diffstat (limited to 'source3')
-rw-r--r-- | source3/smbd/smbXsrv_open.c | 370 |
1 files changed, 44 insertions, 326 deletions
diff --git a/source3/smbd/smbXsrv_open.c b/source3/smbd/smbXsrv_open.c index 9c446a95499..c67556545be 100644 --- a/source3/smbd/smbXsrv_open.c +++ b/source3/smbd/smbXsrv_open.c @@ -33,10 +33,11 @@ #include "librpc/gen_ndr/ndr_smbXsrv.h" #include "serverid.h" #include "source3/include/util_tdb.h" +#include "lib/util/idtree_random.h" struct smbXsrv_open_table { struct { - struct db_context *db_ctx; + struct idr_context *idr; struct db_context *replay_cache_db_ctx; uint32_t lowest_id; uint32_t highest_id; @@ -124,35 +125,6 @@ static NTSTATUS smbXsrv_open_global_key_to_id(TDB_DATA key, uint32_t *id) } #endif -#define SMBXSRV_OPEN_LOCAL_TDB_KEY_SIZE sizeof(uint32_t) - -static TDB_DATA smbXsrv_open_local_id_to_key(uint32_t id, - uint8_t *key_buf) -{ - TDB_DATA key; - - RSIVAL(key_buf, 0, id); - - key = make_tdb_data(key_buf, SMBXSRV_OPEN_LOCAL_TDB_KEY_SIZE); - - return key; -} - -static NTSTATUS smbXsrv_open_local_key_to_id(TDB_DATA key, uint32_t *id) -{ - if (id == NULL) { - return NT_STATUS_INVALID_PARAMETER; - } - - if (key.dsize != SMBXSRV_OPEN_LOCAL_TDB_KEY_SIZE) { - return NT_STATUS_INTERNAL_DB_CORRUPTION; - } - - *id = RIVAL(key.dptr, 0); - - return NT_STATUS_OK; -} - static struct db_record *smbXsrv_open_global_fetch_locked( struct db_context *db, uint32_t id, @@ -174,27 +146,6 @@ static struct db_record *smbXsrv_open_global_fetch_locked( return rec; } -static struct db_record *smbXsrv_open_local_fetch_locked( - struct db_context *db, - uint32_t id, - TALLOC_CTX *mem_ctx) -{ - TDB_DATA key; - uint8_t key_buf[SMBXSRV_OPEN_LOCAL_TDB_KEY_SIZE]; - struct db_record *rec = NULL; - - key = smbXsrv_open_local_id_to_key(id, key_buf); - - rec = dbwrap_fetch_locked(db, mem_ctx, key); - - if (rec == NULL) { - DBG_DEBUG("Failed to lock local id 0x%08x, key '%s'\n", id, - tdb_data_dbg(key)); - } - - return rec; -} - static NTSTATUS smbXsrv_open_table_init(struct smbXsrv_connection *conn, uint32_t lowest_id, uint32_t highest_id, @@ -222,8 +173,8 @@ static NTSTATUS smbXsrv_open_table_init(struct smbXsrv_connection *conn, return NT_STATUS_NO_MEMORY; } - table->local.db_ctx = db_open_rbt(table); - if (table->local.db_ctx == NULL) { + table->local.idr = idr_init(table); + if (table->local.idr == NULL) { TALLOC_FREE(table); return NT_STATUS_NO_MEMORY; } @@ -248,204 +199,13 @@ static NTSTATUS smbXsrv_open_table_init(struct smbXsrv_connection *conn, return NT_STATUS_OK; } -struct smbXsrv_open_local_allocate_state { - const uint32_t lowest_id; - const uint32_t highest_id; - uint32_t last_id; - uint32_t useable_id; - NTSTATUS status; -}; - -static int smbXsrv_open_local_allocate_traverse(struct db_record *rec, - void *private_data) -{ - struct smbXsrv_open_local_allocate_state *state = - (struct smbXsrv_open_local_allocate_state *)private_data; - TDB_DATA key = dbwrap_record_get_key(rec); - uint32_t id = 0; - NTSTATUS status; - - status = smbXsrv_open_local_key_to_id(key, &id); - if (!NT_STATUS_IS_OK(status)) { - state->status = status; - return -1; - } - - if (id <= state->last_id) { - state->status = NT_STATUS_INTERNAL_DB_CORRUPTION; - return -1; - } - state->last_id = id; - - if (id > state->useable_id) { - state->status = NT_STATUS_OK; - return -1; - } - - if (state->useable_id == state->highest_id) { - state->status = NT_STATUS_INSUFFICIENT_RESOURCES; - return -1; - } - - state->useable_id +=1; - return 0; -} - -static NTSTATUS smbXsrv_open_local_allocate_id(struct db_context *db, - uint32_t lowest_id, - uint32_t highest_id, - TALLOC_CTX *mem_ctx, - struct db_record **_rec, - uint32_t *_id) -{ - struct smbXsrv_open_local_allocate_state state = { - .lowest_id = lowest_id, - .highest_id = highest_id, - .last_id = 0, - .useable_id = lowest_id, - .status = NT_STATUS_INTERNAL_ERROR, - }; - uint32_t i; - uint32_t range; - NTSTATUS status; - int count = 0; - - *_rec = NULL; - *_id = 0; - - if (lowest_id > highest_id) { - return NT_STATUS_INSUFFICIENT_RESOURCES; - } - - /* - * first we try randomly - */ - range = (highest_id - lowest_id) + 1; - - for (i = 0; i < (range / 2); i++) { - uint32_t id; - TDB_DATA val; - struct db_record *rec = NULL; - - id = generate_random() % range; - id += lowest_id; - - if (id < lowest_id) { - id = lowest_id; - } - if (id > highest_id) { - id = highest_id; - } - - rec = smbXsrv_open_local_fetch_locked(db, id, mem_ctx); - if (rec == NULL) { - return NT_STATUS_INSUFFICIENT_RESOURCES; - } - - val = dbwrap_record_get_value(rec); - if (val.dsize != 0) { - TALLOC_FREE(rec); - continue; - } - - *_rec = rec; - *_id = id; - return NT_STATUS_OK; - } - - /* - * if the range is almost full, - * we traverse the whole table - * (this relies on sorted behavior of dbwrap_rbt) - */ - status = dbwrap_traverse_read(db, smbXsrv_open_local_allocate_traverse, - &state, &count); - if (NT_STATUS_IS_OK(status)) { - if (NT_STATUS_IS_OK(state.status)) { - return NT_STATUS_INTERNAL_ERROR; - } - - if (!NT_STATUS_EQUAL(state.status, NT_STATUS_INTERNAL_ERROR)) { - return state.status; - } - - if (state.useable_id <= state.highest_id) { - state.status = NT_STATUS_OK; - } else { - return NT_STATUS_INSUFFICIENT_RESOURCES; - } - } else if (!NT_STATUS_EQUAL(status, NT_STATUS_INTERNAL_DB_CORRUPTION)) { - /* - * Here we really expect NT_STATUS_INTERNAL_DB_CORRUPTION! - * - * If we get anything else it is an error, because it - * means we did not manage to find a free slot in - * the db. - */ - return NT_STATUS_INSUFFICIENT_RESOURCES; - } - - if (NT_STATUS_IS_OK(state.status)) { - uint32_t id; - TDB_DATA val; - struct db_record *rec = NULL; - - id = state.useable_id; - - rec = smbXsrv_open_local_fetch_locked(db, id, mem_ctx); - if (rec == NULL) { - return NT_STATUS_INSUFFICIENT_RESOURCES; - } - - val = dbwrap_record_get_value(rec); - if (val.dsize != 0) { - TALLOC_FREE(rec); - return NT_STATUS_INTERNAL_DB_CORRUPTION; - } - - *_rec = rec; - *_id = id; - return NT_STATUS_OK; - } - - return state.status; -} - -struct smbXsrv_open_local_fetch_state { - struct smbXsrv_open *op; - NTSTATUS status; -}; - -static void smbXsrv_open_local_fetch_parser(TDB_DATA key, TDB_DATA data, - void *private_data) -{ - struct smbXsrv_open_local_fetch_state *state = - (struct smbXsrv_open_local_fetch_state *)private_data; - void *ptr; - - if (data.dsize != sizeof(ptr)) { - state->status = NT_STATUS_INTERNAL_DB_ERROR; - return; - } - - memcpy(&ptr, data.dptr, data.dsize); - state->op = talloc_get_type_abort(ptr, struct smbXsrv_open); - state->status = NT_STATUS_OK; -} - static NTSTATUS smbXsrv_open_local_lookup(struct smbXsrv_open_table *table, uint32_t open_local_id, uint32_t open_global_id, NTTIME now, struct smbXsrv_open **_open) { - struct smbXsrv_open_local_fetch_state state = { - .op = NULL, - .status = NT_STATUS_INTERNAL_ERROR, - }; - uint8_t key_buf[SMBXSRV_OPEN_LOCAL_TDB_KEY_SIZE]; - TDB_DATA key; - NTSTATUS status; + struct smbXsrv_open *op = NULL; *_open = NULL; @@ -458,44 +218,30 @@ static NTSTATUS smbXsrv_open_local_lookup(struct smbXsrv_open_table *table, return NT_STATUS_FILE_CLOSED; } - if (table->local.db_ctx == NULL) { + if (table->local.idr == NULL) { return NT_STATUS_INTERNAL_ERROR; } - key = smbXsrv_open_local_id_to_key(open_local_id, key_buf); - - status = dbwrap_parse_record(table->local.db_ctx, key, - smbXsrv_open_local_fetch_parser, - &state); - if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) { - return NT_STATUS_FILE_CLOSED; - } - if (!NT_STATUS_IS_OK(status)) { - return status; - } - if (!NT_STATUS_IS_OK(state.status)) { - return state.status; - } - - if (NT_STATUS_EQUAL(state.op->status, NT_STATUS_FILE_CLOSED)) { + op = idr_find(table->local.idr, open_local_id); + if (op == NULL) { return NT_STATUS_FILE_CLOSED; } if (open_global_id == 0) { /* make the global check a no-op for SMB1 */ - open_global_id = state.op->global->open_global_id; + open_global_id = op->global->open_global_id; } - if (state.op->global->open_global_id != open_global_id) { + if (op->global->open_global_id != open_global_id) { return NT_STATUS_FILE_CLOSED; } if (now != 0) { - state.op->idle_time = now; + op->idle_time = now; } - *_open = state.op; - return state.op->status; + *_open = op; + return NT_STATUS_OK; } static int smbXsrv_open_global_destructor(struct smbXsrv_open_global0 *global) @@ -817,14 +563,12 @@ NTSTATUS smbXsrv_open_create(struct smbXsrv_connection *conn, struct smbXsrv_open **_open) { struct smbXsrv_open_table *table = conn->client->open_table; - struct db_record *local_rec = NULL; struct smbXsrv_open *op = NULL; - void *ptr = NULL; - TDB_DATA val; struct smbXsrv_open_global0 *global = NULL; NTSTATUS status; struct dom_sid *current_sid = NULL; struct security_token *current_token = NULL; + int local_id; if (session_info == NULL) { return NT_STATUS_INVALID_HANDLE; @@ -863,16 +607,16 @@ NTSTATUS smbXsrv_open_create(struct smbXsrv_connection *conn, } op->global = global; - status = smbXsrv_open_local_allocate_id(table->local.db_ctx, - table->local.lowest_id, - table->local.highest_id, - op, - &local_rec, - &op->local_id); - if (!NT_STATUS_IS_OK(status)) { + local_id = idr_get_new_random( + table->local.idr, + op, + table->local.lowest_id, + table->local.highest_id); + if (local_id == -1) { TALLOC_FREE(op); - return status; + return NT_STATUS_INSUFFICIENT_RESOURCES; } + op->local_id = local_id; global->open_persistent_id = global->open_global_id; global->open_volatile_id = op->local_id; @@ -884,14 +628,6 @@ NTSTATUS smbXsrv_open_create(struct smbXsrv_connection *conn, global->client_guid = conn->smb2.client.guid; } - ptr = op; - val = make_tdb_data((uint8_t const *)&ptr, sizeof(ptr)); - status = dbwrap_record_store(local_rec, val, TDB_REPLACE); - TALLOC_FREE(local_rec); - if (!NT_STATUS_IS_OK(status)) { - TALLOC_FREE(op); - return status; - } table->local.num_opens += 1; talloc_set_destructor(op, smbXsrv_open_destructor); @@ -1077,10 +813,10 @@ NTSTATUS smbXsrv_open_update(struct smbXsrv_open *op) NTSTATUS smbXsrv_open_close(struct smbXsrv_open *op, NTTIME now) { struct smbXsrv_open_table *table; - struct db_record *local_rec = NULL; struct db_record *global_rec = NULL; NTSTATUS status; NTSTATUS error = NT_STATUS_OK; + int ret; error = smbXsrv_open_clear_replay_cache(op); if (!NT_STATUS_IS_OK(error)) { @@ -1161,28 +897,11 @@ NTSTATUS smbXsrv_open_close(struct smbXsrv_open *op, NTTIME now) } TALLOC_FREE(global_rec); - local_rec = smbXsrv_open_local_fetch_locked(table->local.db_ctx, - op->local_id, - op /* TALLOC_CTX*/); - if (local_rec == NULL) { - error = NT_STATUS_INTERNAL_ERROR; - } - - status = dbwrap_record_delete(local_rec); - if (!NT_STATUS_IS_OK(status)) { - TDB_DATA key = dbwrap_record_get_key(local_rec); + ret = idr_remove(table->local.idr, op->local_id); + SMB_ASSERT(ret == 0); - DEBUG(0, ("smbXsrv_open_close(0x%08x): " - "failed to delete local key '%s': %s\n", - op->global->open_global_id, - tdb_data_dbg(key), - nt_errstr(status))); - error = status; - } table->local.num_opens -= 1; - TALLOC_FREE(local_rec); - if (op->compat) { op->compat->op = NULL; file_free(NULL, op->compat); @@ -1224,6 +943,7 @@ NTSTATUS smb1srv_open_lookup(struct smbXsrv_connection *conn, NTSTATUS smb2srv_open_table_init(struct smbXsrv_connection *conn) { uint32_t max_opens; + uint32_t highest_id; /* * Allow a range from 1..4294967294. @@ -1241,7 +961,14 @@ NTSTATUS smb2srv_open_table_init(struct smbXsrv_connection *conn) max_opens = conn->client->sconn->real_max_open_files; max_opens = MIN(max_opens, UINT16_MAX - 1); - return smbXsrv_open_table_init(conn, 1, UINT32_MAX - 1, max_opens); + /* + * idtree uses "int" for local IDs. Limit the maximum ID to + * what "int" can hold. + */ + highest_id = UINT32_MAX-1; + highest_id = MIN(highest_id, INT_MAX); + + return smbXsrv_open_table_init(conn, 1, highest_id, max_opens); } NTSTATUS smb2srv_open_lookup(struct smbXsrv_connection *conn, @@ -1483,14 +1210,12 @@ NTSTATUS smb2srv_open_recreate(struct smbXsrv_connection *conn, struct smbXsrv_open **_open) { struct smbXsrv_open_table *table = conn->client->open_table; - struct db_record *local_rec = NULL; struct smbXsrv_open *op = NULL; - void *ptr = NULL; - TDB_DATA val; uint32_t global_id = persistent_id & UINT32_MAX; uint64_t global_zeros = persistent_id & 0xFFFFFFFF00000000LLU; NTSTATUS status; struct security_token *current_token = NULL; + int local_id; if (session_info == NULL) { DEBUG(10, ("session_info=NULL\n")); @@ -1550,31 +1275,24 @@ NTSTATUS smb2srv_open_recreate(struct smbXsrv_connection *conn, return NT_STATUS_INSUFFICIENT_RESOURCES; } - status = smbXsrv_open_local_allocate_id(table->local.db_ctx, - table->local.lowest_id, - table->local.highest_id, - op, - &local_rec, - &op->local_id); - if (!NT_STATUS_IS_OK(status)) { + local_id = idr_get_new_random( + table->local.idr, + op, + table->local.lowest_id, + table->local.highest_id); + if (local_id == -1) { TALLOC_FREE(op); - return status; + return NT_STATUS_INSUFFICIENT_RESOURCES; } + op->local_id = local_id; + op->idle_time = now; op->status = NT_STATUS_FILE_CLOSED; op->global->open_volatile_id = op->local_id; op->global->server_id = messaging_server_id(conn->client->msg_ctx); - ptr = op; - val = make_tdb_data((uint8_t const *)&ptr, sizeof(ptr)); - status = dbwrap_record_store(local_rec, val, TDB_REPLACE); - TALLOC_FREE(local_rec); - if (!NT_STATUS_IS_OK(status)) { - TALLOC_FREE(op); - return status; - } table->local.num_opens += 1; talloc_set_destructor(op, smbXsrv_open_destructor); |