*/
#include "ldb_tdb.h"
-#include <lib/tdb_compat/tdb_compat.h>
+#include "ldb_private.h"
+#include <tdb.h>
/*
prevent memory errors on callbacks
case TDB_ERR_IO:
return LDB_ERR_PROTOCOL_ERROR;
case TDB_ERR_LOCK:
-#ifndef BUILD_TDB2
case TDB_ERR_NOLOCK:
-#endif
return LDB_ERR_BUSY;
-#ifndef BUILD_TDB2
case TDB_ERR_LOCK_TIMEOUT:
-#endif
return LDB_ERR_TIME_LIMIT_EXCEEDED;
case TDB_ERR_EXISTS:
return LDB_ERR_ENTRY_ALREADY_EXISTS;
if (ldb_dn_is_special(dn) &&
(ldb_dn_check_special(dn, LTDB_INDEXLIST) ||
- ldb_dn_check_special(dn, LTDB_ATTRIBUTES)) ) {
+ ldb_dn_check_special(dn, LTDB_ATTRIBUTES)) )
+ {
+ if (ltdb->warn_reindex) {
+ ldb_debug(ldb_module_get_ctx(module),
+ LDB_DEBUG_ERROR, "Reindexing %s due to modification on %s",
+ tdb_name(ltdb->tdb), ldb_dn_get_linearized(dn));
+ }
ret = ltdb_reindex(module);
}
return LDB_ERR_OTHER;
}
- ret = ltdb_pack_data(module, msg, &tdb_data);
+ ret = ldb_pack_data(ldb_module_get_ctx(module),
+ msg, (struct ldb_val *)&tdb_data);
if (ret == -1) {
talloc_free(tdb_key.dptr);
return LDB_ERR_OTHER;
{
struct ldb_context *ldb = ldb_module_get_ctx(module);
int ret = LDB_SUCCESS;
- unsigned int i;
+ unsigned int i, j;
for (i=0;i<msg->num_elements;i++) {
struct ldb_message_element *el = &msg->elements[i];
el->name, ldb_dn_get_linearized(msg->dn));
return LDB_ERR_CONSTRAINT_VIOLATION;
}
+
+ /* TODO: This is O(n^2) - replace with more efficient check */
+ for (j=0; j<el->num_values; j++) {
+ struct ldb_val *found_val;
+ ret = ldb_msg_find_val_schema(ldb, a, el,
+ &el->values[j], &found_val);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ if (found_val != &el->values[j]) {
+ ldb_asprintf_errstring(ldb,
+ "attribute '%s': value #%u on '%s' provided more than once",
+ el->name, j, ldb_dn_get_linearized(msg->dn));
+ return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
+ }
+ }
}
ret = ltdb_store(module, msg, TDB_INSERT);
for (i=0;i<el->num_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);
+ ret = ldb_val_equal_schema(ldb, a, &el->values[i], val, &matched);
+ if (ret != LDB_SUCCESS) {
+ return ret;
}
if (matched) {
if (el->num_values == 1) {
return LDB_ERR_OTHER;
}
- tdb_data = tdb_fetch_compat(ltdb->tdb, tdb_key);
+ tdb_data = tdb_fetch(ltdb->tdb, tdb_key);
if (!tdb_data.dptr) {
talloc_free(tdb_key.dptr);
return ltdb_err_map(tdb_error(ltdb->tdb));
goto done;
}
- ret = ltdb_unpack_data(module, &tdb_data, msg2);
+ ret = ldb_unpack_data(ldb_module_get_ctx(module), (struct ldb_val *)&tdb_data, msg2);
free(tdb_data.dptr);
if (ret == -1) {
ret = LDB_ERR_OTHER;
/* Check that values don't exist yet on multi-
valued attributes or aren't provided twice */
+ /* TODO: This is O(n^2) - replace with more efficient check */
for (j = 0; j < el->num_values; j++) {
- if (ldb_msg_find_val(el2, &el->values[j]) != NULL) {
+ struct ldb_val *matched_val;
+ ret = ldb_msg_find_val_schema(ldb, a, el2,
+ &el->values[j], &matched_val);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ if (matched_val != NULL) {
if (control_permissive) {
/* remove this one as if it was never added */
el->num_values--;
ret = LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
goto done;
}
- if (ldb_msg_find_val(el, &el->values[j]) != &el->values[j]) {
+ ret = ldb_msg_find_val_schema(ldb, a, el,
+ &el->values[j], &matched_val);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ if (matched_val != &el->values[j]) {
ldb_asprintf_errstring(ldb,
"attribute '%s': value #%u on '%s' provided more than once",
el->name, j, ldb_dn_get_linearized(msg2->dn));
/* TODO: This is O(n^2) - replace with more efficient check */
for (j=0; j<el->num_values; j++) {
- if (ldb_msg_find_val(el, &el->values[j]) != &el->values[j]) {
+ struct ldb_val *matched_val;
+ ret = ldb_msg_find_val_schema(ldb, a, el,
+ &el->values[j], &matched_val);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ if (matched_val != &el->values[j]) {
ldb_asprintf_errstring(ldb,
"attribute '%s': value #%u on '%s' provided more than once",
el->name, j, ldb_dn_get_linearized(msg2->dn));
if (idx != -1) {
j = (unsigned int) idx;
el2 = &(msg2->elements[j]);
- if (ldb_msg_element_compare(el, el2) == 0) {
- /* we are replacing with the same values */
+
+ /* 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 (msg_delete_attribute(module, ldb, msg2,
el->name) != 0) {
static int ltdb_rename(struct ltdb_context *ctx)
{
struct ldb_module *module = ctx->module;
+ void *data = ldb_module_get_private(module);
+ struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private);
struct ldb_request *req = ctx->req;
struct ldb_message *msg;
int ret = LDB_SUCCESS;
+ TDB_DATA tdb_key, tdb_key_old;
ldb_request_set_state(req, LDB_ASYNC_PENDING);
return LDB_ERR_OPERATIONS_ERROR;
}
- /* in case any attribute of the message was indexed, we need
- to fetch the old record */
+ /* we need to fetch the old record to re-add under the new name */
ret = ltdb_search_dn1(module, req->op.rename.olddn, msg);
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 */
+ tdb_key = ltdb_key(module, req->op.rename.newdn);
+ if (!tdb_key.dptr) {
+ talloc_free(msg);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ tdb_key_old = ltdb_key(module, 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) {
+ if (tdb_exists(ltdb->tdb, tdb_key)) {
+ talloc_free(tdb_key_old.dptr);
+ talloc_free(tdb_key.dptr);
+ ldb_asprintf_errstring(ldb_module_get_ctx(module),
+ "Entry %s already exists",
+ ldb_dn_get_linearized(msg->dn));
+ /* finding the new record already in the DB is an error */
+ talloc_free(msg);
+ return LDB_ERR_ENTRY_ALREADY_EXISTS;
+ }
+ }
+ 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 = ltdb_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;
}
*/
ret = ltdb_add_internal(module, msg, false);
+ talloc_free(msg);
+
return ret;
}
static int ltdb_init_rootdse(struct ldb_module *module)
{
- struct ldb_context *ldb;
- int ret;
-
- ldb = ldb_module_get_ctx(module);
-
- ret = ldb_mod_register_control(module,
- LDB_CONTROL_PERMISSIVE_MODIFY_OID);
/* 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;
tdb_flags, open_flags,
ldb_get_create_perms(ldb), ldb);
if (!ltdb->tdb) {
+ ldb_asprintf_errstring(ldb,
+ "Unable to open tdb '%s'", path);
ldb_debug(ldb, LDB_DEBUG_ERROR,
"Unable to open tdb '%s'", path);
talloc_free(ltdb);
ltdb->warn_unindexed = true;
}
+ if (getenv("LDB_WARN_REINDEX")) {
+ ltdb->warn_reindex = true;
+ }
+
ltdb->sequence_number = 0;
module = ldb_module_new(ldb, ldb, "ldb_tdb backend", <db_ops);
if (!module) {
+ ldb_oom(ldb);
talloc_free(ltdb);
return LDB_ERR_OPERATIONS_ERROR;
}
talloc_steal(module, ltdb);
if (ltdb_cache_load(module) != 0) {
+ ldb_asprintf_errstring(ldb,
+ "Unable to load ltdb cache records of tdb '%s'", path);
talloc_free(module);
return LDB_ERR_OPERATIONS_ERROR;
}