diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 17:47:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 17:47:29 +0000 |
commit | 4f5791ebd03eaec1c7da0865a383175b05102712 (patch) | |
tree | 8ce7b00f7a76baa386372422adebbe64510812d4 /lib/ldb/ldb_tdb/ldb_tdb.c | |
parent | Initial commit. (diff) | |
download | samba-4f5791ebd03eaec1c7da0865a383175b05102712.tar.xz samba-4f5791ebd03eaec1c7da0865a383175b05102712.zip |
Adding upstream version 2:4.17.12+dfsg.upstream/2%4.17.12+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'lib/ldb/ldb_tdb/ldb_tdb.c')
-rw-r--r-- | lib/ldb/ldb_tdb/ldb_tdb.c | 593 |
1 files changed, 593 insertions, 0 deletions
diff --git a/lib/ldb/ldb_tdb/ldb_tdb.c b/lib/ldb/ldb_tdb/ldb_tdb.c new file mode 100644 index 0000000..77cd5e9 --- /dev/null +++ b/lib/ldb/ldb_tdb/ldb_tdb.c @@ -0,0 +1,593 @@ +/* + ldb database library + + Copyright (C) Andrew Tridgell 2004 + Copyright (C) Stefan Metzmacher 2004 + Copyright (C) Simo Sorce 2006-2008 + Copyright (C) Matthias Dieter Wallnöfer 2009-2010 + + ** NOTE! The following LGPL license applies to the ldb + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see <http://www.gnu.org/licenses/>. +*/ + +/* + * Name: ldb_tdb + * + * Component: ldb tdb backend + * + * Description: core functions for tdb backend + * + * Author: Andrew Tridgell + * Author: Stefan Metzmacher + * + * Modifications: + * + * - description: make the module use asynchronous calls + * date: Feb 2006 + * Author: Simo Sorce + * + * - description: make it possible to use event contexts + * date: Jan 2008 + * Author: Simo Sorce + * + * - description: fix up memory leaks and small bugs + * date: Oct 2009 + * Author: Matthias Dieter Wallnöfer + */ + +#include "ldb_tdb.h" +#include "ldb_private.h" +#include "../ldb_key_value/ldb_kv.h" +#include <tdb.h> + +/* + lock the database for read - use by ltdb_search and ltdb_sequence_number +*/ +static int ltdb_lock_read(struct ldb_module *module) +{ + void *data = ldb_module_get_private(module); + struct ldb_kv_private *ldb_kv = + talloc_get_type(data, struct ldb_kv_private); + int tdb_ret = 0; + int ret; + pid_t pid = getpid(); + + if (ldb_kv->pid != pid) { + ldb_asprintf_errstring(ldb_module_get_ctx(module), + __location__ + ": Reusing ldb opend by pid %d in " + "process %d\n", + ldb_kv->pid, + pid); + return LDB_ERR_PROTOCOL_ERROR; + } + + if (tdb_transaction_active(ldb_kv->tdb) == false && + ldb_kv->read_lock_count == 0) { + tdb_ret = tdb_lockall_read(ldb_kv->tdb); + } + if (tdb_ret == 0) { + ldb_kv->read_lock_count++; + return LDB_SUCCESS; + } + ret = ltdb_err_map(tdb_error(ldb_kv->tdb)); + if (ret == LDB_SUCCESS) { + ret = LDB_ERR_OPERATIONS_ERROR; + } + ldb_debug_set(ldb_module_get_ctx(module), + LDB_DEBUG_FATAL, + "Failure during ltdb_lock_read(): %s -> %s", + tdb_errorstr(ldb_kv->tdb), + ldb_strerror(ret)); + return ret; +} + +/* + unlock the database after a ltdb_lock_read() +*/ +static int ltdb_unlock_read(struct ldb_module *module) +{ + void *data = ldb_module_get_private(module); + struct ldb_kv_private *ldb_kv = + talloc_get_type(data, struct ldb_kv_private); + pid_t pid = getpid(); + + if (ldb_kv->pid != pid) { + ldb_asprintf_errstring(ldb_module_get_ctx(module), + __location__ + ": Reusing ldb opend by pid %d in " + "process %d\n", + ldb_kv->pid, + pid); + return LDB_ERR_PROTOCOL_ERROR; + } + if (!tdb_transaction_active(ldb_kv->tdb) && + ldb_kv->read_lock_count == 1) { + tdb_unlockall_read(ldb_kv->tdb); + ldb_kv->read_lock_count--; + return 0; + } + ldb_kv->read_lock_count--; + return 0; +} + +static int ltdb_store(struct ldb_kv_private *ldb_kv, + struct ldb_val ldb_key, + struct ldb_val ldb_data, + int flags) +{ + TDB_DATA key = { + .dptr = ldb_key.data, + .dsize = ldb_key.length + }; + TDB_DATA data = { + .dptr = ldb_data.data, + .dsize = ldb_data.length + }; + bool transaction_active = tdb_transaction_active(ldb_kv->tdb); + if (transaction_active == false){ + return LDB_ERR_PROTOCOL_ERROR; + } + return tdb_store(ldb_kv->tdb, key, data, flags); +} + +static int ltdb_error(struct ldb_kv_private *ldb_kv) +{ + return ltdb_err_map(tdb_error(ldb_kv->tdb)); +} + +static const char *ltdb_errorstr(struct ldb_kv_private *ldb_kv) +{ + return tdb_errorstr(ldb_kv->tdb); +} + +static int ltdb_delete(struct ldb_kv_private *ldb_kv, struct ldb_val ldb_key) +{ + TDB_DATA tdb_key = { + .dptr = ldb_key.data, + .dsize = ldb_key.length + }; + bool transaction_active = tdb_transaction_active(ldb_kv->tdb); + if (transaction_active == false){ + return LDB_ERR_PROTOCOL_ERROR; + } + return tdb_delete(ldb_kv->tdb, tdb_key); +} + +static int ltdb_transaction_start(struct ldb_kv_private *ldb_kv) +{ + pid_t pid = getpid(); + + if (ldb_kv->pid != pid) { + ldb_asprintf_errstring(ldb_module_get_ctx(ldb_kv->module), + __location__ + ": Reusing ldb opend by pid %d in " + "process %d\n", + ldb_kv->pid, + pid); + return LDB_ERR_PROTOCOL_ERROR; + } + + return tdb_transaction_start(ldb_kv->tdb); +} + +static int ltdb_transaction_cancel(struct ldb_kv_private *ldb_kv) +{ + pid_t pid = getpid(); + + if (ldb_kv->pid != pid) { + ldb_asprintf_errstring(ldb_module_get_ctx(ldb_kv->module), + __location__ + ": Reusing ldb opend by pid %d in " + "process %d\n", + ldb_kv->pid, + pid); + return LDB_ERR_PROTOCOL_ERROR; + } + + return tdb_transaction_cancel(ldb_kv->tdb); +} + +static int ltdb_transaction_prepare_commit(struct ldb_kv_private *ldb_kv) +{ + pid_t pid = getpid(); + + if (ldb_kv->pid != pid) { + ldb_asprintf_errstring(ldb_module_get_ctx(ldb_kv->module), + __location__ + ": Reusing ldb opend by pid %d in " + "process %d\n", + ldb_kv->pid, + pid); + return LDB_ERR_PROTOCOL_ERROR; + } + + return tdb_transaction_prepare_commit(ldb_kv->tdb); +} + +static int ltdb_transaction_commit(struct ldb_kv_private *ldb_kv) +{ + pid_t pid = getpid(); + + if (ldb_kv->pid != pid) { + ldb_asprintf_errstring(ldb_module_get_ctx(ldb_kv->module), + __location__ + ": Reusing ldb opend by pid %d in " + "process %d\n", + ldb_kv->pid, + pid); + return LDB_ERR_PROTOCOL_ERROR; + } + + return tdb_transaction_commit(ldb_kv->tdb); +} +struct kv_ctx { + ldb_kv_traverse_fn kv_traverse_fn; + void *ctx; + struct ldb_kv_private *ldb_kv; + int (*parser)(struct ldb_val key, + struct ldb_val data, + void *private_data); + int parser_ret; +}; + +static int ltdb_traverse_fn_wrapper(struct tdb_context *tdb, + TDB_DATA tdb_key, + TDB_DATA tdb_data, + void *ctx) +{ + struct kv_ctx *kv_ctx = ctx; + struct ldb_val key = { + .length = tdb_key.dsize, + .data = tdb_key.dptr, + }; + struct ldb_val data = { + .length = tdb_data.dsize, + .data = tdb_data.dptr, + }; + return kv_ctx->kv_traverse_fn(kv_ctx->ldb_kv, key, data, kv_ctx->ctx); +} + +static int ltdb_traverse_fn(struct ldb_kv_private *ldb_kv, + ldb_kv_traverse_fn fn, + void *ctx) +{ + struct kv_ctx kv_ctx = { + .kv_traverse_fn = fn, .ctx = ctx, .ldb_kv = ldb_kv}; + if (tdb_transaction_active(ldb_kv->tdb)) { + return tdb_traverse( + ldb_kv->tdb, ltdb_traverse_fn_wrapper, &kv_ctx); + } else { + return tdb_traverse_read( + ldb_kv->tdb, ltdb_traverse_fn_wrapper, &kv_ctx); + } +} + +static int ltdb_update_in_iterate(struct ldb_kv_private *ldb_kv, + struct ldb_val ldb_key, + struct ldb_val ldb_key2, + struct ldb_val ldb_data, + void *state) +{ + int tdb_ret; + struct ldb_context *ldb; + struct ldb_kv_reindex_context *ctx = + (struct ldb_kv_reindex_context *)state; + struct ldb_module *module = ldb_kv->module; + TDB_DATA key = { + .dptr = ldb_key.data, + .dsize = ldb_key.length + }; + TDB_DATA key2 = { + .dptr = ldb_key2.data, + .dsize = ldb_key2.length + }; + TDB_DATA data = { + .dptr = ldb_data.data, + .dsize = ldb_data.length + }; + + ldb = ldb_module_get_ctx(module); + + tdb_ret = tdb_delete(ldb_kv->tdb, key); + if (tdb_ret != 0) { + ldb_debug(ldb, + LDB_DEBUG_ERROR, + "Failed to delete %*.*s " + "for rekey as %*.*s: %s", + (int)key.dsize, + (int)key.dsize, + (const char *)key.dptr, + (int)key2.dsize, + (int)key2.dsize, + (const char *)key.dptr, + tdb_errorstr(ldb_kv->tdb)); + ctx->error = ltdb_err_map(tdb_error(ldb_kv->tdb)); + return -1; + } + tdb_ret = tdb_store(ldb_kv->tdb, key2, data, 0); + if (tdb_ret != 0) { + ldb_debug(ldb, + LDB_DEBUG_ERROR, + "Failed to rekey %*.*s as %*.*s: %s", + (int)key.dsize, + (int)key.dsize, + (const char *)key.dptr, + (int)key2.dsize, + (int)key2.dsize, + (const char *)key.dptr, + tdb_errorstr(ldb_kv->tdb)); + ctx->error = ltdb_err_map(tdb_error(ldb_kv->tdb)); + return -1; + } + return tdb_ret; +} + +static int ltdb_parse_record_wrapper(TDB_DATA tdb_key, + TDB_DATA tdb_data, + void *ctx) +{ + struct kv_ctx *kv_ctx = ctx; + struct ldb_val key = { + .length = tdb_key.dsize, + .data = tdb_key.dptr, + }; + struct ldb_val data = { + .length = tdb_data.dsize, + .data = tdb_data.dptr, + }; + + kv_ctx->parser_ret = kv_ctx->parser(key, data, kv_ctx->ctx); + return kv_ctx->parser_ret; +} + +static int ltdb_parse_record(struct ldb_kv_private *ldb_kv, + struct ldb_val ldb_key, + int (*parser)(struct ldb_val key, + struct ldb_val data, + void *private_data), + void *ctx) +{ + struct kv_ctx kv_ctx = {.parser = parser, .ctx = ctx, .ldb_kv = ldb_kv}; + TDB_DATA key = { + .dptr = ldb_key.data, + .dsize = ldb_key.length + }; + int ret; + + if (tdb_transaction_active(ldb_kv->tdb) == false && + ldb_kv->read_lock_count == 0) { + return LDB_ERR_PROTOCOL_ERROR; + } + + ret = tdb_parse_record( + ldb_kv->tdb, key, ltdb_parse_record_wrapper, &kv_ctx); + if (kv_ctx.parser_ret != LDB_SUCCESS) { + return kv_ctx.parser_ret; + } else if (ret == 0) { + return LDB_SUCCESS; + } + return ltdb_err_map(tdb_error(ldb_kv->tdb)); +} + +static int ltdb_iterate_range(struct ldb_kv_private *ldb_kv, + struct ldb_val start_key, + struct ldb_val end_key, + ldb_kv_traverse_fn fn, + void *ctx) +{ + /* + * We do not implement this operation because we do not know how to + * iterate from one key to the next (in a sorted fashion). + * + * We could mimic it potentially, but it would violate boundaries of + * knowledge (data type representation). + */ + return LDB_ERR_OPERATIONS_ERROR; +} + +static const char *ltdb_name(struct ldb_kv_private *ldb_kv) +{ + return tdb_name(ldb_kv->tdb); +} + +static bool ltdb_changed(struct ldb_kv_private *ldb_kv) +{ + int seq = tdb_get_seqnum(ldb_kv->tdb); + bool has_changed = (seq != ldb_kv->tdb_seqnum); + + ldb_kv->tdb_seqnum = seq; + + return has_changed; +} + +static bool ltdb_transaction_active(struct ldb_kv_private *ldb_kv) +{ + return tdb_transaction_active(ldb_kv->tdb); +} + +/* + * Get an estimate of the number of records in a tdb database. + * + * This implementation will overestimate the number of records in a sparsely + * populated database. The size estimate is only used for allocating + * an in memory tdb to cache index records during a reindex, overestimating + * the contents is acceptable, and preferable to underestimating + */ +#define RECORD_SIZE 500 +static size_t ltdb_get_size(struct ldb_kv_private *ldb_kv) +{ + size_t map_size = tdb_map_size(ldb_kv->tdb); + size_t size = map_size / RECORD_SIZE; + + return size; +} + +/* + * Start a sub transaction + * As TDB does not currently support nested transactions, we do nothing and + * return LDB_SUCCESS + */ +static int ltdb_nested_transaction_start(struct ldb_kv_private *ldb_kv) +{ + return LDB_SUCCESS; +} + +/* + * Commit a sub transaction + * As TDB does not currently support nested transactions, we do nothing and + * return LDB_SUCCESS + */ +static int ltdb_nested_transaction_commit(struct ldb_kv_private *ldb_kv) +{ + return LDB_SUCCESS; +} + +/* + * Cancel a sub transaction + * As TDB does not currently support nested transactions, we do nothing and + * return LDB_SUCCESS + */ +static int ltdb_nested_transaction_cancel(struct ldb_kv_private *ldb_kv) +{ + return LDB_SUCCESS; +} + +static const struct kv_db_ops key_value_ops = { + /* No support for any additional features */ + .options = 0, + + .store = ltdb_store, + .delete = ltdb_delete, + .iterate = ltdb_traverse_fn, + .update_in_iterate = ltdb_update_in_iterate, + .fetch_and_parse = ltdb_parse_record, + .iterate_range = ltdb_iterate_range, + .lock_read = ltdb_lock_read, + .unlock_read = ltdb_unlock_read, + .begin_write = ltdb_transaction_start, + .prepare_write = ltdb_transaction_prepare_commit, + .finish_write = ltdb_transaction_commit, + .abort_write = ltdb_transaction_cancel, + .error = ltdb_error, + .errorstr = ltdb_errorstr, + .name = ltdb_name, + .has_changed = ltdb_changed, + .transaction_active = ltdb_transaction_active, + .get_size = ltdb_get_size, + .begin_nested_write = ltdb_nested_transaction_start, + .finish_nested_write = ltdb_nested_transaction_commit, + .abort_nested_write = ltdb_nested_transaction_cancel, +}; + +/* + connect to the database +*/ +int ltdb_connect(struct ldb_context *ldb, const char *url, + unsigned int flags, const char *options[], + struct ldb_module **_module) +{ + const char *path; + int tdb_flags, open_flags; + struct ldb_kv_private *ldb_kv; + + /* + * We hold locks, so we must use a private event context + * on each returned handle + */ + ldb_set_require_private_event_context(ldb); + + /* parse the url */ + if (strchr(url, ':')) { + if (strncmp(url, "tdb://", 6) != 0) { + ldb_debug(ldb, LDB_DEBUG_ERROR, + "Invalid tdb URL '%s'", url); + return LDB_ERR_OPERATIONS_ERROR; + } + path = url+6; + } else { + path = url; + } + + tdb_flags = TDB_DEFAULT | TDB_SEQNUM | TDB_DISALLOW_NESTING; + + /* check for the 'nosync' option */ + if (flags & LDB_FLG_NOSYNC) { + tdb_flags |= TDB_NOSYNC; + } + + /* and nommap option */ + if (flags & LDB_FLG_NOMMAP) { + tdb_flags |= TDB_NOMMAP; + } + + ldb_kv = talloc_zero(ldb, struct ldb_kv_private); + if (!ldb_kv) { + ldb_oom(ldb); + return LDB_ERR_OPERATIONS_ERROR; + } + + if (flags & LDB_FLG_RDONLY) { + /* + * This is weird, but because we can only have one tdb + * in this process, and the other one could be + * read-write, we can't use the tdb readonly. Plus a + * read only tdb prohibits the all-record lock. + */ + open_flags = O_RDWR; + + ldb_kv->read_only = true; + + } else if (flags & LDB_FLG_DONT_CREATE_DB) { + /* + * This is used by ldbsearch to prevent creation of the database + * if the name is wrong + */ + open_flags = O_RDWR; + } else { + /* + * This is the normal case + */ + open_flags = O_CREAT | O_RDWR; + } + + ldb_kv->kv_ops = &key_value_ops; + + errno = 0; + /* note that we use quite a large default hash size */ + ldb_kv->tdb = ltdb_wrap_open(ldb_kv, + path, + 10000, + tdb_flags, + open_flags, + ldb_get_create_perms(ldb), + ldb); + if (!ldb_kv->tdb) { + ldb_asprintf_errstring(ldb, + "Unable to open tdb '%s': %s", path, strerror(errno)); + ldb_debug(ldb, LDB_DEBUG_ERROR, + "Unable to open tdb '%s': %s", path, strerror(errno)); + talloc_free(ldb_kv); + if (errno == EACCES || errno == EPERM) { + return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS; + } + return LDB_ERR_OPERATIONS_ERROR; + } + + return ldb_kv_init_store( + ldb_kv, "ldb_tdb backend", ldb, options, _module); +} |