1
0
mirror of https://github.com/samba-team/samba.git synced 2025-01-13 13:18:06 +03:00

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
This commit is contained in:
Volker Lendecke 2023-01-04 12:18:44 +01:00 committed by Jeremy Allison
parent b73ecb28a7
commit 8ee2034674

View File

@ -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;
}
ret = idr_remove(table->local.idr, op->local_id);
SMB_ASSERT(ret == 0);
status = dbwrap_record_delete(local_rec);
if (!NT_STATUS_IS_OK(status)) {
TDB_DATA key = dbwrap_record_get_key(local_rec);
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);