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:
parent
b73ecb28a7
commit
8ee2034674
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user