From: Gary Lockyer Date: Sun, 22 Jul 2018 22:02:16 +0000 (+1200) Subject: lib ldb: move key value code to lib/ldb/ldb_key_value X-Git-Tag: ldb-1.5.0~78 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=19be0be232c25250e79dc313fd43829de049b491;p=thirdparty%2Fsamba.git lib ldb: move key value code to lib/ldb/ldb_key_value Move the key value code to a separate subdirectory. Signed-off-by: Gary Lockyer Reviewed-by: Andrew Bartlett --- diff --git a/lib/ldb/ldb_key_value/ldb_kv.c b/lib/ldb/ldb_key_value/ldb_kv.c new file mode 100644 index 00000000000..2449ec276b4 --- /dev/null +++ b/lib/ldb/ldb_key_value/ldb_kv.c @@ -0,0 +1,1933 @@ +/* + 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 . +*/ + +/* + * Name: ldb_kv + * + * Component: ldb key value backend + * + * Description: core functions for ldb key value 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_kv.h" +#include "ldb_private.h" + +/* + prevent memory errors on callbacks +*/ +struct ldb_kv_req_spy { + struct ldb_kv_context *ctx; +}; + +/* + * Determine if this key could hold a record. We allow the new GUID + * index, the old DN index and a possible future ID= + */ +bool ldb_kv_key_is_record(TDB_DATA key) +{ + if (key.dsize < 4) { + return false; + } + + if (memcmp(key.dptr, "DN=", 3) == 0) { + return true; + } + + if (memcmp(key.dptr, "ID=", 3) == 0) { + return true; + } + + if (key.dsize < sizeof(LTDB_GUID_KEY_PREFIX)) { + return false; + } + + if (memcmp(key.dptr, LTDB_GUID_KEY_PREFIX, + sizeof(LTDB_GUID_KEY_PREFIX) - 1) == 0) { + return true; + } + + return false; +} + +/* + form a TDB_DATA for a record key + caller frees + + note that the key for a record can depend on whether the + dn refers to a case sensitive index record or not +*/ +TDB_DATA ldb_kv_key_dn(struct ldb_module *module, + TALLOC_CTX *mem_ctx, + struct ldb_dn *dn) +{ + TDB_DATA key; + char *key_str = NULL; + const char *dn_folded = NULL; + + /* + most DNs are case insensitive. The exception is index DNs for + case sensitive attributes + + there are 3 cases dealt with in this code: + + 1) if the dn doesn't start with @ then uppercase the attribute + names and the attributes values of case insensitive attributes + 2) if the dn starts with @ then leave it alone - + the indexing code handles the rest + */ + + dn_folded = ldb_dn_get_casefold(dn); + if (!dn_folded) { + goto failed; + } + + key_str = talloc_strdup(mem_ctx, "DN="); + if (!key_str) { + goto failed; + } + + key_str = talloc_strdup_append_buffer(key_str, dn_folded); + if (!key_str) { + goto failed; + } + + key.dptr = (uint8_t *)key_str; + key.dsize = strlen(key_str) + 1; + + return key; + +failed: + errno = ENOMEM; + key.dptr = NULL; + key.dsize = 0; + return key; +} + +/* The caller is to provide a correctly sized key */ +int ldb_kv_guid_to_key(struct ldb_module *module, + struct ldb_kv_private *ldb_kv, + const struct ldb_val *GUID_val, + TDB_DATA *key) +{ + const char *GUID_prefix = LTDB_GUID_KEY_PREFIX; + const int GUID_prefix_len = sizeof(LTDB_GUID_KEY_PREFIX) - 1; + + if (key->dsize != (GUID_val->length+GUID_prefix_len)) { + return LDB_ERR_OPERATIONS_ERROR; + } + + memcpy(key->dptr, GUID_prefix, GUID_prefix_len); + memcpy(&key->dptr[GUID_prefix_len], + GUID_val->data, GUID_val->length); + return LDB_SUCCESS; +} + +/* + * The caller is to provide a correctly sized key, used only in + * the GUID index mode + */ +int ldb_kv_idx_to_key(struct ldb_module *module, + struct ldb_kv_private *ldb_kv, + TALLOC_CTX *mem_ctx, + const struct ldb_val *idx_val, + TDB_DATA *key) +{ + struct ldb_context *ldb = ldb_module_get_ctx(module); + struct ldb_dn *dn; + + if (ldb_kv->cache->GUID_index_attribute != NULL) { + return ldb_kv_guid_to_key(module, ldb_kv, idx_val, key); + } + + dn = ldb_dn_from_ldb_val(mem_ctx, ldb, idx_val); + if (dn == NULL) { + /* + * LDB_ERR_INVALID_DN_SYNTAX would just be confusing + * to the caller, as this in an invalid index value + */ + return LDB_ERR_OPERATIONS_ERROR; + } + /* form the key */ + *key = ldb_kv_key_dn(module, mem_ctx, dn); + TALLOC_FREE(dn); + if (!key->dptr) { + return ldb_module_oom(module); + } + return LDB_SUCCESS; +} + +/* + form a TDB_DATA for a record key + caller frees mem_ctx, which may or may not have the key + as a child. + + note that the key for a record can depend on whether a + GUID index is in use, or the DN is used as the key +*/ +TDB_DATA ldb_kv_key_msg(struct ldb_module *module, + TALLOC_CTX *mem_ctx, + const struct ldb_message *msg) +{ + void *data = ldb_module_get_private(module); + struct ldb_kv_private *ldb_kv = + talloc_get_type(data, struct ldb_kv_private); + TDB_DATA key; + const struct ldb_val *guid_val; + int ret; + + if (ldb_kv->cache->GUID_index_attribute == NULL) { + return ldb_kv_key_dn(module, mem_ctx, msg->dn); + } + + if (ldb_dn_is_special(msg->dn)) { + return ldb_kv_key_dn(module, mem_ctx, msg->dn); + } + + guid_val = + ldb_msg_find_ldb_val(msg, ldb_kv->cache->GUID_index_attribute); + if (guid_val == NULL) { + ldb_asprintf_errstring(ldb_module_get_ctx(module), + "Did not find GUID attribute %s " + "in %s, required for TDB record " + "key in " LTDB_IDXGUID " mode.", + ldb_kv->cache->GUID_index_attribute, + ldb_dn_get_linearized(msg->dn)); + errno = EINVAL; + key.dptr = NULL; + key.dsize = 0; + return key; + } + + /* In this case, allocate with talloc */ + key.dptr = talloc_size(mem_ctx, LTDB_GUID_KEY_SIZE); + if (key.dptr == NULL) { + errno = ENOMEM; + key.dptr = NULL; + key.dsize = 0; + return key; + } + key.dsize = talloc_get_size(key.dptr); + + ret = ldb_kv_guid_to_key(module, ldb_kv, guid_val, &key); + + if (ret != LDB_SUCCESS) { + errno = EINVAL; + key.dptr = NULL; + key.dsize = 0; + return key; + } + return key; +} + +/* + check special dn's have valid attributes + currently only @ATTRIBUTES is checked +*/ +static int ldb_kv_check_special_dn(struct ldb_module *module, + const struct ldb_message *msg) +{ + struct ldb_context *ldb = ldb_module_get_ctx(module); + unsigned int i, j; + + if (! ldb_dn_is_special(msg->dn) || + ! ldb_dn_check_special(msg->dn, LTDB_ATTRIBUTES)) { + return LDB_SUCCESS; + } + + /* we have @ATTRIBUTES, let's check attributes are fine */ + /* should we check that we deny multivalued attributes ? */ + for (i = 0; i < msg->num_elements; i++) { + if (ldb_attr_cmp(msg->elements[i].name, "distinguishedName") == 0) continue; + + for (j = 0; j < msg->elements[i].num_values; j++) { + if (ldb_kv_check_at_attributes_values( + &msg->elements[i].values[j]) != 0) { + ldb_set_errstring(ldb, "Invalid attribute value in an @ATTRIBUTES entry"); + return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX; + } + } + } + + return LDB_SUCCESS; +} + + +/* + we've made a modification to a dn - possibly reindex and + update sequence number +*/ +static int ldb_kv_modified(struct ldb_module *module, struct ldb_dn *dn) +{ + int ret = LDB_SUCCESS; + struct ldb_kv_private *ldb_kv = talloc_get_type( + ldb_module_get_private(module), struct ldb_kv_private); + + /* only allow modifies inside a transaction, otherwise the + * ldb is unsafe */ + if (ldb_kv->kv_ops->transaction_active(ldb_kv) == false) { + ldb_set_errstring(ldb_module_get_ctx(module), "ltdb modify without transaction"); + return LDB_ERR_OPERATIONS_ERROR; + } + + if (ldb_dn_is_special(dn) && + (ldb_dn_check_special(dn, LTDB_INDEXLIST) || + ldb_dn_check_special(dn, LTDB_ATTRIBUTES)) ) + { + if (ldb_kv->warn_reindex) { + ldb_debug(ldb_module_get_ctx(module), + LDB_DEBUG_ERROR, + "Reindexing %s due to modification on %s", + ldb_kv->kv_ops->name(ldb_kv), + ldb_dn_get_linearized(dn)); + } + ret = ldb_kv_reindex(module); + } + + /* If the modify was to a normal record, or any special except @BASEINFO, update the seq number */ + if (ret == LDB_SUCCESS && + !(ldb_dn_is_special(dn) && + ldb_dn_check_special(dn, LTDB_BASEINFO)) ) { + ret = ldb_kv_increase_sequence_number(module); + } + + /* If the modify was to @OPTIONS, reload the cache */ + if (ret == LDB_SUCCESS && + ldb_dn_is_special(dn) && + (ldb_dn_check_special(dn, LTDB_OPTIONS)) ) { + ret = ldb_kv_cache_reload(module); + } + + if (ret != LDB_SUCCESS) { + ldb_kv->reindex_failed = true; + } + + return ret; +} +/* + store a record into the db +*/ +int ldb_kv_store(struct ldb_module *module, + const struct ldb_message *msg, + int flgs) +{ + void *data = ldb_module_get_private(module); + struct ldb_kv_private *ldb_kv = + talloc_get_type(data, struct ldb_kv_private); + TDB_DATA tdb_key; + struct ldb_val ldb_key; + struct ldb_val ldb_data; + int ret = LDB_SUCCESS; + TALLOC_CTX *key_ctx = talloc_new(module); + + if (key_ctx == NULL) { + return ldb_module_oom(module); + } + + if (ldb_kv->read_only) { + talloc_free(key_ctx); + return LDB_ERR_UNWILLING_TO_PERFORM; + } + + tdb_key = ldb_kv_key_msg(module, key_ctx, msg); + if (tdb_key.dptr == NULL) { + TALLOC_FREE(key_ctx); + return LDB_ERR_OTHER; + } + + ret = ldb_pack_data(ldb_module_get_ctx(module), + msg, &ldb_data); + if (ret == -1) { + TALLOC_FREE(key_ctx); + return LDB_ERR_OTHER; + } + + ldb_key.data = tdb_key.dptr; + ldb_key.length = tdb_key.dsize; + + ret = ldb_kv->kv_ops->store(ldb_kv, ldb_key, ldb_data, flgs); + if (ret != 0) { + bool is_special = ldb_dn_is_special(msg->dn); + ret = ldb_kv->kv_ops->error(ldb_kv); + + /* + * LDB_ERR_ENTRY_ALREADY_EXISTS means the DN, not + * the GUID, so re-map + */ + if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS && !is_special && + ldb_kv->cache->GUID_index_attribute != NULL) { + ret = LDB_ERR_CONSTRAINT_VIOLATION; + } + goto done; + } + +done: + TALLOC_FREE(key_ctx); + talloc_free(ldb_data.data); + + return ret; +} + + +/* + check if a attribute is a single valued, for a given element + */ +static bool ldb_kv_single_valued(const struct ldb_schema_attribute *a, + struct ldb_message_element *el) +{ + if (!a) return false; + if (el != NULL) { + if (el->flags & LDB_FLAG_INTERNAL_FORCE_SINGLE_VALUE_CHECK) { + /* override from a ldb module, for example + used for the description field, which is + marked multi-valued in the schema but which + should not actually accept multiple + values */ + return true; + } + if (el->flags & LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK) { + /* override from a ldb module, for example used for + deleted linked attribute entries */ + return false; + } + } + if (a->flags & LDB_ATTR_FLAG_SINGLE_VALUE) { + return true; + } + return false; +} + +static int ldb_kv_add_internal(struct ldb_module *module, + struct ldb_kv_private *ldb_kv, + const struct ldb_message *msg, + bool check_single_value) +{ + struct ldb_context *ldb = ldb_module_get_ctx(module); + int ret = LDB_SUCCESS; + unsigned int i; + + for (i=0;inum_elements;i++) { + struct ldb_message_element *el = &msg->elements[i]; + const struct ldb_schema_attribute *a = ldb_schema_attribute_by_name(ldb, el->name); + + if (el->num_values == 0) { + ldb_asprintf_errstring(ldb, "attribute '%s' on '%s' specified, but with 0 values (illegal)", + el->name, ldb_dn_get_linearized(msg->dn)); + return LDB_ERR_CONSTRAINT_VIOLATION; + } + if (check_single_value && el->num_values > 1 && + ldb_kv_single_valued(a, el)) { + ldb_asprintf_errstring(ldb, "SINGLE-VALUE attribute %s on %s specified more than once", + el->name, ldb_dn_get_linearized(msg->dn)); + return LDB_ERR_CONSTRAINT_VIOLATION; + } + + /* Do not check "@ATTRIBUTES" for duplicated values */ + if (ldb_dn_is_special(msg->dn) && + ldb_dn_check_special(msg->dn, LTDB_ATTRIBUTES)) { + continue; + } + + if (check_single_value && + !(el->flags & + LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK)) { + struct ldb_val *duplicate = NULL; + + ret = ldb_msg_find_duplicate_val(ldb, discard_const(msg), + el, &duplicate, 0); + if (ret != LDB_SUCCESS) { + return ret; + } + if (duplicate != NULL) { + ldb_asprintf_errstring( + ldb, + "attribute '%s': value '%.*s' on '%s' " + "provided more than once in ADD object", + el->name, + (int)duplicate->length, + duplicate->data, + ldb_dn_get_linearized(msg->dn)); + return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS; + } + } + } + + ret = ldb_kv_store(module, msg, TDB_INSERT); + if (ret != LDB_SUCCESS) { + /* + * Try really hard to get the right error code for + * a re-add situation, as this can matter! + */ + if (ret == LDB_ERR_CONSTRAINT_VIOLATION) { + int ret2; + struct ldb_dn *dn2 = NULL; + TALLOC_CTX *mem_ctx = talloc_new(module); + if (mem_ctx == NULL) { + return ldb_module_operr(module); + } + ret2 = + ldb_kv_search_base(module, mem_ctx, msg->dn, &dn2); + TALLOC_FREE(mem_ctx); + if (ret2 == LDB_SUCCESS) { + ret = LDB_ERR_ENTRY_ALREADY_EXISTS; + } + } + if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) { + ldb_asprintf_errstring(ldb, + "Entry %s already exists", + ldb_dn_get_linearized(msg->dn)); + } + return ret; + } + + ret = ldb_kv_index_add_new(module, ldb_kv, msg); + if (ret != LDB_SUCCESS) { + /* + * If we failed to index, delete the message again. + * + * This is particularly important for the GUID index + * case, which will only fail for a duplicate DN + * in the index add. + * + * Note that the caller may not cancel the transation + * and this means the above add might really show up! + */ + ldb_kv_delete_noindex(module, msg); + return ret; + } + + ret = ldb_kv_modified(module, msg->dn); + + return ret; +} + +/* + add a record to the database +*/ +static int ldb_kv_add(struct ldb_kv_context *ctx) +{ + struct ldb_module *module = ctx->module; + struct ldb_request *req = ctx->req; + void *data = ldb_module_get_private(module); + struct ldb_kv_private *ldb_kv = + talloc_get_type(data, struct ldb_kv_private); + int ret = LDB_SUCCESS; + + if (ldb_kv->max_key_length != 0 && + ldb_kv->cache->GUID_index_attribute == NULL && + !ldb_dn_is_special(req->op.add.message->dn)) { + ldb_set_errstring(ldb_module_get_ctx(module), + "Must operate ldb_mdb in GUID " + "index mode, but " LTDB_IDXGUID " not set."); + return LDB_ERR_UNWILLING_TO_PERFORM; + } + + ret = ldb_kv_check_special_dn(module, req->op.add.message); + if (ret != LDB_SUCCESS) { + return ret; + } + + ldb_request_set_state(req, LDB_ASYNC_PENDING); + + if (ldb_kv_cache_load(module) != 0) { + return LDB_ERR_OPERATIONS_ERROR; + } + + ret = ldb_kv_add_internal(module, ldb_kv, req->op.add.message, true); + + return ret; +} + +/* + delete a record from the database, not updating indexes (used for deleting + index records) +*/ +int ldb_kv_delete_noindex(struct ldb_module *module, + const struct ldb_message *msg) +{ + void *data = ldb_module_get_private(module); + struct ldb_kv_private *ldb_kv = + talloc_get_type(data, struct ldb_kv_private); + struct ldb_val ldb_key; + TDB_DATA tdb_key; + int ret; + TALLOC_CTX *tdb_key_ctx = talloc_new(module); + + if (tdb_key_ctx == NULL) { + return ldb_module_oom(module); + } + + if (ldb_kv->read_only) { + talloc_free(tdb_key_ctx); + return LDB_ERR_UNWILLING_TO_PERFORM; + } + + tdb_key = ldb_kv_key_msg(module, tdb_key_ctx, msg); + if (!tdb_key.dptr) { + TALLOC_FREE(tdb_key_ctx); + return LDB_ERR_OTHER; + } + + ldb_key.data = tdb_key.dptr; + ldb_key.length = tdb_key.dsize; + + ret = ldb_kv->kv_ops->delete (ldb_kv, ldb_key); + TALLOC_FREE(tdb_key_ctx); + + if (ret != 0) { + ret = ldb_kv->kv_ops->error(ldb_kv); + } + + return ret; +} + +static int ldb_kv_delete_internal(struct ldb_module *module, struct ldb_dn *dn) +{ + struct ldb_message *msg; + int ret = LDB_SUCCESS; + + msg = ldb_msg_new(module); + if (msg == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + + /* in case any attribute of the message was indexed, we need + to fetch the old record */ + ret = ldb_kv_search_dn1( + module, dn, msg, LDB_UNPACK_DATA_FLAG_NO_DATA_ALLOC); + if (ret != LDB_SUCCESS) { + /* not finding the old record is an error */ + goto done; + } + + ret = ldb_kv_delete_noindex(module, msg); + if (ret != LDB_SUCCESS) { + goto done; + } + + /* remove any indexed attributes */ + ret = ldb_kv_index_delete(module, msg); + if (ret != LDB_SUCCESS) { + goto done; + } + + ret = ldb_kv_modified(module, dn); + if (ret != LDB_SUCCESS) { + goto done; + } + +done: + talloc_free(msg); + return ret; +} + +/* + delete a record from the database +*/ +static int ldb_kv_delete(struct ldb_kv_context *ctx) +{ + struct ldb_module *module = ctx->module; + struct ldb_request *req = ctx->req; + int ret = LDB_SUCCESS; + + ldb_request_set_state(req, LDB_ASYNC_PENDING); + + if (ldb_kv_cache_load(module) != 0) { + return LDB_ERR_OPERATIONS_ERROR; + } + + ret = ldb_kv_delete_internal(module, req->op.del.dn); + + return ret; +} + +/* + find an element by attribute name. At the moment this does a linear search, + it should be re-coded to use a binary search once all places that modify + records guarantee sorted order + + return the index of the first matching element if found, otherwise -1 +*/ +static int ldb_kv_find_element(const struct ldb_message *msg, const char *name) +{ + unsigned int i; + for (i=0;inum_elements;i++) { + if (ldb_attr_cmp(msg->elements[i].name, name) == 0) { + return i; + } + } + return -1; +} + + +/* + add an element to an existing record. Assumes a elements array that we + can call re-alloc on, and assumed that we can re-use the data pointers from + the passed in additional values. Use with care! + + returns 0 on success, -1 on failure (and sets errno) +*/ +static int ldb_kv_msg_add_element(struct ldb_message *msg, + struct ldb_message_element *el) +{ + struct ldb_message_element *e2; + unsigned int i; + + if (el->num_values == 0) { + /* nothing to do here - we don't add empty elements */ + return 0; + } + + e2 = talloc_realloc(msg, msg->elements, struct ldb_message_element, + msg->num_elements+1); + if (!e2) { + errno = ENOMEM; + return -1; + } + + msg->elements = e2; + + e2 = &msg->elements[msg->num_elements]; + + e2->name = el->name; + e2->flags = el->flags; + e2->values = talloc_array(msg->elements, + struct ldb_val, el->num_values); + if (!e2->values) { + errno = ENOMEM; + return -1; + } + for (i=0;inum_values;i++) { + e2->values[i] = el->values[i]; + } + e2->num_values = el->num_values; + + ++msg->num_elements; + + return 0; +} + +/* + delete all elements having a specified attribute name +*/ +static int ldb_kv_msg_delete_attribute(struct ldb_module *module, + struct ldb_kv_private *ldb_kv, + struct ldb_message *msg, + const char *name) +{ + unsigned int i; + int ret; + struct ldb_message_element *el; + bool is_special = ldb_dn_is_special(msg->dn); + + if (!is_special && ldb_kv->cache->GUID_index_attribute != NULL && + ldb_attr_cmp(name, ldb_kv->cache->GUID_index_attribute) == 0) { + struct ldb_context *ldb = ldb_module_get_ctx(module); + ldb_asprintf_errstring(ldb, + "Must not modify GUID " + "attribute %s (used as DB index)", + ldb_kv->cache->GUID_index_attribute); + return LDB_ERR_CONSTRAINT_VIOLATION; + } + + el = ldb_msg_find_element(msg, name); + if (el == NULL) { + return LDB_ERR_NO_SUCH_ATTRIBUTE; + } + i = el - msg->elements; + + ret = ldb_kv_index_del_element(module, ldb_kv, msg, el); + if (ret != LDB_SUCCESS) { + return ret; + } + + talloc_free(el->values); + if (msg->num_elements > (i+1)) { + memmove(el, el+1, sizeof(*el) * (msg->num_elements - (i+1))); + } + msg->num_elements--; + msg->elements = talloc_realloc(msg, msg->elements, + struct ldb_message_element, + msg->num_elements); + return LDB_SUCCESS; +} + +/* + delete all elements matching an attribute name/value + + return LDB Error on failure +*/ +static int ldb_kv_msg_delete_element(struct ldb_module *module, + struct ldb_kv_private *ldb_kv, + struct ldb_message *msg, + const char *name, + const struct ldb_val *val) +{ + struct ldb_context *ldb = ldb_module_get_ctx(module); + unsigned int i; + int found, ret; + struct ldb_message_element *el; + const struct ldb_schema_attribute *a; + + found = ldb_kv_find_element(msg, name); + if (found == -1) { + return LDB_ERR_NO_SUCH_ATTRIBUTE; + } + + i = (unsigned int) found; + el = &(msg->elements[i]); + + a = ldb_schema_attribute_by_name(ldb, el->name); + + for (i=0;inum_values;i++) { + bool matched; + if (a->syntax->operator_fn) { + ret = a->syntax->operator_fn(ldb, LDB_OP_EQUALITY, a, + &el->values[i], val, &matched); + if (ret != LDB_SUCCESS) return ret; + } else { + matched = (a->syntax->comparison_fn(ldb, ldb, + &el->values[i], val) == 0); + } + if (matched) { + if (el->num_values == 1) { + return ldb_kv_msg_delete_attribute( + module, ldb_kv, msg, name); + } + + ret = + ldb_kv_index_del_value(module, ldb_kv, msg, el, i); + if (ret != LDB_SUCCESS) { + return ret; + } + + if (inum_values-1) { + memmove(&el->values[i], &el->values[i+1], + sizeof(el->values[i])* + (el->num_values-(i+1))); + } + el->num_values--; + + /* per definition we find in a canonicalised message an + attribute value only once. So we are finished here */ + return LDB_SUCCESS; + } + } + + /* Not found */ + return LDB_ERR_NO_SUCH_ATTRIBUTE; +} + +/* + modify a record - internal interface + + yuck - this is O(n^2). Luckily n is usually small so we probably + get away with it, but if we ever have really large attribute lists + then we'll need to look at this again + + 'req' is optional, and is used to specify controls if supplied +*/ +int ldb_kv_modify_internal(struct ldb_module *module, + const struct ldb_message *msg, + struct ldb_request *req) +{ + struct ldb_context *ldb = ldb_module_get_ctx(module); + void *data = ldb_module_get_private(module); + struct ldb_kv_private *ldb_kv = + talloc_get_type(data, struct ldb_kv_private); + struct ldb_message *msg2; + unsigned int i, j; + int ret = LDB_SUCCESS, idx; + struct ldb_control *control_permissive = NULL; + TALLOC_CTX *mem_ctx = talloc_new(req); + + if (mem_ctx == NULL) { + return ldb_module_oom(module); + } + + if (req) { + control_permissive = ldb_request_get_control(req, + LDB_CONTROL_PERMISSIVE_MODIFY_OID); + } + + msg2 = ldb_msg_new(mem_ctx); + if (msg2 == NULL) { + ret = LDB_ERR_OTHER; + goto done; + } + + ret = ldb_kv_search_dn1( + module, msg->dn, msg2, LDB_UNPACK_DATA_FLAG_NO_DATA_ALLOC); + if (ret != LDB_SUCCESS) { + goto done; + } + + for (i=0; inum_elements; i++) { + struct ldb_message_element *el = &msg->elements[i], *el2; + struct ldb_val *vals; + const struct ldb_schema_attribute *a = ldb_schema_attribute_by_name(ldb, el->name); + const char *dn; + uint32_t options = 0; + if (control_permissive != NULL) { + options |= LDB_MSG_FIND_COMMON_REMOVE_DUPLICATES; + } + + switch (msg->elements[i].flags & LDB_FLAG_MOD_MASK) { + case LDB_FLAG_MOD_ADD: + + if (el->num_values == 0) { + ldb_asprintf_errstring(ldb, + "attribute '%s': attribute on '%s' specified, but with 0 values (illegal)", + el->name, ldb_dn_get_linearized(msg2->dn)); + ret = LDB_ERR_CONSTRAINT_VIOLATION; + goto done; + } + + /* make a copy of the array so that a permissive + * control can remove duplicates without changing the + * original values, but do not copy data as we do not + * need to keep it around once the operation is + * finished */ + if (control_permissive) { + el = talloc(msg2, struct ldb_message_element); + if (!el) { + ret = LDB_ERR_OTHER; + goto done; + } + *el = msg->elements[i]; + el->values = talloc_array(el, struct ldb_val, el->num_values); + if (el->values == NULL) { + ret = LDB_ERR_OTHER; + goto done; + } + for (j = 0; j < el->num_values; j++) { + el->values[j] = msg->elements[i].values[j]; + } + } + + if (el->num_values > 1 && ldb_kv_single_valued(a, el)) { + ldb_asprintf_errstring(ldb, "SINGLE-VALUE attribute %s on %s specified more than once", + el->name, ldb_dn_get_linearized(msg2->dn)); + ret = LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS; + goto done; + } + + /* Checks if element already exists */ + idx = ldb_kv_find_element(msg2, el->name); + if (idx == -1) { + if (ldb_kv_msg_add_element(msg2, el) != 0) { + ret = LDB_ERR_OTHER; + goto done; + } + ret = ldb_kv_index_add_element( + module, ldb_kv, msg2, el); + if (ret != LDB_SUCCESS) { + goto done; + } + } else { + j = (unsigned int) idx; + el2 = &(msg2->elements[j]); + + /* We cannot add another value on a existing one + if the attribute is single-valued */ + if (ldb_kv_single_valued(a, el)) { + ldb_asprintf_errstring(ldb, "SINGLE-VALUE attribute %s on %s specified more than once", + el->name, ldb_dn_get_linearized(msg2->dn)); + ret = LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS; + goto done; + } + + /* Check that values don't exist yet on multi- + valued attributes or aren't provided twice */ + if (!(el->flags & + LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK)) { + struct ldb_val *duplicate = NULL; + ret = ldb_msg_find_common_values(ldb, + msg2, + el, + el2, + options); + + if (ret == + LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS) { + ldb_asprintf_errstring(ldb, + "attribute '%s': value " + "#%u on '%s' already " + "exists", el->name, j, + ldb_dn_get_linearized(msg2->dn)); + goto done; + } else if (ret != LDB_SUCCESS) { + goto done; + } + + ret = ldb_msg_find_duplicate_val( + ldb, msg2, el, &duplicate, 0); + if (ret != LDB_SUCCESS) { + goto done; + } + if (duplicate != NULL) { + ldb_asprintf_errstring( + ldb, + "attribute '%s': value " + "'%.*s' on '%s' " + "provided more than " + "once in ADD", + el->name, + (int)duplicate->length, + duplicate->data, + ldb_dn_get_linearized(msg->dn)); + ret = LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS; + goto done; + } + } + + /* Now combine existing and new values to a new + attribute record */ + vals = talloc_realloc(msg2->elements, + el2->values, struct ldb_val, + el2->num_values + el->num_values); + if (vals == NULL) { + ldb_oom(ldb); + ret = LDB_ERR_OTHER; + goto done; + } + + for (j=0; jnum_values; j++) { + vals[el2->num_values + j] = + ldb_val_dup(vals, &el->values[j]); + } + + el2->values = vals; + el2->num_values += el->num_values; + + ret = ldb_kv_index_add_element( + module, ldb_kv, msg2, el); + if (ret != LDB_SUCCESS) { + goto done; + } + } + + break; + + case LDB_FLAG_MOD_REPLACE: + + if (el->num_values > 1 && ldb_kv_single_valued(a, el)) { + ldb_asprintf_errstring(ldb, "SINGLE-VALUE attribute %s on %s specified more than once", + el->name, ldb_dn_get_linearized(msg2->dn)); + ret = LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS; + goto done; + } + + /* + * We don't need to check this if we have been + * pre-screened by the repl_meta_data module + * in Samba, or someone else who can claim to + * know what they are doing. + */ + if (!(el->flags & LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK)) { + struct ldb_val *duplicate = NULL; + + ret = ldb_msg_find_duplicate_val(ldb, msg2, el, + &duplicate, 0); + if (ret != LDB_SUCCESS) { + goto done; + } + if (duplicate != NULL) { + ldb_asprintf_errstring( + ldb, + "attribute '%s': value '%.*s' " + "on '%s' provided more than " + "once in REPLACE", + el->name, + (int)duplicate->length, + duplicate->data, + ldb_dn_get_linearized(msg2->dn)); + ret = LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS; + goto done; + } + } + + /* Checks if element already exists */ + idx = ldb_kv_find_element(msg2, el->name); + if (idx != -1) { + j = (unsigned int) idx; + el2 = &(msg2->elements[j]); + + /* we consider two elements to be + * equal only if the order + * matches. This allows dbcheck to + * fix the ordering on attributes + * where order matters, such as + * objectClass + */ + if (ldb_msg_element_equal_ordered(el, el2)) { + continue; + } + + /* Delete the attribute if it exists in the DB */ + if (ldb_kv_msg_delete_attribute( + module, ldb_kv, msg2, el->name) != 0) { + ret = LDB_ERR_OTHER; + goto done; + } + } + + /* Recreate it with the new values */ + if (ldb_kv_msg_add_element(msg2, el) != 0) { + ret = LDB_ERR_OTHER; + goto done; + } + + ret = + ldb_kv_index_add_element(module, ldb_kv, msg2, el); + if (ret != LDB_SUCCESS) { + goto done; + } + + break; + + case LDB_FLAG_MOD_DELETE: + dn = ldb_dn_get_linearized(msg2->dn); + if (dn == NULL) { + ret = LDB_ERR_OTHER; + goto done; + } + + if (msg->elements[i].num_values == 0) { + /* Delete the whole attribute */ + ret = ldb_kv_msg_delete_attribute( + module, + ldb_kv, + msg2, + msg->elements[i].name); + if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE && + control_permissive) { + ret = LDB_SUCCESS; + } else { + ldb_asprintf_errstring(ldb, + "attribute '%s': no such attribute for delete on '%s'", + msg->elements[i].name, dn); + } + if (ret != LDB_SUCCESS) { + goto done; + } + } else { + /* Delete specified values from an attribute */ + for (j=0; j < msg->elements[i].num_values; j++) { + ret = ldb_kv_msg_delete_element( + module, + ldb_kv, + msg2, + msg->elements[i].name, + &msg->elements[i].values[j]); + if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE && + control_permissive) { + ret = LDB_SUCCESS; + } else if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE) { + ldb_asprintf_errstring(ldb, + "attribute '%s': no matching attribute value while deleting attribute on '%s'", + msg->elements[i].name, dn); + } + if (ret != LDB_SUCCESS) { + goto done; + } + } + } + break; + default: + ldb_asprintf_errstring(ldb, + "attribute '%s': invalid modify flags on '%s': 0x%x", + msg->elements[i].name, ldb_dn_get_linearized(msg->dn), + msg->elements[i].flags & LDB_FLAG_MOD_MASK); + ret = LDB_ERR_PROTOCOL_ERROR; + goto done; + } + } + + ret = ldb_kv_store(module, msg2, TDB_MODIFY); + if (ret != LDB_SUCCESS) { + goto done; + } + + ret = ldb_kv_modified(module, msg2->dn); + if (ret != LDB_SUCCESS) { + goto done; + } + +done: + TALLOC_FREE(mem_ctx); + return ret; +} + +/* + modify a record +*/ +static int ldb_kv_modify(struct ldb_kv_context *ctx) +{ + struct ldb_module *module = ctx->module; + struct ldb_request *req = ctx->req; + int ret = LDB_SUCCESS; + + ret = ldb_kv_check_special_dn(module, req->op.mod.message); + if (ret != LDB_SUCCESS) { + return ret; + } + + ldb_request_set_state(req, LDB_ASYNC_PENDING); + + if (ldb_kv_cache_load(module) != 0) { + return LDB_ERR_OPERATIONS_ERROR; + } + + ret = ldb_kv_modify_internal(module, req->op.mod.message, req); + + return ret; +} + +/* + rename a record +*/ +static int ldb_kv_rename(struct ldb_kv_context *ctx) +{ + struct ldb_module *module = ctx->module; + void *data = ldb_module_get_private(module); + struct ldb_kv_private *ldb_kv = + talloc_get_type(data, struct ldb_kv_private); + struct ldb_request *req = ctx->req; + struct ldb_message *msg; + int ret = LDB_SUCCESS; + TDB_DATA tdb_key, tdb_key_old; + struct ldb_dn *db_dn; + + ldb_request_set_state(req, LDB_ASYNC_PENDING); + + if (ldb_kv_cache_load(ctx->module) != 0) { + return LDB_ERR_OPERATIONS_ERROR; + } + + msg = ldb_msg_new(ctx); + if (msg == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + + /* we need to fetch the old record to re-add under the new name */ + ret = ldb_kv_search_dn1(module, + req->op.rename.olddn, + msg, + LDB_UNPACK_DATA_FLAG_NO_DATA_ALLOC); + if (ret != LDB_SUCCESS) { + /* not finding the old record is an error */ + return ret; + } + + /* We need to, before changing the DB, check if the new DN + * exists, so we can return this error to the caller with an + * unmodified DB + * + * Even in GUID index mode we use ltdb_key_dn() as we are + * trying to figure out if this is just a case rename + */ + tdb_key = ldb_kv_key_dn(module, msg, req->op.rename.newdn); + if (!tdb_key.dptr) { + talloc_free(msg); + return LDB_ERR_OPERATIONS_ERROR; + } + + tdb_key_old = ldb_kv_key_dn(module, msg, req->op.rename.olddn); + if (!tdb_key_old.dptr) { + talloc_free(msg); + talloc_free(tdb_key.dptr); + return LDB_ERR_OPERATIONS_ERROR; + } + + /* + * Only declare a conflict if the new DN already exists, + * and it isn't a case change on the old DN + */ + if (tdb_key_old.dsize != tdb_key.dsize + || memcmp(tdb_key.dptr, tdb_key_old.dptr, tdb_key.dsize) != 0) { + ret = ldb_kv_search_base( + module, msg, req->op.rename.newdn, &db_dn); + if (ret == LDB_SUCCESS) { + ret = LDB_ERR_ENTRY_ALREADY_EXISTS; + } else if (ret == LDB_ERR_NO_SUCH_OBJECT) { + ret = LDB_SUCCESS; + } + } + + /* finding the new record already in the DB is an error */ + + if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) { + ldb_asprintf_errstring(ldb_module_get_ctx(module), + "Entry %s already exists", + ldb_dn_get_linearized(req->op.rename.newdn)); + } + if (ret != LDB_SUCCESS) { + talloc_free(tdb_key_old.dptr); + talloc_free(tdb_key.dptr); + talloc_free(msg); + return ret; + } + + talloc_free(tdb_key_old.dptr); + talloc_free(tdb_key.dptr); + + /* Always delete first then add, to avoid conflicts with + * unique indexes. We rely on the transaction to make this + * atomic + */ + ret = ldb_kv_delete_internal(module, msg->dn); + if (ret != LDB_SUCCESS) { + talloc_free(msg); + return ret; + } + + msg->dn = ldb_dn_copy(msg, req->op.rename.newdn); + if (msg->dn == NULL) { + talloc_free(msg); + return LDB_ERR_OPERATIONS_ERROR; + } + + /* We don't check single value as we can have more than 1 with + * deleted attributes. We could go through all elements but that's + * maybe not the most efficient way + */ + ret = ldb_kv_add_internal(module, ldb_kv, msg, false); + + talloc_free(msg); + + return ret; +} + +static int ldb_kv_start_trans(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(ldb_kv->module), + __location__ + ": Reusing ldb opend by pid %d in " + "process %d\n", + ldb_kv->pid, + pid); + return LDB_ERR_PROTOCOL_ERROR; + } + + /* Do not take out the transaction lock on a read-only DB */ + if (ldb_kv->read_only) { + return LDB_ERR_UNWILLING_TO_PERFORM; + } + + if (ldb_kv->kv_ops->begin_write(ldb_kv) != 0) { + return ldb_kv->kv_ops->error(ldb_kv); + } + + ldb_kv_index_transaction_start(module); + + ldb_kv->reindex_failed = false; + + return LDB_SUCCESS; +} + +/* + * Forward declaration to allow prepare_commit to in fact abort the + * transaction + */ +static int ldb_kv_del_trans(struct ldb_module *module); + +static int ldb_kv_prepare_commit(struct ldb_module *module) +{ + int ret; + 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 (!ldb_kv->kv_ops->transaction_active(ldb_kv)) { + ldb_set_errstring(ldb_module_get_ctx(module), + "ltdb_prepare_commit() called " + "without transaction active"); + return LDB_ERR_OPERATIONS_ERROR; + } + + /* + * Check if the last re-index failed. + * + * This can happen if for example a duplicate value was marked + * unique. We must not write a partial re-index into the DB. + */ + if (ldb_kv->reindex_failed) { + /* + * We must instead abort the transaction so we get the + * old values and old index back + */ + ldb_kv_del_trans(module); + ldb_set_errstring(ldb_module_get_ctx(module), + "Failure during re-index, so " + "transaction must be aborted."); + return LDB_ERR_OPERATIONS_ERROR; + } + + ret = ldb_kv_index_transaction_commit(module); + if (ret != LDB_SUCCESS) { + ldb_kv->kv_ops->abort_write(ldb_kv); + return ret; + } + + if (ldb_kv->kv_ops->prepare_write(ldb_kv) != 0) { + ret = ldb_kv->kv_ops->error(ldb_kv); + ldb_debug_set(ldb_module_get_ctx(module), + LDB_DEBUG_FATAL, + "Failure during " + "prepare_write): %s -> %s", + ldb_kv->kv_ops->errorstr(ldb_kv), + ldb_strerror(ret)); + return ret; + } + + ldb_kv->prepared_commit = true; + + return LDB_SUCCESS; +} + +static int ldb_kv_end_trans(struct ldb_module *module) +{ + int ret; + void *data = ldb_module_get_private(module); + struct ldb_kv_private *ldb_kv = + talloc_get_type(data, struct ldb_kv_private); + + if (!ldb_kv->prepared_commit) { + ret = ldb_kv_prepare_commit(module); + if (ret != LDB_SUCCESS) { + return ret; + } + } + + ldb_kv->prepared_commit = false; + + if (ldb_kv->kv_ops->finish_write(ldb_kv) != 0) { + ret = ldb_kv->kv_ops->error(ldb_kv); + ldb_asprintf_errstring( + ldb_module_get_ctx(module), + "Failure during tdb_transaction_commit(): %s -> %s", + ldb_kv->kv_ops->errorstr(ldb_kv), + ldb_strerror(ret)); + return ret; + } + + return LDB_SUCCESS; +} + +static int ldb_kv_del_trans(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); + + if (ldb_kv_index_transaction_cancel(module) != 0) { + ldb_kv->kv_ops->abort_write(ldb_kv); + return ldb_kv->kv_ops->error(ldb_kv); + } + + ldb_kv->kv_ops->abort_write(ldb_kv); + return LDB_SUCCESS; +} + +/* + return sequenceNumber from @BASEINFO +*/ +static int ldb_kv_sequence_number(struct ldb_kv_context *ctx, + struct ldb_extended **ext) +{ + struct ldb_context *ldb; + struct ldb_module *module = ctx->module; + struct ldb_request *req = ctx->req; + void *data = ldb_module_get_private(module); + struct ldb_kv_private *ldb_kv = + talloc_get_type(data, struct ldb_kv_private); + TALLOC_CTX *tmp_ctx = NULL; + struct ldb_seqnum_request *seq; + struct ldb_seqnum_result *res; + struct ldb_message *msg = NULL; + struct ldb_dn *dn; + const char *date; + int ret = LDB_SUCCESS; + + ldb = ldb_module_get_ctx(module); + + seq = talloc_get_type(req->op.extended.data, + struct ldb_seqnum_request); + if (seq == NULL) { + return LDB_ERR_OPERATIONS_ERROR; + } + + ldb_request_set_state(req, LDB_ASYNC_PENDING); + + if (ldb_kv->kv_ops->lock_read(module) != 0) { + return LDB_ERR_OPERATIONS_ERROR; + } + + res = talloc_zero(req, struct ldb_seqnum_result); + if (res == NULL) { + ret = LDB_ERR_OPERATIONS_ERROR; + goto done; + } + + tmp_ctx = talloc_new(req); + if (tmp_ctx == NULL) { + ret = LDB_ERR_OPERATIONS_ERROR; + goto done; + } + + dn = ldb_dn_new(tmp_ctx, ldb, LTDB_BASEINFO); + if (dn == NULL) { + ret = LDB_ERR_OPERATIONS_ERROR; + goto done; + } + + msg = ldb_msg_new(tmp_ctx); + if (msg == NULL) { + ret = LDB_ERR_OPERATIONS_ERROR; + goto done; + } + + ret = ldb_kv_search_dn1(module, dn, msg, 0); + if (ret != LDB_SUCCESS) { + goto done; + } + + switch (seq->type) { + case LDB_SEQ_HIGHEST_SEQ: + res->seq_num = ldb_msg_find_attr_as_uint64(msg, LTDB_SEQUENCE_NUMBER, 0); + break; + case LDB_SEQ_NEXT: + res->seq_num = ldb_msg_find_attr_as_uint64(msg, LTDB_SEQUENCE_NUMBER, 0); + res->seq_num++; + break; + case LDB_SEQ_HIGHEST_TIMESTAMP: + date = ldb_msg_find_attr_as_string(msg, LTDB_MOD_TIMESTAMP, NULL); + if (date) { + res->seq_num = ldb_string_to_time(date); + } else { + res->seq_num = 0; + /* zero is as good as anything when we don't know */ + } + break; + } + + *ext = talloc_zero(req, struct ldb_extended); + if (*ext == NULL) { + ret = LDB_ERR_OPERATIONS_ERROR; + goto done; + } + (*ext)->oid = LDB_EXTENDED_SEQUENCE_NUMBER; + (*ext)->data = talloc_steal(*ext, res); + +done: + talloc_free(tmp_ctx); + + ldb_kv->kv_ops->unlock_read(module); + return ret; +} + +static void ldb_kv_request_done(struct ldb_kv_context *ctx, int error) +{ + struct ldb_context *ldb; + struct ldb_request *req; + struct ldb_reply *ares; + + ldb = ldb_module_get_ctx(ctx->module); + req = ctx->req; + + /* if we already returned an error just return */ + if (ldb_request_get_status(req) != LDB_SUCCESS) { + return; + } + + ares = talloc_zero(req, struct ldb_reply); + if (!ares) { + ldb_oom(ldb); + req->callback(req, NULL); + return; + } + ares->type = LDB_REPLY_DONE; + ares->error = error; + + req->callback(req, ares); +} + +static void ldb_kv_timeout(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval t, + void *private_data) +{ + struct ldb_kv_context *ctx; + ctx = talloc_get_type(private_data, struct ldb_kv_context); + + if (!ctx->request_terminated) { + /* request is done now */ + ldb_kv_request_done(ctx, LDB_ERR_TIME_LIMIT_EXCEEDED); + } + + if (ctx->spy) { + /* neutralize the spy */ + ctx->spy->ctx = NULL; + ctx->spy = NULL; + } + talloc_free(ctx); +} + +static void ldb_kv_request_extended_done(struct ldb_kv_context *ctx, + struct ldb_extended *ext, + int error) +{ + struct ldb_context *ldb; + struct ldb_request *req; + struct ldb_reply *ares; + + ldb = ldb_module_get_ctx(ctx->module); + req = ctx->req; + + /* if we already returned an error just return */ + if (ldb_request_get_status(req) != LDB_SUCCESS) { + return; + } + + ares = talloc_zero(req, struct ldb_reply); + if (!ares) { + ldb_oom(ldb); + req->callback(req, NULL); + return; + } + ares->type = LDB_REPLY_DONE; + ares->response = ext; + ares->error = error; + + req->callback(req, ares); +} + +static void ldb_kv_handle_extended(struct ldb_kv_context *ctx) +{ + struct ldb_extended *ext = NULL; + int ret; + + if (strcmp(ctx->req->op.extended.oid, + LDB_EXTENDED_SEQUENCE_NUMBER) == 0) { + /* get sequence number */ + ret = ldb_kv_sequence_number(ctx, &ext); + } else { + /* not recognized */ + ret = LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION; + } + + ldb_kv_request_extended_done(ctx, ext, ret); +} + +static void ldb_kv_callback(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval t, + void *private_data) +{ + struct ldb_kv_context *ctx; + int ret; + + ctx = talloc_get_type(private_data, struct ldb_kv_context); + + if (ctx->request_terminated) { + goto done; + } + + switch (ctx->req->operation) { + case LDB_SEARCH: + ret = ldb_kv_search(ctx); + break; + case LDB_ADD: + ret = ldb_kv_add(ctx); + break; + case LDB_MODIFY: + ret = ldb_kv_modify(ctx); + break; + case LDB_DELETE: + ret = ldb_kv_delete(ctx); + break; + case LDB_RENAME: + ret = ldb_kv_rename(ctx); + break; + case LDB_EXTENDED: + ldb_kv_handle_extended(ctx); + goto done; + default: + /* no other op supported */ + ret = LDB_ERR_PROTOCOL_ERROR; + } + + if (!ctx->request_terminated) { + /* request is done now */ + ldb_kv_request_done(ctx, ret); + } + +done: + if (ctx->spy) { + /* neutralize the spy */ + ctx->spy->ctx = NULL; + ctx->spy = NULL; + } + talloc_free(ctx); +} + +static int ldb_kv_request_destructor(void *ptr) +{ + struct ldb_kv_req_spy *spy = + talloc_get_type(ptr, struct ldb_kv_req_spy); + + if (spy->ctx != NULL) { + spy->ctx->spy = NULL; + spy->ctx->request_terminated = true; + spy->ctx = NULL; + } + + return 0; +} + +static int ldb_kv_handle_request(struct ldb_module *module, + struct ldb_request *req) +{ + struct ldb_control *control_permissive; + struct ldb_context *ldb; + struct tevent_context *ev; + struct ldb_kv_context *ac; + struct tevent_timer *te; + struct timeval tv; + unsigned int i; + + ldb = ldb_module_get_ctx(module); + + control_permissive = ldb_request_get_control(req, + LDB_CONTROL_PERMISSIVE_MODIFY_OID); + + for (i = 0; req->controls && req->controls[i]; i++) { + if (req->controls[i]->critical && + req->controls[i] != control_permissive) { + ldb_asprintf_errstring(ldb, "Unsupported critical extension %s", + req->controls[i]->oid); + return LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION; + } + } + + if (req->starttime == 0 || req->timeout == 0) { + ldb_set_errstring(ldb, "Invalid timeout settings"); + return LDB_ERR_TIME_LIMIT_EXCEEDED; + } + + ev = ldb_handle_get_event_context(req->handle); + + ac = talloc_zero(ldb, struct ldb_kv_context); + if (ac == NULL) { + ldb_oom(ldb); + return LDB_ERR_OPERATIONS_ERROR; + } + + ac->module = module; + ac->req = req; + + tv.tv_sec = 0; + tv.tv_usec = 0; + te = tevent_add_timer(ev, ac, tv, ldb_kv_callback, ac); + if (NULL == te) { + talloc_free(ac); + return LDB_ERR_OPERATIONS_ERROR; + } + + if (req->timeout > 0) { + tv.tv_sec = req->starttime + req->timeout; + tv.tv_usec = 0; + ac->timeout_event = + tevent_add_timer(ev, ac, tv, ldb_kv_timeout, ac); + if (NULL == ac->timeout_event) { + talloc_free(ac); + return LDB_ERR_OPERATIONS_ERROR; + } + } + + /* set a spy so that we do not try to use the request context + * if it is freed before ltdb_callback fires */ + ac->spy = talloc(req, struct ldb_kv_req_spy); + if (NULL == ac->spy) { + talloc_free(ac); + return LDB_ERR_OPERATIONS_ERROR; + } + ac->spy->ctx = ac; + + talloc_set_destructor((TALLOC_CTX *)ac->spy, ldb_kv_request_destructor); + + return LDB_SUCCESS; +} + +static int ldb_kv_init_rootdse(struct ldb_module *module) +{ + /* ignore errors on this - we expect it for non-sam databases */ + ldb_mod_register_control(module, LDB_CONTROL_PERMISSIVE_MODIFY_OID); + + /* there can be no module beyond the backend, just return */ + return LDB_SUCCESS; +} + +static int ldb_kv_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); + return ldb_kv->kv_ops->lock_read(module); +} + +static int ldb_kv_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); + return ldb_kv->kv_ops->unlock_read(module); +} + +static const struct ldb_module_ops ldb_kv_ops = { + .name = "tdb", + .init_context = ldb_kv_init_rootdse, + .search = ldb_kv_handle_request, + .add = ldb_kv_handle_request, + .modify = ldb_kv_handle_request, + .del = ldb_kv_handle_request, + .rename = ldb_kv_handle_request, + .extended = ldb_kv_handle_request, + .start_transaction = ldb_kv_start_trans, + .end_transaction = ldb_kv_end_trans, + .prepare_commit = ldb_kv_prepare_commit, + .del_transaction = ldb_kv_del_trans, + .read_lock = ldb_kv_lock_read, + .read_unlock = ldb_kv_unlock_read, +}; + +int ldb_kv_init_store(struct ldb_kv_private *ldb_kv, + const char *name, + struct ldb_context *ldb, + const char *options[], + struct ldb_module **_module) +{ + if (getenv("LDB_WARN_UNINDEXED")) { + ldb_kv->warn_unindexed = true; + } + + if (getenv("LDB_WARN_REINDEX")) { + ldb_kv->warn_reindex = true; + } + + ldb_kv->sequence_number = 0; + + ldb_kv->pid = getpid(); + + ldb_kv->module = ldb_module_new(ldb, ldb, name, &ldb_kv_ops); + if (!ldb_kv->module) { + ldb_oom(ldb); + talloc_free(ldb_kv); + return LDB_ERR_OPERATIONS_ERROR; + } + ldb_module_set_private(ldb_kv->module, ldb_kv); + talloc_steal(ldb_kv->module, ldb_kv); + + if (ldb_kv_cache_load(ldb_kv->module) != 0) { + ldb_asprintf_errstring(ldb, "Unable to load ltdb cache " + "records for backend '%s'", name); + talloc_free(ldb_kv->module); + return LDB_ERR_OPERATIONS_ERROR; + } + + *_module = ldb_kv->module; + /* + * Set or override the maximum key length + * + * The ldb_mdb code will have set this to 511, but our tests + * set this even smaller (to make the tests more practical). + * + * This must only be used for the selftest as the length + * becomes encoded in the index keys. + */ + { + const char *len_str = + ldb_options_find(ldb, options, + "max_key_len_for_self_test"); + if (len_str != NULL) { + unsigned len = strtoul(len_str, NULL, 0); + ldb_kv->max_key_length = len; + } + } + + /* + * Override full DB scans + * + * A full DB scan is expensive on a large database. This + * option is for testing to show that the full DB scan is not + * triggered. + */ + { + const char *len_str = + ldb_options_find(ldb, options, + "disable_full_db_scan_for_self_test"); + if (len_str != NULL) { + ldb_kv->disable_full_db_scan = true; + } + } + + return LDB_SUCCESS; +} diff --git a/lib/ldb/ldb_key_value/ldb_kv.h b/lib/ldb/ldb_key_value/ldb_kv.h new file mode 100644 index 00000000000..13fe1256766 --- /dev/null +++ b/lib/ldb/ldb_key_value/ldb_kv.h @@ -0,0 +1,265 @@ +#include "replace.h" +#include "system/filesys.h" +#include "system/time.h" +#include "tdb.h" +#include "ldb_module.h" + +struct ldb_kv_private; +typedef int (*ldb_kv_traverse_fn)(struct ldb_kv_private *ldb_kv, + struct ldb_val key, + struct ldb_val data, + void *ctx); + +struct kv_db_ops { + int (*store)(struct ldb_kv_private *ldb_kv, + struct ldb_val key, + struct ldb_val data, + int flags); + int (*delete)(struct ldb_kv_private *ldb_kv, struct ldb_val key); + int (*iterate)(struct ldb_kv_private *ldb_kv, + ldb_kv_traverse_fn fn, + void *ctx); + int (*update_in_iterate)(struct ldb_kv_private *ldb_kv, + struct ldb_val key, + struct ldb_val key2, + struct ldb_val data, + void *ctx); + int (*fetch_and_parse)(struct ldb_kv_private *ldb_kv, + struct ldb_val key, + int (*parser)(struct ldb_val key, + struct ldb_val data, + void *private_data), + void *ctx); + int (*lock_read)(struct ldb_module *); + int (*unlock_read)(struct ldb_module *); + int (*begin_write)(struct ldb_kv_private *); + int (*prepare_write)(struct ldb_kv_private *); + int (*abort_write)(struct ldb_kv_private *); + int (*finish_write)(struct ldb_kv_private *); + int (*error)(struct ldb_kv_private *ldb_kv); + const char *(*errorstr)(struct ldb_kv_private *ldb_kv); + const char *(*name)(struct ldb_kv_private *ldb_kv); + bool (*has_changed)(struct ldb_kv_private *ldb_kv); + bool (*transaction_active)(struct ldb_kv_private *ldb_kv); +}; + +/* this private structure is used by the key value backends in the + ldb_context */ +struct ldb_kv_private { + const struct kv_db_ops *kv_ops; + struct ldb_module *module; + TDB_CONTEXT *tdb; + struct lmdb_private *lmdb_private; + unsigned int connect_flags; + + unsigned long long sequence_number; + + /* the low level tdb seqnum - used to avoid loading BASEINFO when + possible */ + int tdb_seqnum; + + struct ldb_kv_cache { + struct ldb_message *indexlist; + bool one_level_indexes; + bool attribute_indexes; + const char *GUID_index_attribute; + const char *GUID_index_dn_component; + } *cache; + + + bool check_base; + bool disallow_dn_filter; + struct ldb_kv_idxptr *idxptr; + bool prepared_commit; + int read_lock_count; + + bool warn_unindexed; + bool warn_reindex; + + bool read_only; + + bool reindex_failed; + + const struct ldb_schema_syntax *GUID_index_syntax; + + /* + * Maximum index key length. If non zero keys longer than this length + * will be truncated for non unique indexes. Keys for unique indexes + * greater than this length will be rejected. + */ + unsigned max_key_length; + + /* + * To allow testing that ensures the DB does not fall back + * to a full scan + */ + bool disable_full_db_scan; + + /* + * The PID that opened this database so we don't work in a + * fork()ed child. + */ + pid_t pid; +}; + +struct ldb_kv_context { + struct ldb_module *module; + struct ldb_request *req; + + bool request_terminated; + struct ldb_kv_req_spy *spy; + + /* search stuff */ + const struct ldb_parse_tree *tree; + struct ldb_dn *base; + enum ldb_scope scope; + const char * const *attrs; + struct tevent_timer *timeout_event; + + /* error handling */ + int error; +}; + +struct ldb_kv_reindex_context { + struct ldb_module *module; + int error; + uint32_t count; +}; + + +/* special record types */ +#define LTDB_INDEX "@INDEX" +#define LTDB_INDEXLIST "@INDEXLIST" +#define LTDB_IDX "@IDX" +#define LTDB_IDXVERSION "@IDXVERSION" +#define LTDB_IDXATTR "@IDXATTR" +#define LTDB_IDXONE "@IDXONE" +#define LTDB_IDXDN "@IDXDN" +#define LTDB_IDXGUID "@IDXGUID" +#define LTDB_IDX_DN_GUID "@IDX_DN_GUID" + +/* + * This will be used to indicate when a new, yet to be developed + * sub-database version of the indicies are in use, to ensure we do + * not load future databases unintentionally. + */ + +#define LTDB_IDX_LMDB_SUBDB "@IDX_LMDB_SUBDB" + +#define LTDB_BASEINFO "@BASEINFO" +#define LTDB_OPTIONS "@OPTIONS" +#define LTDB_ATTRIBUTES "@ATTRIBUTES" + +/* special attribute types */ +#define LTDB_SEQUENCE_NUMBER "sequenceNumber" +#define LTDB_CHECK_BASE "checkBaseOnSearch" +#define LTDB_DISALLOW_DN_FILTER "disallowDNFilter" +#define LTDB_MOD_TIMESTAMP "whenChanged" +#define LTDB_OBJECTCLASS "objectClass" + +/* DB keys */ +#define LTDB_GUID_KEY_PREFIX "GUID=" +#define LTDB_GUID_SIZE 16 +#define LTDB_GUID_KEY_SIZE (LTDB_GUID_SIZE + sizeof(LTDB_GUID_KEY_PREFIX) - 1) + +/* + * The following definitions come from lib/ldb/ldb_key_value/ldb_kv_cache.c + */ + +int ldb_kv_cache_reload(struct ldb_module *module); +int ldb_kv_cache_load(struct ldb_module *module); +int ldb_kv_increase_sequence_number(struct ldb_module *module); +int ldb_kv_check_at_attributes_values(const struct ldb_val *value); + +/* + * The following definitions come from lib/ldb/ldb_key_value/ldb_kv_index.c + */ + +struct ldb_parse_tree; + +int ldb_kv_search_indexed(struct ldb_kv_context *ctx, uint32_t *); +int ldb_kv_index_add_new(struct ldb_module *module, + struct ldb_kv_private *ldb_kv, + const struct ldb_message *msg); +int ldb_kv_index_delete(struct ldb_module *module, + const struct ldb_message *msg); +int ldb_kv_index_del_element(struct ldb_module *module, + struct ldb_kv_private *ldb_kv, + const struct ldb_message *msg, + struct ldb_message_element *el); +int ldb_kv_index_add_element(struct ldb_module *module, + struct ldb_kv_private *ldb_kv, + const struct ldb_message *msg, + struct ldb_message_element *el); +int ldb_kv_index_del_value(struct ldb_module *module, + struct ldb_kv_private *ldb_kv, + const struct ldb_message *msg, + struct ldb_message_element *el, + unsigned int v_idx); +int ldb_kv_reindex(struct ldb_module *module); +int ldb_kv_index_transaction_start(struct ldb_module *module); +int ldb_kv_index_transaction_commit(struct ldb_module *module); +int ldb_kv_index_transaction_cancel(struct ldb_module *module); +int ldb_kv_key_dn_from_idx(struct ldb_module *module, + struct ldb_kv_private *ldb_kv, + TALLOC_CTX *mem_ctx, + struct ldb_dn *dn, + TDB_DATA *tdb_key); + +/* + * The following definitions come from lib/ldb/ldb_key_value/ldb_kv_search.c + */ +int ldb_kv_search_dn1(struct ldb_module *module, + struct ldb_dn *dn, + struct ldb_message *msg, + unsigned int unpack_flags); +int ldb_kv_search_base(struct ldb_module *module, + TALLOC_CTX *mem_ctx, + struct ldb_dn *dn, + struct ldb_dn **ret_dn); +int ldb_kv_search_key(struct ldb_module *module, + struct ldb_kv_private *ldb_kv, + struct TDB_DATA tdb_key, + struct ldb_message *msg, + unsigned int unpack_flags); +int ldb_kv_filter_attrs(TALLOC_CTX *mem_ctx, + const struct ldb_message *msg, + const char *const *attrs, + struct ldb_message **filtered_msg); +int ldb_kv_search(struct ldb_kv_context *ctx); + +/* + * The following definitions come from lib/ldb/ldb_key_value/ldb_kv.c */ +/* + * Determine if this key could hold a record. We allow the new GUID + * index, the old DN index and a possible future ID= + */ +bool ldb_kv_key_is_record(TDB_DATA key); +TDB_DATA ldb_kv_key_dn(struct ldb_module *module, + TALLOC_CTX *mem_ctx, + struct ldb_dn *dn); +TDB_DATA ldb_kv_key_msg(struct ldb_module *module, + TALLOC_CTX *mem_ctx, + const struct ldb_message *msg); +int ldb_kv_guid_to_key(struct ldb_module *module, + struct ldb_kv_private *ldb_kv, + const struct ldb_val *GUID_val, + TDB_DATA *key); +int ldb_kv_idx_to_key(struct ldb_module *module, + struct ldb_kv_private *ldb_kv, + TALLOC_CTX *mem_ctx, + const struct ldb_val *idx_val, + TDB_DATA *key); +int ldb_kv_store(struct ldb_module *module, + const struct ldb_message *msg, + int flgs); +int ldb_kv_modify_internal(struct ldb_module *module, + const struct ldb_message *msg, + struct ldb_request *req); +int ldb_kv_delete_noindex(struct ldb_module *module, + const struct ldb_message *msg); +int ldb_kv_init_store(struct ldb_kv_private *ldb_kv, + const char *name, + struct ldb_context *ldb, + const char *options[], + struct ldb_module **_module); diff --git a/lib/ldb/ldb_tdb/ldb_cache.c b/lib/ldb/ldb_key_value/ldb_kv_cache.c similarity index 99% rename from lib/ldb/ldb_tdb/ldb_cache.c rename to lib/ldb/ldb_key_value/ldb_kv_cache.c index 9e81b61592f..f0bc31b60a5 100644 --- a/lib/ldb/ldb_tdb/ldb_cache.c +++ b/lib/ldb/ldb_key_value/ldb_kv_cache.c @@ -1,4 +1,4 @@ -/* +/* ldb database library Copyright (C) Andrew Tridgell 2004 @@ -6,7 +6,7 @@ ** 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 @@ -24,14 +24,14 @@ /* * Name: ldb * - * Component: ldb tdb cache functions + * Component: ldb key value cache functions * * Description: cache special records in a ldb/tdb * * Author: Andrew Tridgell */ -#include "ldb_tdb.h" +#include "ldb_kv.h" #include "ldb_private.h" #define LTDB_FLAG_CASE_INSENSITIVE (1<<0) @@ -184,7 +184,7 @@ static int ldb_kv_attributes_load(struct ldb_module *module) } else if (flags == 0) { syntax = LDB_SYNTAX_OCTET_STRING; } else { - ldb_debug(ldb, LDB_DEBUG_ERROR, + ldb_debug(ldb, LDB_DEBUG_ERROR, "Invalid flag combination 0x%x for '%s' " "in @ATTRIBUTES", flags, attrs_msg->elements[i].name); @@ -193,7 +193,7 @@ static int ldb_kv_attributes_load(struct ldb_module *module) s = ldb_standard_syntax_by_name(ldb, syntax); if (s == NULL) { - ldb_debug(ldb, LDB_DEBUG_ERROR, + ldb_debug(ldb, LDB_DEBUG_ERROR, "Invalid attribute syntax '%s' for '%s' " "in @ATTRIBUTES", syntax, attrs_msg->elements[i].name); @@ -488,7 +488,7 @@ int ldb_kv_cache_load(struct ldb_module *module) if (r != LDB_SUCCESS && r != LDB_ERR_NO_SUCH_OBJECT) { goto failed_and_unlock; } - + /* set flags if they do exist */ if (r == LDB_SUCCESS) { ldb_kv->check_base = @@ -660,4 +660,3 @@ int ldb_kv_check_at_attributes_values(const struct ldb_val *value) return -1; } - diff --git a/lib/ldb/ldb_tdb/ldb_index.c b/lib/ldb/ldb_key_value/ldb_kv_index.c similarity index 99% rename from lib/ldb/ldb_tdb/ldb_index.c rename to lib/ldb/ldb_key_value/ldb_kv_index.c index ce107da30b5..4cefe0a69b3 100644 --- a/lib/ldb/ldb_tdb/ldb_index.c +++ b/lib/ldb/ldb_key_value/ldb_kv_index.c @@ -24,16 +24,16 @@ /* * Name: ldb * - * Component: ldb tdb backend - indexing + * Component: ldb key value backend - indexing * - * Description: indexing routines for ldb tdb backend + * Description: indexing routines for ldb key value backend * * Author: Andrew Tridgell */ /* -LDB Index design and choice of TDB key: +LDB Index design and choice of key: ======================================= LDB has index records held as LDB objects with a special record like: @@ -144,7 +144,8 @@ ldb_schema_set_override_GUID_index() must be called. */ -#include "ldb_tdb.h" +#include "ldb_kv.h" +#include "../ldb_tdb/ldb_tdb.h" #include "ldb_private.h" #include "lib/util/binsearch.h" @@ -970,7 +971,7 @@ static struct ldb_dn *ldb_kv_index_key(struct ldb_context *ldb, return NULL; } vstr_len = strlen(vstr); - /* + /* * Overflow here is not critical as we only use this * to choose the printf truncation */ @@ -2865,7 +2866,7 @@ static int re_key(struct ldb_kv_private *ldb_kv, .dptr = ldb_key.data, .dsize = ldb_key.length }; - + ldb = ldb_module_get_ctx(module); if (key.dsize > 4 && @@ -2877,7 +2878,7 @@ static int re_key(struct ldb_kv_private *ldb_kv, if (is_record == false) { return 0; } - + msg = ldb_msg_new(module); if (msg == NULL) { return -1; @@ -2905,7 +2906,7 @@ static int re_key(struct ldb_kv_private *ldb_kv, talloc_free(msg); return -1; } - + /* check if the DN key has changed, perhaps due to the case insensitivity of an element changing, or a change from DN to GUID keys */ @@ -2960,7 +2961,7 @@ static int re_index(struct ldb_kv_private *ldb_kv, }; int ret; bool is_record; - + ldb = ldb_module_get_ctx(module); if (key.dsize > 4 && @@ -2972,7 +2973,7 @@ static int re_index(struct ldb_kv_private *ldb_kv, if (is_record == false) { return 0; } - + msg = ldb_msg_new(module); if (msg == NULL) { return -1; diff --git a/lib/ldb/ldb_tdb/ldb_search.c b/lib/ldb/ldb_key_value/ldb_kv_search.c similarity index 98% rename from lib/ldb/ldb_tdb/ldb_search.c rename to lib/ldb/ldb_key_value/ldb_kv_search.c index 0138f9262b2..5bb780a2788 100644 --- a/lib/ldb/ldb_tdb/ldb_search.c +++ b/lib/ldb/ldb_key_value/ldb_kv_search.c @@ -1,4 +1,4 @@ -/* +/* ldb database library Copyright (C) Andrew Tridgell 2004 @@ -6,7 +6,7 @@ ** 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 @@ -31,14 +31,13 @@ * Author: Andrew Tridgell */ -#include "ldb_tdb.h" +#include "ldb_kv.h" #include "ldb_private.h" -#include /* add one element to a message */ -static int msg_add_element(struct ldb_message *ret, +static int msg_add_element(struct ldb_message *ret, const struct ldb_message_element *el, int check_duplicates) { @@ -55,7 +54,7 @@ static int msg_add_element(struct ldb_message *ret, return -1; } ret->elements = e2; - + elnew = &e2[ret->num_elements]; elnew->name = talloc_strdup(ret->elements, el->name); @@ -745,14 +744,14 @@ int ldb_kv_search(struct ldb_kv_context *ctx) /* Check what we should do with a NULL dn */ switch (req->op.search.scope) { case LDB_SCOPE_BASE: - ldb_asprintf_errstring(ldb, + ldb_asprintf_errstring(ldb, "NULL Base DN invalid for a base search"); ret = LDB_ERR_INVALID_DN_SYNTAX; break; case LDB_SCOPE_ONELEVEL: - ldb_asprintf_errstring(ldb, + ldb_asprintf_errstring(ldb, "NULL Base DN invalid for a one-level search"); - ret = LDB_ERR_INVALID_DN_SYNTAX; + ret = LDB_ERR_INVALID_DN_SYNTAX; break; case LDB_SCOPE_SUBTREE: default: @@ -762,8 +761,8 @@ int ldb_kv_search(struct ldb_kv_context *ctx) } else if (ldb_dn_is_valid(req->op.search.base) == false) { /* We don't want invalid base DNs here */ - ldb_asprintf_errstring(ldb, - "Invalid Base DN: %s", + ldb_asprintf_errstring(ldb, + "Invalid Base DN: %s", ldb_dn_get_linearized(req->op.search.base)); ret = LDB_ERR_INVALID_DN_SYNTAX; @@ -793,11 +792,11 @@ int ldb_kv_search(struct ldb_kv_context *ctx) module, ctx, req->op.search.base, &ctx->base); if (ret == LDB_ERR_NO_SUCH_OBJECT) { - ldb_asprintf_errstring(ldb, - "No such Base DN: %s", + ldb_asprintf_errstring(ldb, + "No such Base DN: %s", ldb_dn_get_linearized(req->op.search.base)); } - + } else { /* If we are not checking the base DN life is easy */ ret = LDB_SUCCESS; @@ -810,12 +809,12 @@ int ldb_kv_search(struct ldb_kv_context *ctx) if (ret == LDB_ERR_NO_SUCH_OBJECT) { /* Not in the index, therefore OK! */ ret = LDB_SUCCESS; - + } /* Check if we got just a normal error. * In that case proceed to a full search unless we got a * callback error */ - if ( ! ctx->request_terminated && ret != LDB_SUCCESS) { + if (!ctx->request_terminated && ret != LDB_SUCCESS) { /* Not indexed, so we need to do a full scan */ if (ldb_kv->warn_unindexed || ldb_kv->disable_full_db_scan) { @@ -863,4 +862,3 @@ int ldb_kv_search(struct ldb_kv_context *ctx) return ret; } - diff --git a/lib/ldb/ldb_mdb/ldb_mdb.c b/lib/ldb/ldb_mdb/ldb_mdb.c index 3e523ba2e85..646a67c554c 100644 --- a/lib/ldb/ldb_mdb/ldb_mdb.c +++ b/lib/ldb/ldb_mdb/ldb_mdb.c @@ -23,7 +23,7 @@ */ #include "ldb_mdb.h" -#include "../ldb_tdb/ldb_tdb.h" +#include "../ldb_key_value/ldb_kv.h" #include "include/dlinklist.h" #define MDB_URL_PREFIX "mdb://" diff --git a/lib/ldb/ldb_tdb/ldb_tdb.c b/lib/ldb/ldb_tdb/ldb_tdb.c index 95190904261..812ddd3e389 100644 --- a/lib/ldb/ldb_tdb/ldb_tdb.c +++ b/lib/ldb/ldb_tdb/ldb_tdb.c @@ -51,46 +51,9 @@ #include "ldb_tdb.h" #include "ldb_private.h" +#include "../ldb_key_value/ldb_kv.h" #include -/* - prevent memory errors on callbacks -*/ -struct ldb_kv_req_spy { - struct ldb_kv_context *ctx; -}; - -/* - map a tdb error code to a ldb error code -*/ -int ltdb_err_map(enum TDB_ERROR tdb_code) -{ - switch (tdb_code) { - case TDB_SUCCESS: - return LDB_SUCCESS; - case TDB_ERR_CORRUPT: - case TDB_ERR_OOM: - case TDB_ERR_EINVAL: - return LDB_ERR_OPERATIONS_ERROR; - case TDB_ERR_IO: - return LDB_ERR_PROTOCOL_ERROR; - case TDB_ERR_LOCK: - case TDB_ERR_NOLOCK: - return LDB_ERR_BUSY; - case TDB_ERR_LOCK_TIMEOUT: - return LDB_ERR_TIME_LIMIT_EXCEEDED; - case TDB_ERR_EXISTS: - return LDB_ERR_ENTRY_ALREADY_EXISTS; - case TDB_ERR_NOEXIST: - return LDB_ERR_NO_SUCH_OBJECT; - case TDB_ERR_RDONLY: - return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS; - default: - break; - } - return LDB_ERR_OTHER; -} - /* lock the database for read - use by ltdb_search and ltdb_sequence_number */ @@ -162,292 +125,6 @@ static int ltdb_unlock_read(struct ldb_module *module) return 0; } - -/* - * Determine if this key could hold a record. We allow the new GUID - * index, the old DN index and a possible future ID= - */ -bool ldb_kv_key_is_record(TDB_DATA key) -{ - if (key.dsize < 4) { - return false; - } - - if (memcmp(key.dptr, "DN=", 3) == 0) { - return true; - } - - if (memcmp(key.dptr, "ID=", 3) == 0) { - return true; - } - - if (key.dsize < sizeof(LTDB_GUID_KEY_PREFIX)) { - return false; - } - - if (memcmp(key.dptr, LTDB_GUID_KEY_PREFIX, - sizeof(LTDB_GUID_KEY_PREFIX) - 1) == 0) { - return true; - } - - return false; -} - -/* - form a TDB_DATA for a record key - caller frees - - note that the key for a record can depend on whether the - dn refers to a case sensitive index record or not -*/ -TDB_DATA ldb_kv_key_dn(struct ldb_module *module, - TALLOC_CTX *mem_ctx, - struct ldb_dn *dn) -{ - TDB_DATA key; - char *key_str = NULL; - const char *dn_folded = NULL; - - /* - most DNs are case insensitive. The exception is index DNs for - case sensitive attributes - - there are 3 cases dealt with in this code: - - 1) if the dn doesn't start with @ then uppercase the attribute - names and the attributes values of case insensitive attributes - 2) if the dn starts with @ then leave it alone - - the indexing code handles the rest - */ - - dn_folded = ldb_dn_get_casefold(dn); - if (!dn_folded) { - goto failed; - } - - key_str = talloc_strdup(mem_ctx, "DN="); - if (!key_str) { - goto failed; - } - - key_str = talloc_strdup_append_buffer(key_str, dn_folded); - if (!key_str) { - goto failed; - } - - key.dptr = (uint8_t *)key_str; - key.dsize = strlen(key_str) + 1; - - return key; - -failed: - errno = ENOMEM; - key.dptr = NULL; - key.dsize = 0; - return key; -} - -/* The caller is to provide a correctly sized key */ -int ldb_kv_guid_to_key(struct ldb_module *module, - struct ldb_kv_private *ldb_kv, - const struct ldb_val *GUID_val, - TDB_DATA *key) -{ - const char *GUID_prefix = LTDB_GUID_KEY_PREFIX; - const int GUID_prefix_len = sizeof(LTDB_GUID_KEY_PREFIX) - 1; - - if (key->dsize != (GUID_val->length+GUID_prefix_len)) { - return LDB_ERR_OPERATIONS_ERROR; - } - - memcpy(key->dptr, GUID_prefix, GUID_prefix_len); - memcpy(&key->dptr[GUID_prefix_len], - GUID_val->data, GUID_val->length); - return LDB_SUCCESS; -} - -/* - * The caller is to provide a correctly sized key, used only in - * the GUID index mode - */ -int ldb_kv_idx_to_key(struct ldb_module *module, - struct ldb_kv_private *ldb_kv, - TALLOC_CTX *mem_ctx, - const struct ldb_val *idx_val, - TDB_DATA *key) -{ - struct ldb_context *ldb = ldb_module_get_ctx(module); - struct ldb_dn *dn; - - if (ldb_kv->cache->GUID_index_attribute != NULL) { - return ldb_kv_guid_to_key(module, ldb_kv, idx_val, key); - } - - dn = ldb_dn_from_ldb_val(mem_ctx, ldb, idx_val); - if (dn == NULL) { - /* - * LDB_ERR_INVALID_DN_SYNTAX would just be confusing - * to the caller, as this in an invalid index value - */ - return LDB_ERR_OPERATIONS_ERROR; - } - /* form the key */ - *key = ldb_kv_key_dn(module, mem_ctx, dn); - TALLOC_FREE(dn); - if (!key->dptr) { - return ldb_module_oom(module); - } - return LDB_SUCCESS; -} - -/* - form a TDB_DATA for a record key - caller frees mem_ctx, which may or may not have the key - as a child. - - note that the key for a record can depend on whether a - GUID index is in use, or the DN is used as the key -*/ -TDB_DATA ldb_kv_key_msg(struct ldb_module *module, - TALLOC_CTX *mem_ctx, - const struct ldb_message *msg) -{ - void *data = ldb_module_get_private(module); - struct ldb_kv_private *ldb_kv = - talloc_get_type(data, struct ldb_kv_private); - TDB_DATA key; - const struct ldb_val *guid_val; - int ret; - - if (ldb_kv->cache->GUID_index_attribute == NULL) { - return ldb_kv_key_dn(module, mem_ctx, msg->dn); - } - - if (ldb_dn_is_special(msg->dn)) { - return ldb_kv_key_dn(module, mem_ctx, msg->dn); - } - - guid_val = - ldb_msg_find_ldb_val(msg, ldb_kv->cache->GUID_index_attribute); - if (guid_val == NULL) { - ldb_asprintf_errstring(ldb_module_get_ctx(module), - "Did not find GUID attribute %s " - "in %s, required for TDB record " - "key in " LTDB_IDXGUID " mode.", - ldb_kv->cache->GUID_index_attribute, - ldb_dn_get_linearized(msg->dn)); - errno = EINVAL; - key.dptr = NULL; - key.dsize = 0; - return key; - } - - /* In this case, allocate with talloc */ - key.dptr = talloc_size(mem_ctx, LTDB_GUID_KEY_SIZE); - if (key.dptr == NULL) { - errno = ENOMEM; - key.dptr = NULL; - key.dsize = 0; - return key; - } - key.dsize = talloc_get_size(key.dptr); - - ret = ldb_kv_guid_to_key(module, ldb_kv, guid_val, &key); - - if (ret != LDB_SUCCESS) { - errno = EINVAL; - key.dptr = NULL; - key.dsize = 0; - return key; - } - return key; -} - -/* - check special dn's have valid attributes - currently only @ATTRIBUTES is checked -*/ -static int ldb_kv_check_special_dn(struct ldb_module *module, - const struct ldb_message *msg) -{ - struct ldb_context *ldb = ldb_module_get_ctx(module); - unsigned int i, j; - - if (! ldb_dn_is_special(msg->dn) || - ! ldb_dn_check_special(msg->dn, LTDB_ATTRIBUTES)) { - return LDB_SUCCESS; - } - - /* we have @ATTRIBUTES, let's check attributes are fine */ - /* should we check that we deny multivalued attributes ? */ - for (i = 0; i < msg->num_elements; i++) { - if (ldb_attr_cmp(msg->elements[i].name, "distinguishedName") == 0) continue; - - for (j = 0; j < msg->elements[i].num_values; j++) { - if (ldb_kv_check_at_attributes_values( - &msg->elements[i].values[j]) != 0) { - ldb_set_errstring(ldb, "Invalid attribute value in an @ATTRIBUTES entry"); - return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX; - } - } - } - - return LDB_SUCCESS; -} - - -/* - we've made a modification to a dn - possibly reindex and - update sequence number -*/ -static int ldb_kv_modified(struct ldb_module *module, struct ldb_dn *dn) -{ - int ret = LDB_SUCCESS; - struct ldb_kv_private *ldb_kv = talloc_get_type( - ldb_module_get_private(module), struct ldb_kv_private); - - /* only allow modifies inside a transaction, otherwise the - * ldb is unsafe */ - if (ldb_kv->kv_ops->transaction_active(ldb_kv) == false) { - ldb_set_errstring(ldb_module_get_ctx(module), "ltdb modify without transaction"); - return LDB_ERR_OPERATIONS_ERROR; - } - - if (ldb_dn_is_special(dn) && - (ldb_dn_check_special(dn, LTDB_INDEXLIST) || - ldb_dn_check_special(dn, LTDB_ATTRIBUTES)) ) - { - if (ldb_kv->warn_reindex) { - ldb_debug(ldb_module_get_ctx(module), - LDB_DEBUG_ERROR, - "Reindexing %s due to modification on %s", - ldb_kv->kv_ops->name(ldb_kv), - ldb_dn_get_linearized(dn)); - } - ret = ldb_kv_reindex(module); - } - - /* If the modify was to a normal record, or any special except @BASEINFO, update the seq number */ - if (ret == LDB_SUCCESS && - !(ldb_dn_is_special(dn) && - ldb_dn_check_special(dn, LTDB_BASEINFO)) ) { - ret = ldb_kv_increase_sequence_number(module); - } - - /* If the modify was to @OPTIONS, reload the cache */ - if (ret == LDB_SUCCESS && - ldb_dn_is_special(dn) && - (ldb_dn_check_special(dn, LTDB_OPTIONS)) ) { - ret = ldb_kv_cache_reload(module); - } - - if (ret != LDB_SUCCESS) { - ldb_kv->reindex_failed = true; - } - - return ret; -} - static int ltdb_store(struct ldb_kv_private *ldb_kv, struct ldb_val ldb_key, struct ldb_val ldb_data, @@ -477,1050 +154,21 @@ static const char *ltdb_errorstr(struct ldb_kv_private *ldb_kv) { return tdb_errorstr(ldb_kv->tdb); } - -/* - store a record into the db -*/ -int ldb_kv_store(struct ldb_module *module, - const struct ldb_message *msg, - int flgs) -{ - void *data = ldb_module_get_private(module); - struct ldb_kv_private *ldb_kv = - talloc_get_type(data, struct ldb_kv_private); - TDB_DATA tdb_key; - struct ldb_val ldb_key; - struct ldb_val ldb_data; - int ret = LDB_SUCCESS; - TALLOC_CTX *key_ctx = talloc_new(module); - - if (key_ctx == NULL) { - return ldb_module_oom(module); - } - - if (ldb_kv->read_only) { - talloc_free(key_ctx); - return LDB_ERR_UNWILLING_TO_PERFORM; - } - - tdb_key = ldb_kv_key_msg(module, key_ctx, msg); - if (tdb_key.dptr == NULL) { - TALLOC_FREE(key_ctx); - return LDB_ERR_OTHER; - } - - ret = ldb_pack_data(ldb_module_get_ctx(module), - msg, &ldb_data); - if (ret == -1) { - TALLOC_FREE(key_ctx); - return LDB_ERR_OTHER; - } - - ldb_key.data = tdb_key.dptr; - ldb_key.length = tdb_key.dsize; - - ret = ldb_kv->kv_ops->store(ldb_kv, ldb_key, ldb_data, flgs); - if (ret != 0) { - bool is_special = ldb_dn_is_special(msg->dn); - ret = ldb_kv->kv_ops->error(ldb_kv); - - /* - * LDB_ERR_ENTRY_ALREADY_EXISTS means the DN, not - * the GUID, so re-map - */ - if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS && !is_special && - ldb_kv->cache->GUID_index_attribute != NULL) { - ret = LDB_ERR_CONSTRAINT_VIOLATION; - } - goto done; - } - -done: - TALLOC_FREE(key_ctx); - talloc_free(ldb_data.data); - - return ret; -} - - -/* - check if a attribute is a single valued, for a given element - */ -static bool ldb_kv_single_valued(const struct ldb_schema_attribute *a, - struct ldb_message_element *el) -{ - if (!a) return false; - if (el != NULL) { - if (el->flags & LDB_FLAG_INTERNAL_FORCE_SINGLE_VALUE_CHECK) { - /* override from a ldb module, for example - used for the description field, which is - marked multi-valued in the schema but which - should not actually accept multiple - values */ - return true; - } - if (el->flags & LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK) { - /* override from a ldb module, for example used for - deleted linked attribute entries */ - return false; - } - } - if (a->flags & LDB_ATTR_FLAG_SINGLE_VALUE) { - return true; - } - return false; -} - -static int ldb_kv_add_internal(struct ldb_module *module, - struct ldb_kv_private *ldb_kv, - const struct ldb_message *msg, - bool check_single_value) -{ - struct ldb_context *ldb = ldb_module_get_ctx(module); - int ret = LDB_SUCCESS; - unsigned int i; - - for (i=0;inum_elements;i++) { - struct ldb_message_element *el = &msg->elements[i]; - const struct ldb_schema_attribute *a = ldb_schema_attribute_by_name(ldb, el->name); - - if (el->num_values == 0) { - ldb_asprintf_errstring(ldb, "attribute '%s' on '%s' specified, but with 0 values (illegal)", - el->name, ldb_dn_get_linearized(msg->dn)); - return LDB_ERR_CONSTRAINT_VIOLATION; - } - if (check_single_value && el->num_values > 1 && - ldb_kv_single_valued(a, el)) { - ldb_asprintf_errstring(ldb, "SINGLE-VALUE attribute %s on %s specified more than once", - el->name, ldb_dn_get_linearized(msg->dn)); - return LDB_ERR_CONSTRAINT_VIOLATION; - } - - /* Do not check "@ATTRIBUTES" for duplicated values */ - if (ldb_dn_is_special(msg->dn) && - ldb_dn_check_special(msg->dn, LTDB_ATTRIBUTES)) { - continue; - } - - if (check_single_value && - !(el->flags & - LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK)) { - struct ldb_val *duplicate = NULL; - - ret = ldb_msg_find_duplicate_val(ldb, discard_const(msg), - el, &duplicate, 0); - if (ret != LDB_SUCCESS) { - return ret; - } - if (duplicate != NULL) { - ldb_asprintf_errstring( - ldb, - "attribute '%s': value '%.*s' on '%s' " - "provided more than once in ADD object", - el->name, - (int)duplicate->length, - duplicate->data, - ldb_dn_get_linearized(msg->dn)); - return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS; - } - } - } - - ret = ldb_kv_store(module, msg, TDB_INSERT); - if (ret != LDB_SUCCESS) { - /* - * Try really hard to get the right error code for - * a re-add situation, as this can matter! - */ - if (ret == LDB_ERR_CONSTRAINT_VIOLATION) { - int ret2; - struct ldb_dn *dn2 = NULL; - TALLOC_CTX *mem_ctx = talloc_new(module); - if (mem_ctx == NULL) { - return ldb_module_operr(module); - } - ret2 = - ldb_kv_search_base(module, mem_ctx, msg->dn, &dn2); - TALLOC_FREE(mem_ctx); - if (ret2 == LDB_SUCCESS) { - ret = LDB_ERR_ENTRY_ALREADY_EXISTS; - } - } - if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) { - ldb_asprintf_errstring(ldb, - "Entry %s already exists", - ldb_dn_get_linearized(msg->dn)); - } - return ret; - } - - ret = ldb_kv_index_add_new(module, ldb_kv, msg); - if (ret != LDB_SUCCESS) { - /* - * If we failed to index, delete the message again. - * - * This is particularly important for the GUID index - * case, which will only fail for a duplicate DN - * in the index add. - * - * Note that the caller may not cancel the transation - * and this means the above add might really show up! - */ - ldb_kv_delete_noindex(module, msg); - return ret; - } - - ret = ldb_kv_modified(module, msg->dn); - - return ret; -} - -/* - add a record to the database -*/ -static int ldb_kv_add(struct ldb_kv_context *ctx) -{ - struct ldb_module *module = ctx->module; - struct ldb_request *req = ctx->req; - void *data = ldb_module_get_private(module); - struct ldb_kv_private *ldb_kv = - talloc_get_type(data, struct ldb_kv_private); - int ret = LDB_SUCCESS; - - if (ldb_kv->max_key_length != 0 && - ldb_kv->cache->GUID_index_attribute == NULL && - !ldb_dn_is_special(req->op.add.message->dn)) { - ldb_set_errstring(ldb_module_get_ctx(module), - "Must operate ldb_mdb in GUID " - "index mode, but " LTDB_IDXGUID " not set."); - return LDB_ERR_UNWILLING_TO_PERFORM; - } - - ret = ldb_kv_check_special_dn(module, req->op.add.message); - if (ret != LDB_SUCCESS) { - return ret; - } - - ldb_request_set_state(req, LDB_ASYNC_PENDING); - - if (ldb_kv_cache_load(module) != 0) { - return LDB_ERR_OPERATIONS_ERROR; - } - - ret = ldb_kv_add_internal(module, ldb_kv, req->op.add.message, true); - - return ret; -} - -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); -} - -/* - delete a record from the database, not updating indexes (used for deleting - index records) -*/ -int ldb_kv_delete_noindex(struct ldb_module *module, - const struct ldb_message *msg) -{ - void *data = ldb_module_get_private(module); - struct ldb_kv_private *ldb_kv = - talloc_get_type(data, struct ldb_kv_private); - struct ldb_val ldb_key; - TDB_DATA tdb_key; - int ret; - TALLOC_CTX *tdb_key_ctx = talloc_new(module); - - if (tdb_key_ctx == NULL) { - return ldb_module_oom(module); - } - - if (ldb_kv->read_only) { - talloc_free(tdb_key_ctx); - return LDB_ERR_UNWILLING_TO_PERFORM; - } - - tdb_key = ldb_kv_key_msg(module, tdb_key_ctx, msg); - if (!tdb_key.dptr) { - TALLOC_FREE(tdb_key_ctx); - return LDB_ERR_OTHER; - } - - ldb_key.data = tdb_key.dptr; - ldb_key.length = tdb_key.dsize; - - ret = ldb_kv->kv_ops->delete (ldb_kv, ldb_key); - TALLOC_FREE(tdb_key_ctx); - - if (ret != 0) { - ret = ldb_kv->kv_ops->error(ldb_kv); - } - - return ret; -} - -static int ldb_kv_delete_internal(struct ldb_module *module, struct ldb_dn *dn) -{ - struct ldb_message *msg; - int ret = LDB_SUCCESS; - - msg = ldb_msg_new(module); - if (msg == NULL) { - return LDB_ERR_OPERATIONS_ERROR; - } - - /* in case any attribute of the message was indexed, we need - to fetch the old record */ - ret = ldb_kv_search_dn1( - module, dn, msg, LDB_UNPACK_DATA_FLAG_NO_DATA_ALLOC); - if (ret != LDB_SUCCESS) { - /* not finding the old record is an error */ - goto done; - } - - ret = ldb_kv_delete_noindex(module, msg); - if (ret != LDB_SUCCESS) { - goto done; - } - - /* remove any indexed attributes */ - ret = ldb_kv_index_delete(module, msg); - if (ret != LDB_SUCCESS) { - goto done; - } - - ret = ldb_kv_modified(module, dn); - if (ret != LDB_SUCCESS) { - goto done; - } - -done: - talloc_free(msg); - return ret; -} - -/* - delete a record from the database -*/ -static int ldb_kv_delete(struct ldb_kv_context *ctx) -{ - struct ldb_module *module = ctx->module; - struct ldb_request *req = ctx->req; - int ret = LDB_SUCCESS; - - ldb_request_set_state(req, LDB_ASYNC_PENDING); - - if (ldb_kv_cache_load(module) != 0) { - return LDB_ERR_OPERATIONS_ERROR; - } - - ret = ldb_kv_delete_internal(module, req->op.del.dn); - - return ret; -} - -/* - find an element by attribute name. At the moment this does a linear search, - it should be re-coded to use a binary search once all places that modify - records guarantee sorted order - - return the index of the first matching element if found, otherwise -1 -*/ -static int ldb_kv_find_element(const struct ldb_message *msg, const char *name) -{ - unsigned int i; - for (i=0;inum_elements;i++) { - if (ldb_attr_cmp(msg->elements[i].name, name) == 0) { - return i; - } - } - return -1; -} - - -/* - add an element to an existing record. Assumes a elements array that we - can call re-alloc on, and assumed that we can re-use the data pointers from - the passed in additional values. Use with care! - - returns 0 on success, -1 on failure (and sets errno) -*/ -static int ldb_kv_msg_add_element(struct ldb_message *msg, - struct ldb_message_element *el) -{ - struct ldb_message_element *e2; - unsigned int i; - - if (el->num_values == 0) { - /* nothing to do here - we don't add empty elements */ - return 0; - } - - e2 = talloc_realloc(msg, msg->elements, struct ldb_message_element, - msg->num_elements+1); - if (!e2) { - errno = ENOMEM; - return -1; - } - - msg->elements = e2; - - e2 = &msg->elements[msg->num_elements]; - - e2->name = el->name; - e2->flags = el->flags; - e2->values = talloc_array(msg->elements, - struct ldb_val, el->num_values); - if (!e2->values) { - errno = ENOMEM; - return -1; - } - for (i=0;inum_values;i++) { - e2->values[i] = el->values[i]; - } - e2->num_values = el->num_values; - - ++msg->num_elements; - - return 0; -} - -/* - delete all elements having a specified attribute name -*/ -static int ldb_kv_msg_delete_attribute(struct ldb_module *module, - struct ldb_kv_private *ldb_kv, - struct ldb_message *msg, - const char *name) -{ - unsigned int i; - int ret; - struct ldb_message_element *el; - bool is_special = ldb_dn_is_special(msg->dn); - - if (!is_special && ldb_kv->cache->GUID_index_attribute != NULL && - ldb_attr_cmp(name, ldb_kv->cache->GUID_index_attribute) == 0) { - struct ldb_context *ldb = ldb_module_get_ctx(module); - ldb_asprintf_errstring(ldb, - "Must not modify GUID " - "attribute %s (used as DB index)", - ldb_kv->cache->GUID_index_attribute); - return LDB_ERR_CONSTRAINT_VIOLATION; - } - - el = ldb_msg_find_element(msg, name); - if (el == NULL) { - return LDB_ERR_NO_SUCH_ATTRIBUTE; - } - i = el - msg->elements; - - ret = ldb_kv_index_del_element(module, ldb_kv, msg, el); - if (ret != LDB_SUCCESS) { - return ret; - } - - talloc_free(el->values); - if (msg->num_elements > (i+1)) { - memmove(el, el+1, sizeof(*el) * (msg->num_elements - (i+1))); - } - msg->num_elements--; - msg->elements = talloc_realloc(msg, msg->elements, - struct ldb_message_element, - msg->num_elements); - return LDB_SUCCESS; -} - -/* - delete all elements matching an attribute name/value - - return LDB Error on failure -*/ -static int ldb_kv_msg_delete_element(struct ldb_module *module, - struct ldb_kv_private *ldb_kv, - struct ldb_message *msg, - const char *name, - const struct ldb_val *val) -{ - struct ldb_context *ldb = ldb_module_get_ctx(module); - unsigned int i; - int found, ret; - struct ldb_message_element *el; - const struct ldb_schema_attribute *a; - - found = ldb_kv_find_element(msg, name); - if (found == -1) { - return LDB_ERR_NO_SUCH_ATTRIBUTE; - } - - i = (unsigned int) found; - el = &(msg->elements[i]); - - a = ldb_schema_attribute_by_name(ldb, el->name); - - for (i=0;inum_values;i++) { - bool matched; - if (a->syntax->operator_fn) { - ret = a->syntax->operator_fn(ldb, LDB_OP_EQUALITY, a, - &el->values[i], val, &matched); - if (ret != LDB_SUCCESS) return ret; - } else { - matched = (a->syntax->comparison_fn(ldb, ldb, - &el->values[i], val) == 0); - } - if (matched) { - if (el->num_values == 1) { - return ldb_kv_msg_delete_attribute( - module, ldb_kv, msg, name); - } - - ret = - ldb_kv_index_del_value(module, ldb_kv, msg, el, i); - if (ret != LDB_SUCCESS) { - return ret; - } - - if (inum_values-1) { - memmove(&el->values[i], &el->values[i+1], - sizeof(el->values[i])* - (el->num_values-(i+1))); - } - el->num_values--; - - /* per definition we find in a canonicalised message an - attribute value only once. So we are finished here */ - return LDB_SUCCESS; - } - } - - /* Not found */ - return LDB_ERR_NO_SUCH_ATTRIBUTE; -} - -/* - modify a record - internal interface - - yuck - this is O(n^2). Luckily n is usually small so we probably - get away with it, but if we ever have really large attribute lists - then we'll need to look at this again - - 'req' is optional, and is used to specify controls if supplied -*/ -int ldb_kv_modify_internal(struct ldb_module *module, - const struct ldb_message *msg, - struct ldb_request *req) -{ - struct ldb_context *ldb = ldb_module_get_ctx(module); - void *data = ldb_module_get_private(module); - struct ldb_kv_private *ldb_kv = - talloc_get_type(data, struct ldb_kv_private); - struct ldb_message *msg2; - unsigned int i, j; - int ret = LDB_SUCCESS, idx; - struct ldb_control *control_permissive = NULL; - TALLOC_CTX *mem_ctx = talloc_new(req); - - if (mem_ctx == NULL) { - return ldb_module_oom(module); - } - - if (req) { - control_permissive = ldb_request_get_control(req, - LDB_CONTROL_PERMISSIVE_MODIFY_OID); - } - - msg2 = ldb_msg_new(mem_ctx); - if (msg2 == NULL) { - ret = LDB_ERR_OTHER; - goto done; - } - - ret = ldb_kv_search_dn1( - module, msg->dn, msg2, LDB_UNPACK_DATA_FLAG_NO_DATA_ALLOC); - if (ret != LDB_SUCCESS) { - goto done; - } - - for (i=0; inum_elements; i++) { - struct ldb_message_element *el = &msg->elements[i], *el2; - struct ldb_val *vals; - const struct ldb_schema_attribute *a = ldb_schema_attribute_by_name(ldb, el->name); - const char *dn; - uint32_t options = 0; - if (control_permissive != NULL) { - options |= LDB_MSG_FIND_COMMON_REMOVE_DUPLICATES; - } - - switch (msg->elements[i].flags & LDB_FLAG_MOD_MASK) { - case LDB_FLAG_MOD_ADD: - - if (el->num_values == 0) { - ldb_asprintf_errstring(ldb, - "attribute '%s': attribute on '%s' specified, but with 0 values (illegal)", - el->name, ldb_dn_get_linearized(msg2->dn)); - ret = LDB_ERR_CONSTRAINT_VIOLATION; - goto done; - } - - /* make a copy of the array so that a permissive - * control can remove duplicates without changing the - * original values, but do not copy data as we do not - * need to keep it around once the operation is - * finished */ - if (control_permissive) { - el = talloc(msg2, struct ldb_message_element); - if (!el) { - ret = LDB_ERR_OTHER; - goto done; - } - *el = msg->elements[i]; - el->values = talloc_array(el, struct ldb_val, el->num_values); - if (el->values == NULL) { - ret = LDB_ERR_OTHER; - goto done; - } - for (j = 0; j < el->num_values; j++) { - el->values[j] = msg->elements[i].values[j]; - } - } - - if (el->num_values > 1 && ldb_kv_single_valued(a, el)) { - ldb_asprintf_errstring(ldb, "SINGLE-VALUE attribute %s on %s specified more than once", - el->name, ldb_dn_get_linearized(msg2->dn)); - ret = LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS; - goto done; - } - - /* Checks if element already exists */ - idx = ldb_kv_find_element(msg2, el->name); - if (idx == -1) { - if (ldb_kv_msg_add_element(msg2, el) != 0) { - ret = LDB_ERR_OTHER; - goto done; - } - ret = ldb_kv_index_add_element( - module, ldb_kv, msg2, el); - if (ret != LDB_SUCCESS) { - goto done; - } - } else { - j = (unsigned int) idx; - el2 = &(msg2->elements[j]); - - /* We cannot add another value on a existing one - if the attribute is single-valued */ - if (ldb_kv_single_valued(a, el)) { - ldb_asprintf_errstring(ldb, "SINGLE-VALUE attribute %s on %s specified more than once", - el->name, ldb_dn_get_linearized(msg2->dn)); - ret = LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS; - goto done; - } - - /* Check that values don't exist yet on multi- - valued attributes or aren't provided twice */ - if (!(el->flags & - LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK)) { - struct ldb_val *duplicate = NULL; - ret = ldb_msg_find_common_values(ldb, - msg2, - el, - el2, - options); - - if (ret == - LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS) { - ldb_asprintf_errstring(ldb, - "attribute '%s': value " - "#%u on '%s' already " - "exists", el->name, j, - ldb_dn_get_linearized(msg2->dn)); - goto done; - } else if (ret != LDB_SUCCESS) { - goto done; - } - - ret = ldb_msg_find_duplicate_val( - ldb, msg2, el, &duplicate, 0); - if (ret != LDB_SUCCESS) { - goto done; - } - if (duplicate != NULL) { - ldb_asprintf_errstring( - ldb, - "attribute '%s': value " - "'%.*s' on '%s' " - "provided more than " - "once in ADD", - el->name, - (int)duplicate->length, - duplicate->data, - ldb_dn_get_linearized(msg->dn)); - ret = LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS; - goto done; - } - } - - /* Now combine existing and new values to a new - attribute record */ - vals = talloc_realloc(msg2->elements, - el2->values, struct ldb_val, - el2->num_values + el->num_values); - if (vals == NULL) { - ldb_oom(ldb); - ret = LDB_ERR_OTHER; - goto done; - } - - for (j=0; jnum_values; j++) { - vals[el2->num_values + j] = - ldb_val_dup(vals, &el->values[j]); - } - - el2->values = vals; - el2->num_values += el->num_values; - - ret = ldb_kv_index_add_element( - module, ldb_kv, msg2, el); - if (ret != LDB_SUCCESS) { - goto done; - } - } - - break; - - case LDB_FLAG_MOD_REPLACE: - - if (el->num_values > 1 && ldb_kv_single_valued(a, el)) { - ldb_asprintf_errstring(ldb, "SINGLE-VALUE attribute %s on %s specified more than once", - el->name, ldb_dn_get_linearized(msg2->dn)); - ret = LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS; - goto done; - } - - /* - * We don't need to check this if we have been - * pre-screened by the repl_meta_data module - * in Samba, or someone else who can claim to - * know what they are doing. - */ - if (!(el->flags & LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK)) { - struct ldb_val *duplicate = NULL; - - ret = ldb_msg_find_duplicate_val(ldb, msg2, el, - &duplicate, 0); - if (ret != LDB_SUCCESS) { - goto done; - } - if (duplicate != NULL) { - ldb_asprintf_errstring( - ldb, - "attribute '%s': value '%.*s' " - "on '%s' provided more than " - "once in REPLACE", - el->name, - (int)duplicate->length, - duplicate->data, - ldb_dn_get_linearized(msg2->dn)); - ret = LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS; - goto done; - } - } - - /* Checks if element already exists */ - idx = ldb_kv_find_element(msg2, el->name); - if (idx != -1) { - j = (unsigned int) idx; - el2 = &(msg2->elements[j]); - - /* we consider two elements to be - * equal only if the order - * matches. This allows dbcheck to - * fix the ordering on attributes - * where order matters, such as - * objectClass - */ - if (ldb_msg_element_equal_ordered(el, el2)) { - continue; - } - - /* Delete the attribute if it exists in the DB */ - if (ldb_kv_msg_delete_attribute( - module, ldb_kv, msg2, el->name) != 0) { - ret = LDB_ERR_OTHER; - goto done; - } - } - - /* Recreate it with the new values */ - if (ldb_kv_msg_add_element(msg2, el) != 0) { - ret = LDB_ERR_OTHER; - goto done; - } - - ret = - ldb_kv_index_add_element(module, ldb_kv, msg2, el); - if (ret != LDB_SUCCESS) { - goto done; - } - - break; - - case LDB_FLAG_MOD_DELETE: - dn = ldb_dn_get_linearized(msg2->dn); - if (dn == NULL) { - ret = LDB_ERR_OTHER; - goto done; - } - - if (msg->elements[i].num_values == 0) { - /* Delete the whole attribute */ - ret = ldb_kv_msg_delete_attribute( - module, - ldb_kv, - msg2, - msg->elements[i].name); - if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE && - control_permissive) { - ret = LDB_SUCCESS; - } else { - ldb_asprintf_errstring(ldb, - "attribute '%s': no such attribute for delete on '%s'", - msg->elements[i].name, dn); - } - if (ret != LDB_SUCCESS) { - goto done; - } - } else { - /* Delete specified values from an attribute */ - for (j=0; j < msg->elements[i].num_values; j++) { - ret = ldb_kv_msg_delete_element( - module, - ldb_kv, - msg2, - msg->elements[i].name, - &msg->elements[i].values[j]); - if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE && - control_permissive) { - ret = LDB_SUCCESS; - } else if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE) { - ldb_asprintf_errstring(ldb, - "attribute '%s': no matching attribute value while deleting attribute on '%s'", - msg->elements[i].name, dn); - } - if (ret != LDB_SUCCESS) { - goto done; - } - } - } - break; - default: - ldb_asprintf_errstring(ldb, - "attribute '%s': invalid modify flags on '%s': 0x%x", - msg->elements[i].name, ldb_dn_get_linearized(msg->dn), - msg->elements[i].flags & LDB_FLAG_MOD_MASK); - ret = LDB_ERR_PROTOCOL_ERROR; - goto done; - } - } - - ret = ldb_kv_store(module, msg2, TDB_MODIFY); - if (ret != LDB_SUCCESS) { - goto done; - } - - ret = ldb_kv_modified(module, msg2->dn); - if (ret != LDB_SUCCESS) { - goto done; - } - -done: - TALLOC_FREE(mem_ctx); - return ret; -} - -/* - modify a record -*/ -static int ldb_kv_modify(struct ldb_kv_context *ctx) -{ - struct ldb_module *module = ctx->module; - struct ldb_request *req = ctx->req; - int ret = LDB_SUCCESS; - - ret = ldb_kv_check_special_dn(module, req->op.mod.message); - if (ret != LDB_SUCCESS) { - return ret; - } - - ldb_request_set_state(req, LDB_ASYNC_PENDING); - - if (ldb_kv_cache_load(module) != 0) { - return LDB_ERR_OPERATIONS_ERROR; - } - - ret = ldb_kv_modify_internal(module, req->op.mod.message, req); - - return ret; -} - -/* - rename a record -*/ -static int ldb_kv_rename(struct ldb_kv_context *ctx) -{ - struct ldb_module *module = ctx->module; - void *data = ldb_module_get_private(module); - struct ldb_kv_private *ldb_kv = - talloc_get_type(data, struct ldb_kv_private); - struct ldb_request *req = ctx->req; - struct ldb_message *msg; - int ret = LDB_SUCCESS; - TDB_DATA tdb_key, tdb_key_old; - struct ldb_dn *db_dn; - - ldb_request_set_state(req, LDB_ASYNC_PENDING); - - if (ldb_kv_cache_load(ctx->module) != 0) { - return LDB_ERR_OPERATIONS_ERROR; - } - - msg = ldb_msg_new(ctx); - if (msg == NULL) { - return LDB_ERR_OPERATIONS_ERROR; - } - - /* we need to fetch the old record to re-add under the new name */ - ret = ldb_kv_search_dn1(module, - req->op.rename.olddn, - msg, - LDB_UNPACK_DATA_FLAG_NO_DATA_ALLOC); - if (ret != LDB_SUCCESS) { - /* not finding the old record is an error */ - return ret; - } - - /* We need to, before changing the DB, check if the new DN - * exists, so we can return this error to the caller with an - * unmodified DB - * - * Even in GUID index mode we use ltdb_key_dn() as we are - * trying to figure out if this is just a case rename - */ - tdb_key = ldb_kv_key_dn(module, msg, req->op.rename.newdn); - if (!tdb_key.dptr) { - talloc_free(msg); - return LDB_ERR_OPERATIONS_ERROR; - } - - tdb_key_old = ldb_kv_key_dn(module, msg, req->op.rename.olddn); - if (!tdb_key_old.dptr) { - talloc_free(msg); - talloc_free(tdb_key.dptr); - return LDB_ERR_OPERATIONS_ERROR; - } - - /* - * Only declare a conflict if the new DN already exists, - * and it isn't a case change on the old DN - */ - if (tdb_key_old.dsize != tdb_key.dsize - || memcmp(tdb_key.dptr, tdb_key_old.dptr, tdb_key.dsize) != 0) { - ret = ldb_kv_search_base( - module, msg, req->op.rename.newdn, &db_dn); - if (ret == LDB_SUCCESS) { - ret = LDB_ERR_ENTRY_ALREADY_EXISTS; - } else if (ret == LDB_ERR_NO_SUCH_OBJECT) { - ret = LDB_SUCCESS; - } - } - - /* finding the new record already in the DB is an error */ - - if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) { - ldb_asprintf_errstring(ldb_module_get_ctx(module), - "Entry %s already exists", - ldb_dn_get_linearized(req->op.rename.newdn)); - } - if (ret != LDB_SUCCESS) { - talloc_free(tdb_key_old.dptr); - talloc_free(tdb_key.dptr); - talloc_free(msg); - return ret; - } - - talloc_free(tdb_key_old.dptr); - talloc_free(tdb_key.dptr); - - /* Always delete first then add, to avoid conflicts with - * unique indexes. We rely on the transaction to make this - * atomic - */ - ret = ldb_kv_delete_internal(module, msg->dn); - if (ret != LDB_SUCCESS) { - talloc_free(msg); - return ret; - } - - msg->dn = ldb_dn_copy(msg, req->op.rename.newdn); - if (msg->dn == NULL) { - talloc_free(msg); - return LDB_ERR_OPERATIONS_ERROR; - } - - /* We don't check single value as we can have more than 1 with - * deleted attributes. We could go through all elements but that's - * maybe not the most efficient way - */ - ret = ldb_kv_add_internal(module, ldb_kv, msg, false); - - talloc_free(msg); - - return ret; -} - -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); + +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_transaction_cancel(ldb_kv->tdb); + return tdb_delete(ldb_kv->tdb, tdb_key); } -static int ltdb_transaction_prepare_commit(struct ldb_kv_private *ldb_kv) +static int ltdb_transaction_start(struct ldb_kv_private *ldb_kv) { pid_t pid = getpid(); @@ -1534,10 +182,10 @@ static int ltdb_transaction_prepare_commit(struct ldb_kv_private *ldb_kv) return LDB_ERR_PROTOCOL_ERROR; } - return tdb_transaction_prepare_commit(ldb_kv->tdb); + return tdb_transaction_start(ldb_kv->tdb); } -static int ltdb_transaction_commit(struct ldb_kv_private *ldb_kv) +static int ltdb_transaction_cancel(struct ldb_kv_private *ldb_kv) { pid_t pid = getpid(); @@ -1551,15 +199,11 @@ static int ltdb_transaction_commit(struct ldb_kv_private *ldb_kv) return LDB_ERR_PROTOCOL_ERROR; } - return tdb_transaction_commit(ldb_kv->tdb); + return tdb_transaction_cancel(ldb_kv->tdb); } -static int ldb_kv_start_trans(struct ldb_module *module) +static int ltdb_transaction_prepare_commit(struct ldb_kv_private *ldb_kv) { - 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) { @@ -1572,38 +216,15 @@ static int ldb_kv_start_trans(struct ldb_module *module) return LDB_ERR_PROTOCOL_ERROR; } - /* Do not take out the transaction lock on a read-only DB */ - if (ldb_kv->read_only) { - return LDB_ERR_UNWILLING_TO_PERFORM; - } - - if (ldb_kv->kv_ops->begin_write(ldb_kv) != 0) { - return ldb_kv->kv_ops->error(ldb_kv); - } - - ldb_kv_index_transaction_start(module); - - ldb_kv->reindex_failed = false; - - return LDB_SUCCESS; + return tdb_transaction_prepare_commit(ldb_kv->tdb); } -/* - * Forward declaration to allow prepare_commit to in fact abort the - * transaction - */ -static int ldb_kv_del_trans(struct ldb_module *module); - -static int ldb_kv_prepare_commit(struct ldb_module *module) +static int ltdb_transaction_commit(struct ldb_kv_private *ldb_kv) { - int ret; - 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), + ldb_asprintf_errstring(ldb_module_get_ctx(ldb_kv->module), __location__ ": Reusing ldb opend by pid %d in " "process %d\n", @@ -1612,287 +233,8 @@ static int ldb_kv_prepare_commit(struct ldb_module *module) return LDB_ERR_PROTOCOL_ERROR; } - if (!ldb_kv->kv_ops->transaction_active(ldb_kv)) { - ldb_set_errstring(ldb_module_get_ctx(module), - "ltdb_prepare_commit() called " - "without transaction active"); - return LDB_ERR_OPERATIONS_ERROR; - } - - /* - * Check if the last re-index failed. - * - * This can happen if for example a duplicate value was marked - * unique. We must not write a partial re-index into the DB. - */ - if (ldb_kv->reindex_failed) { - /* - * We must instead abort the transaction so we get the - * old values and old index back - */ - ldb_kv_del_trans(module); - ldb_set_errstring(ldb_module_get_ctx(module), - "Failure during re-index, so " - "transaction must be aborted."); - return LDB_ERR_OPERATIONS_ERROR; - } - - ret = ldb_kv_index_transaction_commit(module); - if (ret != LDB_SUCCESS) { - ldb_kv->kv_ops->abort_write(ldb_kv); - return ret; - } - - if (ldb_kv->kv_ops->prepare_write(ldb_kv) != 0) { - ret = ldb_kv->kv_ops->error(ldb_kv); - ldb_debug_set(ldb_module_get_ctx(module), - LDB_DEBUG_FATAL, - "Failure during " - "prepare_write): %s -> %s", - ldb_kv->kv_ops->errorstr(ldb_kv), - ldb_strerror(ret)); - return ret; - } - - ldb_kv->prepared_commit = true; - - return LDB_SUCCESS; -} - -static int ldb_kv_end_trans(struct ldb_module *module) -{ - int ret; - void *data = ldb_module_get_private(module); - struct ldb_kv_private *ldb_kv = - talloc_get_type(data, struct ldb_kv_private); - - if (!ldb_kv->prepared_commit) { - ret = ldb_kv_prepare_commit(module); - if (ret != LDB_SUCCESS) { - return ret; - } - } - - ldb_kv->prepared_commit = false; - - if (ldb_kv->kv_ops->finish_write(ldb_kv) != 0) { - ret = ldb_kv->kv_ops->error(ldb_kv); - ldb_asprintf_errstring( - ldb_module_get_ctx(module), - "Failure during tdb_transaction_commit(): %s -> %s", - ldb_kv->kv_ops->errorstr(ldb_kv), - ldb_strerror(ret)); - return ret; - } - - return LDB_SUCCESS; -} - -static int ldb_kv_del_trans(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); - - if (ldb_kv_index_transaction_cancel(module) != 0) { - ldb_kv->kv_ops->abort_write(ldb_kv); - return ldb_kv->kv_ops->error(ldb_kv); - } - - ldb_kv->kv_ops->abort_write(ldb_kv); - return LDB_SUCCESS; -} - -/* - return sequenceNumber from @BASEINFO -*/ -static int ldb_kv_sequence_number(struct ldb_kv_context *ctx, - struct ldb_extended **ext) -{ - struct ldb_context *ldb; - struct ldb_module *module = ctx->module; - struct ldb_request *req = ctx->req; - void *data = ldb_module_get_private(module); - struct ldb_kv_private *ldb_kv = - talloc_get_type(data, struct ldb_kv_private); - TALLOC_CTX *tmp_ctx = NULL; - struct ldb_seqnum_request *seq; - struct ldb_seqnum_result *res; - struct ldb_message *msg = NULL; - struct ldb_dn *dn; - const char *date; - int ret = LDB_SUCCESS; - - ldb = ldb_module_get_ctx(module); - - seq = talloc_get_type(req->op.extended.data, - struct ldb_seqnum_request); - if (seq == NULL) { - return LDB_ERR_OPERATIONS_ERROR; - } - - ldb_request_set_state(req, LDB_ASYNC_PENDING); - - if (ldb_kv->kv_ops->lock_read(module) != 0) { - return LDB_ERR_OPERATIONS_ERROR; - } - - res = talloc_zero(req, struct ldb_seqnum_result); - if (res == NULL) { - ret = LDB_ERR_OPERATIONS_ERROR; - goto done; - } - - tmp_ctx = talloc_new(req); - if (tmp_ctx == NULL) { - ret = LDB_ERR_OPERATIONS_ERROR; - goto done; - } - - dn = ldb_dn_new(tmp_ctx, ldb, LTDB_BASEINFO); - if (dn == NULL) { - ret = LDB_ERR_OPERATIONS_ERROR; - goto done; - } - - msg = ldb_msg_new(tmp_ctx); - if (msg == NULL) { - ret = LDB_ERR_OPERATIONS_ERROR; - goto done; - } - - ret = ldb_kv_search_dn1(module, dn, msg, 0); - if (ret != LDB_SUCCESS) { - goto done; - } - - switch (seq->type) { - case LDB_SEQ_HIGHEST_SEQ: - res->seq_num = ldb_msg_find_attr_as_uint64(msg, LTDB_SEQUENCE_NUMBER, 0); - break; - case LDB_SEQ_NEXT: - res->seq_num = ldb_msg_find_attr_as_uint64(msg, LTDB_SEQUENCE_NUMBER, 0); - res->seq_num++; - break; - case LDB_SEQ_HIGHEST_TIMESTAMP: - date = ldb_msg_find_attr_as_string(msg, LTDB_MOD_TIMESTAMP, NULL); - if (date) { - res->seq_num = ldb_string_to_time(date); - } else { - res->seq_num = 0; - /* zero is as good as anything when we don't know */ - } - break; - } - - *ext = talloc_zero(req, struct ldb_extended); - if (*ext == NULL) { - ret = LDB_ERR_OPERATIONS_ERROR; - goto done; - } - (*ext)->oid = LDB_EXTENDED_SEQUENCE_NUMBER; - (*ext)->data = talloc_steal(*ext, res); - -done: - talloc_free(tmp_ctx); - - ldb_kv->kv_ops->unlock_read(module); - return ret; -} - -static void ldb_kv_request_done(struct ldb_kv_context *ctx, int error) -{ - struct ldb_context *ldb; - struct ldb_request *req; - struct ldb_reply *ares; - - ldb = ldb_module_get_ctx(ctx->module); - req = ctx->req; - - /* if we already returned an error just return */ - if (ldb_request_get_status(req) != LDB_SUCCESS) { - return; - } - - ares = talloc_zero(req, struct ldb_reply); - if (!ares) { - ldb_oom(ldb); - req->callback(req, NULL); - return; - } - ares->type = LDB_REPLY_DONE; - ares->error = error; - - req->callback(req, ares); -} - -static void ldb_kv_timeout(struct tevent_context *ev, - struct tevent_timer *te, - struct timeval t, - void *private_data) -{ - struct ldb_kv_context *ctx; - ctx = talloc_get_type(private_data, struct ldb_kv_context); - - if (!ctx->request_terminated) { - /* request is done now */ - ldb_kv_request_done(ctx, LDB_ERR_TIME_LIMIT_EXCEEDED); - } - - if (ctx->spy) { - /* neutralize the spy */ - ctx->spy->ctx = NULL; - ctx->spy = NULL; - } - talloc_free(ctx); -} - -static void ldb_kv_request_extended_done(struct ldb_kv_context *ctx, - struct ldb_extended *ext, - int error) -{ - struct ldb_context *ldb; - struct ldb_request *req; - struct ldb_reply *ares; - - ldb = ldb_module_get_ctx(ctx->module); - req = ctx->req; - - /* if we already returned an error just return */ - if (ldb_request_get_status(req) != LDB_SUCCESS) { - return; - } - - ares = talloc_zero(req, struct ldb_reply); - if (!ares) { - ldb_oom(ldb); - req->callback(req, NULL); - return; - } - ares->type = LDB_REPLY_DONE; - ares->response = ext; - ares->error = error; - - req->callback(req, ares); -} - -static void ldb_kv_handle_extended(struct ldb_kv_context *ctx) -{ - struct ldb_extended *ext = NULL; - int ret; - - if (strcmp(ctx->req->op.extended.oid, - LDB_EXTENDED_SEQUENCE_NUMBER) == 0) { - /* get sequence number */ - ret = ldb_kv_sequence_number(ctx, &ext); - } else { - /* not recognized */ - ret = LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION; - } - - ldb_kv_request_extended_done(ctx, ext, ret); + return tdb_transaction_commit(ldb_kv->tdb); } - struct kv_ctx { ldb_kv_traverse_fn kv_traverse_fn; void *ctx; @@ -2077,261 +419,6 @@ static const struct kv_db_ops key_value_ops = { .transaction_active = ltdb_transaction_active, }; -static void ldb_kv_callback(struct tevent_context *ev, - struct tevent_timer *te, - struct timeval t, - void *private_data) -{ - struct ldb_kv_context *ctx; - int ret; - - ctx = talloc_get_type(private_data, struct ldb_kv_context); - - if (ctx->request_terminated) { - goto done; - } - - switch (ctx->req->operation) { - case LDB_SEARCH: - ret = ldb_kv_search(ctx); - break; - case LDB_ADD: - ret = ldb_kv_add(ctx); - break; - case LDB_MODIFY: - ret = ldb_kv_modify(ctx); - break; - case LDB_DELETE: - ret = ldb_kv_delete(ctx); - break; - case LDB_RENAME: - ret = ldb_kv_rename(ctx); - break; - case LDB_EXTENDED: - ldb_kv_handle_extended(ctx); - goto done; - default: - /* no other op supported */ - ret = LDB_ERR_PROTOCOL_ERROR; - } - - if (!ctx->request_terminated) { - /* request is done now */ - ldb_kv_request_done(ctx, ret); - } - -done: - if (ctx->spy) { - /* neutralize the spy */ - ctx->spy->ctx = NULL; - ctx->spy = NULL; - } - talloc_free(ctx); -} - -static int ldb_kv_request_destructor(void *ptr) -{ - struct ldb_kv_req_spy *spy = - talloc_get_type(ptr, struct ldb_kv_req_spy); - - if (spy->ctx != NULL) { - spy->ctx->spy = NULL; - spy->ctx->request_terminated = true; - spy->ctx = NULL; - } - - return 0; -} - -static int ldb_kv_handle_request(struct ldb_module *module, - struct ldb_request *req) -{ - struct ldb_control *control_permissive; - struct ldb_context *ldb; - struct tevent_context *ev; - struct ldb_kv_context *ac; - struct tevent_timer *te; - struct timeval tv; - unsigned int i; - - ldb = ldb_module_get_ctx(module); - - control_permissive = ldb_request_get_control(req, - LDB_CONTROL_PERMISSIVE_MODIFY_OID); - - for (i = 0; req->controls && req->controls[i]; i++) { - if (req->controls[i]->critical && - req->controls[i] != control_permissive) { - ldb_asprintf_errstring(ldb, "Unsupported critical extension %s", - req->controls[i]->oid); - return LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION; - } - } - - if (req->starttime == 0 || req->timeout == 0) { - ldb_set_errstring(ldb, "Invalid timeout settings"); - return LDB_ERR_TIME_LIMIT_EXCEEDED; - } - - ev = ldb_handle_get_event_context(req->handle); - - ac = talloc_zero(ldb, struct ldb_kv_context); - if (ac == NULL) { - ldb_oom(ldb); - return LDB_ERR_OPERATIONS_ERROR; - } - - ac->module = module; - ac->req = req; - - tv.tv_sec = 0; - tv.tv_usec = 0; - te = tevent_add_timer(ev, ac, tv, ldb_kv_callback, ac); - if (NULL == te) { - talloc_free(ac); - return LDB_ERR_OPERATIONS_ERROR; - } - - if (req->timeout > 0) { - tv.tv_sec = req->starttime + req->timeout; - tv.tv_usec = 0; - ac->timeout_event = - tevent_add_timer(ev, ac, tv, ldb_kv_timeout, ac); - if (NULL == ac->timeout_event) { - talloc_free(ac); - return LDB_ERR_OPERATIONS_ERROR; - } - } - - /* set a spy so that we do not try to use the request context - * if it is freed before ltdb_callback fires */ - ac->spy = talloc(req, struct ldb_kv_req_spy); - if (NULL == ac->spy) { - talloc_free(ac); - return LDB_ERR_OPERATIONS_ERROR; - } - ac->spy->ctx = ac; - - talloc_set_destructor((TALLOC_CTX *)ac->spy, ldb_kv_request_destructor); - - return LDB_SUCCESS; -} - -static int ldb_kv_init_rootdse(struct ldb_module *module) -{ - /* ignore errors on this - we expect it for non-sam databases */ - ldb_mod_register_control(module, LDB_CONTROL_PERMISSIVE_MODIFY_OID); - - /* there can be no module beyond the backend, just return */ - return LDB_SUCCESS; -} - -static int ldb_kv_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); - return ldb_kv->kv_ops->lock_read(module); -} - -static int ldb_kv_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); - return ldb_kv->kv_ops->unlock_read(module); -} - -static const struct ldb_module_ops ldb_kv_ops = { - .name = "tdb", - .init_context = ldb_kv_init_rootdse, - .search = ldb_kv_handle_request, - .add = ldb_kv_handle_request, - .modify = ldb_kv_handle_request, - .del = ldb_kv_handle_request, - .rename = ldb_kv_handle_request, - .extended = ldb_kv_handle_request, - .start_transaction = ldb_kv_start_trans, - .end_transaction = ldb_kv_end_trans, - .prepare_commit = ldb_kv_prepare_commit, - .del_transaction = ldb_kv_del_trans, - .read_lock = ldb_kv_lock_read, - .read_unlock = ldb_kv_unlock_read, -}; - -int ldb_kv_init_store(struct ldb_kv_private *ldb_kv, - const char *name, - struct ldb_context *ldb, - const char *options[], - struct ldb_module **_module) -{ - if (getenv("LDB_WARN_UNINDEXED")) { - ldb_kv->warn_unindexed = true; - } - - if (getenv("LDB_WARN_REINDEX")) { - ldb_kv->warn_reindex = true; - } - - ldb_kv->sequence_number = 0; - - ldb_kv->pid = getpid(); - - ldb_kv->module = ldb_module_new(ldb, ldb, name, &ldb_kv_ops); - if (!ldb_kv->module) { - ldb_oom(ldb); - talloc_free(ldb_kv); - return LDB_ERR_OPERATIONS_ERROR; - } - ldb_module_set_private(ldb_kv->module, ldb_kv); - talloc_steal(ldb_kv->module, ldb_kv); - - if (ldb_kv_cache_load(ldb_kv->module) != 0) { - ldb_asprintf_errstring(ldb, "Unable to load ltdb cache " - "records for backend '%s'", name); - talloc_free(ldb_kv->module); - return LDB_ERR_OPERATIONS_ERROR; - } - - *_module = ldb_kv->module; - /* - * Set or override the maximum key length - * - * The ldb_mdb code will have set this to 511, but our tests - * set this even smaller (to make the tests more practical). - * - * This must only be used for the selftest as the length - * becomes encoded in the index keys. - */ - { - const char *len_str = - ldb_options_find(ldb, options, - "max_key_len_for_self_test"); - if (len_str != NULL) { - unsigned len = strtoul(len_str, NULL, 0); - ldb_kv->max_key_length = len; - } - } - - /* - * Override full DB scans - * - * A full DB scan is expensive on a large database. This - * option is for testing to show that the full DB scan is not - * triggered. - */ - { - const char *len_str = - ldb_options_find(ldb, options, - "disable_full_db_scan_for_self_test"); - if (len_str != NULL) { - ldb_kv->disable_full_db_scan = true; - } - } - - return LDB_SUCCESS; -} - /* connect to the database */ diff --git a/lib/ldb/ldb_tdb/ldb_tdb.h b/lib/ldb/ldb_tdb/ldb_tdb.h index 4cbc7c23046..5395d425341 100644 --- a/lib/ldb/ldb_tdb/ldb_tdb.h +++ b/lib/ldb/ldb_tdb/ldb_tdb.h @@ -4,266 +4,13 @@ #include "tdb.h" #include "ldb_module.h" -struct ldb_kv_private; -typedef int (*ldb_kv_traverse_fn)(struct ldb_kv_private *ldb_kv, - struct ldb_val key, - struct ldb_val data, - void *ctx); - -struct kv_db_ops { - int (*store)(struct ldb_kv_private *ldb_kv, - struct ldb_val key, - struct ldb_val data, - int flags); - int (*delete)(struct ldb_kv_private *ldb_kv, struct ldb_val key); - int (*iterate)(struct ldb_kv_private *ldb_kv, - ldb_kv_traverse_fn fn, - void *ctx); - int (*update_in_iterate)(struct ldb_kv_private *ldb_kv, - struct ldb_val key, - struct ldb_val key2, - struct ldb_val data, - void *ctx); - int (*fetch_and_parse)(struct ldb_kv_private *ldb_kv, - struct ldb_val key, - int (*parser)(struct ldb_val key, - struct ldb_val data, - void *private_data), - void *ctx); - int (*lock_read)(struct ldb_module *); - int (*unlock_read)(struct ldb_module *); - int (*begin_write)(struct ldb_kv_private *); - int (*prepare_write)(struct ldb_kv_private *); - int (*abort_write)(struct ldb_kv_private *); - int (*finish_write)(struct ldb_kv_private *); - int (*error)(struct ldb_kv_private *ldb_kv); - const char *(*errorstr)(struct ldb_kv_private *ldb_kv); - const char *(*name)(struct ldb_kv_private *ldb_kv); - bool (*has_changed)(struct ldb_kv_private *ldb_kv); - bool (*transaction_active)(struct ldb_kv_private *ldb_kv); -}; - -/* this private structure is used by the key value backends in the - ldb_context */ -struct ldb_kv_private { - const struct kv_db_ops *kv_ops; - struct ldb_module *module; - TDB_CONTEXT *tdb; - struct lmdb_private *lmdb_private; - unsigned int connect_flags; - - unsigned long long sequence_number; - - /* the low level tdb seqnum - used to avoid loading BASEINFO when - possible */ - int tdb_seqnum; - - struct ldb_kv_cache { - struct ldb_message *indexlist; - bool one_level_indexes; - bool attribute_indexes; - const char *GUID_index_attribute; - const char *GUID_index_dn_component; - } *cache; - - - bool check_base; - bool disallow_dn_filter; - struct ldb_kv_idxptr *idxptr; - bool prepared_commit; - int read_lock_count; - - bool warn_unindexed; - bool warn_reindex; - - bool read_only; - - bool reindex_failed; - - const struct ldb_schema_syntax *GUID_index_syntax; - - /* - * Maximum index key length. If non zero keys longer than this length - * will be truncated for non unique indexes. Keys for unique indexes - * greater than this length will be rejected. - */ - unsigned max_key_length; - - /* - * To allow testing that ensures the DB does not fall back - * to a full scan - */ - bool disable_full_db_scan; - - /* - * The PID that opened this database so we don't work in a - * fork()ed child. - */ - pid_t pid; -}; - -struct ldb_kv_context { - struct ldb_module *module; - struct ldb_request *req; - - bool request_terminated; - struct ldb_kv_req_spy *spy; - - /* search stuff */ - const struct ldb_parse_tree *tree; - struct ldb_dn *base; - enum ldb_scope scope; - const char * const *attrs; - struct tevent_timer *timeout_event; - - /* error handling */ - int error; -}; - -struct ldb_kv_reindex_context { - struct ldb_module *module; - int error; - uint32_t count; -}; - - -/* special record types */ -#define LTDB_INDEX "@INDEX" -#define LTDB_INDEXLIST "@INDEXLIST" -#define LTDB_IDX "@IDX" -#define LTDB_IDXVERSION "@IDXVERSION" -#define LTDB_IDXATTR "@IDXATTR" -#define LTDB_IDXONE "@IDXONE" -#define LTDB_IDXDN "@IDXDN" -#define LTDB_IDXGUID "@IDXGUID" -#define LTDB_IDX_DN_GUID "@IDX_DN_GUID" - -/* - * This will be used to indicate when a new, yet to be developed - * sub-database version of the indicies are in use, to ensure we do - * not load future databases unintentionally. - */ - -#define LTDB_IDX_LMDB_SUBDB "@IDX_LMDB_SUBDB" - -#define LTDB_BASEINFO "@BASEINFO" -#define LTDB_OPTIONS "@OPTIONS" -#define LTDB_ATTRIBUTES "@ATTRIBUTES" - -/* special attribute types */ -#define LTDB_SEQUENCE_NUMBER "sequenceNumber" -#define LTDB_CHECK_BASE "checkBaseOnSearch" -#define LTDB_DISALLOW_DN_FILTER "disallowDNFilter" -#define LTDB_MOD_TIMESTAMP "whenChanged" -#define LTDB_OBJECTCLASS "objectClass" - -/* DB keys */ -#define LTDB_GUID_KEY_PREFIX "GUID=" -#define LTDB_GUID_SIZE 16 -#define LTDB_GUID_KEY_SIZE (LTDB_GUID_SIZE + sizeof(LTDB_GUID_KEY_PREFIX) - 1) - -/* The following definitions come from lib/ldb/ldb_tdb/ldb_cache.c */ - -int ldb_kv_cache_reload(struct ldb_module *module); -int ldb_kv_cache_load(struct ldb_module *module); -int ldb_kv_increase_sequence_number(struct ldb_module *module); -int ldb_kv_check_at_attributes_values(const struct ldb_val *value); - -/* The following definitions come from lib/ldb/ldb_tdb/ldb_index.c */ - -struct ldb_parse_tree; - -int ldb_kv_search_indexed(struct ldb_kv_context *ctx, uint32_t *); -int ldb_kv_index_add_new(struct ldb_module *module, - struct ldb_kv_private *ldb_kv, - const struct ldb_message *msg); -int ldb_kv_index_delete(struct ldb_module *module, - const struct ldb_message *msg); -int ldb_kv_index_del_element(struct ldb_module *module, - struct ldb_kv_private *ldb_kv, - const struct ldb_message *msg, - struct ldb_message_element *el); -int ldb_kv_index_add_element(struct ldb_module *module, - struct ldb_kv_private *ldb_kv, - const struct ldb_message *msg, - struct ldb_message_element *el); -int ldb_kv_index_del_value(struct ldb_module *module, - struct ldb_kv_private *ldb_kv, - const struct ldb_message *msg, - struct ldb_message_element *el, - unsigned int v_idx); -int ldb_kv_reindex(struct ldb_module *module); -int ldb_kv_index_transaction_start(struct ldb_module *module); -int ldb_kv_index_transaction_commit(struct ldb_module *module); -int ldb_kv_index_transaction_cancel(struct ldb_module *module); -int ldb_kv_key_dn_from_idx(struct ldb_module *module, - struct ldb_kv_private *ldb_kv, - TALLOC_CTX *mem_ctx, - struct ldb_dn *dn, - TDB_DATA *tdb_key); - -/* The following definitions come from lib/ldb/ldb_tdb/ldb_search.c */ -int ldb_kv_search_dn1(struct ldb_module *module, - struct ldb_dn *dn, - struct ldb_message *msg, - unsigned int unpack_flags); -int ldb_kv_search_base(struct ldb_module *module, - TALLOC_CTX *mem_ctx, - struct ldb_dn *dn, - struct ldb_dn **ret_dn); -int ldb_kv_search_key(struct ldb_module *module, - struct ldb_kv_private *ldb_kv, - struct TDB_DATA tdb_key, - struct ldb_message *msg, - unsigned int unpack_flags); -int ldb_kv_filter_attrs(TALLOC_CTX *mem_ctx, - const struct ldb_message *msg, - const char *const *attrs, - struct ldb_message **filtered_msg); -int ldb_kv_search(struct ldb_kv_context *ctx); - -/* The following definitions come from lib/ldb/ldb_tdb/ldb_tdb.c */ -/* - * Determine if this key could hold a record. We allow the new GUID - * index, the old DN index and a possible future ID= - */ -bool ldb_kv_key_is_record(TDB_DATA key); -TDB_DATA ldb_kv_key_dn(struct ldb_module *module, - TALLOC_CTX *mem_ctx, - struct ldb_dn *dn); -TDB_DATA ldb_kv_key_msg(struct ldb_module *module, - TALLOC_CTX *mem_ctx, - const struct ldb_message *msg); -int ldb_kv_guid_to_key(struct ldb_module *module, - struct ldb_kv_private *ldb_kv, - const struct ldb_val *GUID_val, - TDB_DATA *key); -int ldb_kv_idx_to_key(struct ldb_module *module, - struct ldb_kv_private *ldb_kv, - TALLOC_CTX *mem_ctx, - const struct ldb_val *idx_val, - TDB_DATA *key); TDB_DATA ltdb_key(struct ldb_module *module, struct ldb_dn *dn); -int ldb_kv_store(struct ldb_module *module, - const struct ldb_message *msg, - int flgs); -int ldb_kv_modify_internal(struct ldb_module *module, - const struct ldb_message *msg, - struct ldb_request *req); -int ldb_kv_delete_noindex(struct ldb_module *module, - const struct ldb_message *msg); int ltdb_err_map(enum TDB_ERROR tdb_code); struct tdb_context *ltdb_wrap_open(TALLOC_CTX *mem_ctx, const char *path, int hash_size, int tdb_flags, int open_flags, mode_t mode, struct ldb_context *ldb); -int ldb_kv_init_store(struct ldb_kv_private *ldb_kv, - const char *name, - struct ldb_context *ldb, - const char *options[], - struct ldb_module **_module); - int ltdb_connect(struct ldb_context *ldb, const char *url, unsigned int flags, const char *options[], struct ldb_module **_module); diff --git a/lib/ldb/ldb_tdb/ldb_tdb_err_map.c b/lib/ldb/ldb_tdb/ldb_tdb_err_map.c new file mode 100644 index 00000000000..41e5318272c --- /dev/null +++ b/lib/ldb/ldb_tdb/ldb_tdb_err_map.c @@ -0,0 +1,84 @@ +/* + 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 . +*/ + +/* + * 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 + +/* + map a tdb error code to a ldb error code +*/ +int ltdb_err_map(enum TDB_ERROR tdb_code) +{ + switch (tdb_code) { + case TDB_SUCCESS: + return LDB_SUCCESS; + case TDB_ERR_CORRUPT: + case TDB_ERR_OOM: + case TDB_ERR_EINVAL: + return LDB_ERR_OPERATIONS_ERROR; + case TDB_ERR_IO: + return LDB_ERR_PROTOCOL_ERROR; + case TDB_ERR_LOCK: + case TDB_ERR_NOLOCK: + return LDB_ERR_BUSY; + case TDB_ERR_LOCK_TIMEOUT: + return LDB_ERR_TIME_LIMIT_EXCEEDED; + case TDB_ERR_EXISTS: + return LDB_ERR_ENTRY_ALREADY_EXISTS; + case TDB_ERR_NOEXIST: + return LDB_ERR_NO_SUCH_OBJECT; + case TDB_ERR_RDONLY: + return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS; + default: + break; + } + return LDB_ERR_OTHER; +} diff --git a/lib/ldb/tests/ldb_kv_ops_test.c b/lib/ldb/tests/ldb_kv_ops_test.c index 36ea3bf45d5..68c04863db6 100644 --- a/lib/ldb/tests/ldb_kv_ops_test.c +++ b/lib/ldb/tests/ldb_kv_ops_test.c @@ -62,6 +62,7 @@ #include #include "ldb_tdb/ldb_tdb.h" +#include "ldb_key_value/ldb_kv.h" #define DEFAULT_BE "tdb" diff --git a/lib/ldb/tests/ldb_lmdb_test.c b/lib/ldb/tests/ldb_lmdb_test.c index 364a4f6cff1..78758bbbe16 100644 --- a/lib/ldb/tests/ldb_lmdb_test.c +++ b/lib/ldb/tests/ldb_lmdb_test.c @@ -57,6 +57,7 @@ #include "../ldb_tdb/ldb_tdb.h" #include "../ldb_mdb/ldb_mdb.h" +#include "../ldb_key_value/ldb_kv.h" #define TEST_BE "mdb" diff --git a/lib/ldb/tests/ldb_tdb_test.c b/lib/ldb/tests/ldb_tdb_test.c index 226fcb1d454..8418dbfd671 100644 --- a/lib/ldb/tests/ldb_tdb_test.c +++ b/lib/ldb/tests/ldb_tdb_test.c @@ -56,6 +56,7 @@ #include #include "../ldb_tdb/ldb_tdb.h" +#include "../ldb_key_value/ldb_kv.h" #define TEST_BE "tdb" diff --git a/lib/ldb/wscript b/lib/ldb/wscript index 33e787c5e5f..5d144130520 100644 --- a/lib/ldb/wscript +++ b/lib/ldb/wscript @@ -396,15 +396,27 @@ def build(bld): init_function='ldb_tdb_init', module_init_name='ldb_init_module', internal_module=False, - deps='tdb ldb ldb_key_value', + deps='ldb ldb_tdb_int ldb_key_value', subsystem='ldb') - bld.SAMBA_LIBRARY('ldb_key_value', + bld.SAMBA_LIBRARY('ldb_tdb_int', + bld.SUBDIR('ldb_tdb', + '''ldb_tdb_wrap.c ldb_tdb.c'''), + private_library=True, + deps='ldb tdb ldb_key_value ldb_tdb_err_map') + + bld.SAMBA_LIBRARY('ldb_tdb_err_map', bld.SUBDIR('ldb_tdb', - '''ldb_tdb.c ldb_search.c ldb_index.c - ldb_cache.c ldb_tdb_wrap.c'''), + '''ldb_tdb_err_map.c '''), + private_library=True, + deps='ldb tdb') + + bld.SAMBA_LIBRARY('ldb_key_value', + bld.SUBDIR('ldb_key_value', + '''ldb_kv.c ldb_kv_search.c ldb_kv_index.c + ldb_kv_cache.c'''), private_library=True, - deps='tdb ldb') + deps='tdb ldb ldb_tdb_err_map') if bld.CONFIG_SET('HAVE_LMDB'): bld.SAMBA_MODULE('ldb_mdb', @@ -432,7 +444,7 @@ def build(bld): init_function='ldb_ldb_init', module_init_name='ldb_init_module', internal_module=False, - deps='ldb ldb_key_value' + lmdb_deps, + deps='ldb ldb_tdb ldb_key_value' + lmdb_deps, subsystem='ldb') # have a separate subsystem for common/ldb.c, so it can rebuild