X-Git-Url: http://git.samba.org/samba.git/?a=blobdiff_plain;f=source4%2Flib%2Fldb%2Fldb_tdb%2Fldb_tdb.c;h=7427b9816323e9a9981e538dfa3e9626bf2adcec;hb=2f211daa47d16eddb807f8cde00509f62be7fd1f;hp=3461f98d5f3f46d44dd5eda73b47a3baa20d37d9;hpb=fa7608481a33c9e3907306a5d44bcea94d255f3b;p=samba.git diff --git a/source4/lib/ldb/ldb_tdb/ldb_tdb.c b/source4/lib/ldb/ldb_tdb/ldb_tdb.c index 3461f98d5f3..7427b981632 100644 --- a/source4/lib/ldb/ldb_tdb/ldb_tdb.c +++ b/source4/lib/ldb/ldb_tdb/ldb_tdb.c @@ -1,15 +1,15 @@ -/* +/* ldb database library Copyright (C) Andrew Tridgell 2004 Copyright (C) Stefan Metzmacher 2004 - Copyright (C) Simo Sorce 2006 - + Copyright (C) Simo Sorce 2006-2008 + ** 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 @@ -39,10 +39,12 @@ * - description: make the module use asyncronous calls * date: Feb 2006 * Author: Simo Sorce + * + * - description: make it possible to use event contexts + * date: Jan 2008 + * Author: Simo Sorce */ -#include "ldb_includes.h" - #include "ldb_tdb.h" @@ -75,50 +77,43 @@ static int ltdb_err_map(enum TDB_ERROR tdb_code) return LDB_ERR_OTHER; } - -struct ldb_handle *init_ltdb_handle(struct ltdb_private *ltdb, struct ldb_module *module, - struct ldb_request *req) +/* + lock the database for read - use by ltdb_search and ltdb_sequence_number +*/ +int ltdb_lock_read(struct ldb_module *module) { - struct ltdb_context *ac; - struct ldb_handle *h; - - h = talloc_zero(req, struct ldb_handle); - if (h == NULL) { - ldb_set_errstring(module->ldb, "Out of Memory"); - return NULL; + void *data = ldb_module_get_private(module); + struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private); + if (ltdb->in_transaction == 0) { + return tdb_lockall_read(ltdb->tdb); } + return 0; +} - h->module = module; - - ac = talloc_zero(h, struct ltdb_context); - if (ac == NULL) { - ldb_set_errstring(module->ldb, "Out of Memory"); - talloc_free(h); - return NULL; +/* + unlock the database after a ltdb_lock_read() +*/ +int ltdb_unlock_read(struct ldb_module *module) +{ + void *data = ldb_module_get_private(module); + struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private); + if (ltdb->in_transaction == 0) { + return tdb_unlockall_read(ltdb->tdb); } - - h->private_data = (void *)ac; - - h->state = LDB_ASYNC_INIT; - h->status = LDB_SUCCESS; - - ac->module = module; - ac->context = req->context; - ac->callback = req->callback; - - return h; + return 0; } + /* form a TDB_DATA for a record key caller frees - note that the key for a record can depend on whether the + note that the key for a record can depend on whether the dn refers to a case sensitive index record or not */ struct TDB_DATA ltdb_key(struct ldb_module *module, struct ldb_dn *dn) { - struct ldb_context *ldb = module->ldb; + struct ldb_context *ldb = ldb_module_get_ctx(module); TDB_DATA key; char *key_str = NULL; const char *dn_folded = NULL; @@ -131,8 +126,8 @@ struct TDB_DATA ltdb_key(struct ldb_module *module, struct ldb_dn *dn) 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 + 2) if the dn starts with @ then leave it alone - + the indexing code handles the rest */ dn_folded = ldb_dn_get_casefold(dn); @@ -166,10 +161,12 @@ failed: check special dn's have valid attributes currently only @ATTRIBUTES is checked */ -int ltdb_check_special_dn(struct ldb_module *module, const struct ldb_message *msg) +static int ltdb_check_special_dn(struct ldb_module *module, + const struct ldb_message *msg) { + struct ldb_context *ldb = ldb_module_get_ctx(module); int i, j; - + if (! ldb_dn_is_special(msg->dn) || ! ldb_dn_check_special(msg->dn, LTDB_ATTRIBUTES)) { return 0; @@ -180,7 +177,7 @@ int ltdb_check_special_dn(struct ldb_module *module, const struct ldb_message *m for (i = 0; i < msg->num_elements; i++) { for (j = 0; j < msg->elements[i].num_values; j++) { if (ltdb_check_at_attributes_values(&msg->elements[i].values[j]) != 0) { - ldb_set_errstring(module->ldb, "Invalid attribute value in an @ATTRIBUTES entry"); + ldb_set_errstring(ldb, "Invalid attribute value in an @ATTRIBUTES entry"); return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX; } } @@ -191,7 +188,7 @@ int ltdb_check_special_dn(struct ldb_module *module, const struct ldb_message *m /* - we've made a modification to a dn - possibly reindex and + we've made a modification to a dn - possibly reindex and update sequence number */ static int ltdb_modified(struct ldb_module *module, struct ldb_dn *dn) @@ -218,8 +215,8 @@ static int ltdb_modified(struct ldb_module *module, struct ldb_dn *dn) */ int ltdb_store(struct ldb_module *module, const struct ldb_message *msg, int flgs) { - struct ltdb_private *ltdb = - talloc_get_type(module->private_data, struct ltdb_private); + void *data = ldb_module_get_private(module); + struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private); TDB_DATA tdb_key, tdb_data; int ret; @@ -239,7 +236,7 @@ int ltdb_store(struct ldb_module *module, const struct ldb_message *msg, int flg ret = ltdb_err_map(tdb_error(ltdb->tdb)); goto done; } - + ret = ltdb_index_add(module, msg); if (ret != LDB_SUCCESS) { tdb_delete(ltdb->tdb, tdb_key); @@ -253,26 +250,48 @@ done: } -static int ltdb_add_internal(struct ldb_module *module, const struct ldb_message *msg) +static int ltdb_add_internal(struct ldb_module *module, + const struct ldb_message *msg) { - int ret; - + struct ldb_context *ldb = ldb_module_get_ctx(module); + int ret, i; + ret = ltdb_check_special_dn(module, msg); if (ret != LDB_SUCCESS) { return ret; } - + if (ltdb_cache_load(module) != 0) { return LDB_ERR_OPERATIONS_ERROR; } + 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 (a && a->flags & LDB_ATTR_FLAG_SINGLE_VALUE) { + if (el->num_values > 1) { + ldb_asprintf_errstring(ldb, "SINGLE-VALUE attribute %s on %s speicified more than once", + el->name, ldb_dn_get_linearized(msg->dn)); + return LDB_ERR_CONSTRAINT_VIOLATION; + } + } + } + ret = ltdb_store(module, msg, TDB_INSERT); if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) { - ldb_asprintf_errstring(module->ldb, "Entry %s already exists", ldb_dn_get_linearized(msg->dn)); + ldb_asprintf_errstring(ldb, + "Entry %s already exists", + ldb_dn_get_linearized(msg->dn)); return ret; } - + if (ret == LDB_SUCCESS) { ret = ltdb_index_one(module, msg, 1); if (ret != LDB_SUCCESS) { @@ -291,34 +310,20 @@ static int ltdb_add_internal(struct ldb_module *module, const struct ldb_message /* add a record to the database */ -static int ltdb_add(struct ldb_module *module, struct ldb_request *req) +static int ltdb_add(struct ltdb_context *ctx) { - struct ltdb_private *ltdb = talloc_get_type(module->private_data, struct ltdb_private); - struct ltdb_context *ltdb_ac; - int tret, ret = LDB_SUCCESS; + struct ldb_module *module = ctx->module; + struct ldb_request *req = ctx->req; + int tret; - if (check_critical_controls(req->controls)) { - return LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION; - } - - req->handle = init_ltdb_handle(ltdb, module, req); - if (req->handle == NULL) { - return LDB_ERR_OPERATIONS_ERROR; - } - ltdb_ac = talloc_get_type(req->handle->private_data, struct ltdb_context); + ldb_request_set_state(req, LDB_ASYNC_PENDING); tret = ltdb_add_internal(module, req->op.add.message); if (tret != LDB_SUCCESS) { - req->handle->status = tret; - goto done; - } - - if (ltdb_ac->callback) { - ret = ltdb_ac->callback(module->ldb, ltdb_ac->context, NULL); + return tret; } -done: - req->handle->state = LDB_ASYNC_DONE; - return ret; + + return LDB_SUCCESS; } /* @@ -327,8 +332,8 @@ done: */ int ltdb_delete_noindex(struct ldb_module *module, struct ldb_dn *dn) { - struct ltdb_private *ltdb = - talloc_get_type(module->private_data, struct ltdb_private); + void *data = ldb_module_get_private(module); + struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private); TDB_DATA tdb_key; int ret; @@ -395,46 +400,30 @@ done: /* delete a record from the database */ -static int ltdb_delete(struct ldb_module *module, struct ldb_request *req) +static int ltdb_delete(struct ltdb_context *ctx) { - struct ltdb_private *ltdb = talloc_get_type(module->private_data, struct ltdb_private); - struct ltdb_context *ltdb_ac; - int tret, ret = LDB_SUCCESS; + struct ldb_module *module = ctx->module; + struct ldb_request *req = ctx->req; + int tret; - if (check_critical_controls(req->controls)) { - return LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION; - } - - req->handle = NULL; + ldb_request_set_state(req, LDB_ASYNC_PENDING); if (ltdb_cache_load(module) != 0) { return LDB_ERR_OPERATIONS_ERROR; } - req->handle = init_ltdb_handle(ltdb, module, req); - if (req->handle == NULL) { - return LDB_ERR_OPERATIONS_ERROR; - } - ltdb_ac = talloc_get_type(req->handle->private_data, struct ltdb_context); - tret = ltdb_delete_internal(module, req->op.del.dn); if (tret != LDB_SUCCESS) { - req->handle->status = tret; - goto done; + return tret; } - if (ltdb_ac->callback) { - ret = ltdb_ac->callback(module->ldb, ltdb_ac->context, NULL); - } -done: - req->handle->state = LDB_ASYNC_DONE; - return ret; + return LDB_SUCCESS; } /* - 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 + 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 */ @@ -452,18 +441,19 @@ static int find_element(const struct ldb_message *msg, const char *name) /* 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! + 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 msg_add_element(struct ldb_context *ldb, - struct ldb_message *msg, struct ldb_message_element *el) + struct ldb_message *msg, + struct ldb_message_element *el) { struct ldb_message_element *e2; unsigned int i; - e2 = talloc_realloc(msg, msg->elements, struct ldb_message_element, + e2 = talloc_realloc(msg, msg->elements, struct ldb_message_element, msg->num_elements+1); if (!e2) { errno = ENOMEM; @@ -478,7 +468,8 @@ static int msg_add_element(struct ldb_context *ldb, e2->flags = el->flags; e2->values = NULL; if (el->num_values != 0) { - e2->values = talloc_array(msg->elements, struct ldb_val, el->num_values); + e2->values = talloc_array(msg->elements, + struct ldb_val, el->num_values); if (!e2->values) { errno = ENOMEM; return -1; @@ -512,20 +503,21 @@ static int msg_delete_attribute(struct ldb_module *module, for (i=0;inum_elements;i++) { if (ldb_attr_cmp(msg->elements[i].name, name) == 0) { for (j=0;jelements[i].num_values;j++) { - ltdb_index_del_value(module, dn, &msg->elements[i], j); + ltdb_index_del_value(module, dn, + &msg->elements[i], j); } talloc_free(msg->elements[i].values); if (msg->num_elements > (i+1)) { - memmove(&msg->elements[i], - &msg->elements[i+1], + memmove(&msg->elements[i], + &msg->elements[i+1], sizeof(struct ldb_message_element)* (msg->num_elements - (i+1))); } msg->num_elements--; i--; - msg->elements = talloc_realloc(msg, msg->elements, - struct ldb_message_element, - msg->num_elements); + msg->elements = talloc_realloc(msg, msg->elements, + struct ldb_message_element, + msg->num_elements); } } @@ -533,16 +525,16 @@ static int msg_delete_attribute(struct ldb_module *module, } /* - delete all elements matching an attribute name/value + delete all elements matching an attribute name/value return 0 on success, -1 on failure */ static int msg_delete_element(struct ldb_module *module, - struct ldb_message *msg, + struct ldb_message *msg, const char *name, const struct ldb_val *val) { - struct ldb_context *ldb = module->ldb; + struct ldb_context *ldb = ldb_module_get_ctx(module); unsigned int i; int found; struct ldb_message_element *el; @@ -558,14 +550,17 @@ static int msg_delete_element(struct ldb_module *module, a = ldb_schema_attribute_by_name(ldb, el->name); for (i=0;inum_values;i++) { - if (a->syntax->comparison_fn(ldb, ldb, &el->values[i], val) == 0) { + if (a->syntax->comparison_fn(ldb, ldb, + &el->values[i], val) == 0) { if (inum_values-1) { memmove(&el->values[i], &el->values[i+1], - sizeof(el->values[i])*(el->num_values-(i+1))); + sizeof(el->values[i])* + (el->num_values-(i+1))); } el->num_values--; if (el->num_values == 0) { - return msg_delete_attribute(module, ldb, msg, name); + return msg_delete_attribute(module, ldb, + msg, name); } return 0; } @@ -579,14 +574,15 @@ static int msg_delete_element(struct ldb_module *module, 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 + get away with it, but if we ever have really large attribute lists then we'll need to look at this again */ -int ltdb_modify_internal(struct ldb_module *module, const struct ldb_message *msg) +int ltdb_modify_internal(struct ldb_module *module, + const struct ldb_message *msg) { - struct ldb_context *ldb = module->ldb; - struct ltdb_private *ltdb = - talloc_get_type(module->private_data, struct ltdb_private); + struct ldb_context *ldb = ldb_module_get_ctx(module); + void *data = ldb_module_get_private(module); + struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private); TDB_DATA tdb_key, tdb_data; struct ldb_message *msg2; unsigned i, j; @@ -624,15 +620,34 @@ int ltdb_modify_internal(struct ldb_module *module, const struct ldb_message *ms struct ldb_message_element *el2; struct ldb_val *vals; const char *dn; + const struct ldb_schema_attribute *a = ldb_schema_attribute_by_name(ldb, el->name); - switch (msg->elements[i].flags & LDB_FLAG_MOD_MASK) { + if (ldb_attr_cmp(el->name, "distinguishedName") == 0) { + ldb_asprintf_errstring(ldb, "it is not permitted to perform a modify on distinguishedName (use rename instead): %s", + ldb_dn_get_linearized(msg->dn)); + return LDB_ERR_UNWILLING_TO_PERFORM; + } + switch (msg->elements[i].flags & LDB_FLAG_MOD_MASK) { case LDB_FLAG_MOD_ADD: + /* add this element to the message. fail if it already exists */ idx = find_element(msg2, el->name); + if (el->num_values == 0) { + ldb_asprintf_errstring(ldb, "attribute %s on %s speicified, but with 0 values (illigal)", + el->name, ldb_dn_get_linearized(msg->dn)); + return LDB_ERR_CONSTRAINT_VIOLATION; + } if (idx == -1) { + if (a && a->flags & LDB_ATTR_FLAG_SINGLE_VALUE) { + if (el->num_values > 1) { + ldb_asprintf_errstring(ldb, "SINGLE-VALUE attribute %s on %s speicified more than once", + el->name, ldb_dn_get_linearized(msg->dn)); + return LDB_ERR_CONSTRAINT_VIOLATION; + } + } if (msg_add_element(ldb, msg2, el) != 0) { ret = LDB_ERR_OTHER; goto failed; @@ -640,6 +655,13 @@ int ltdb_modify_internal(struct ldb_module *module, const struct ldb_message *ms continue; } + /* If this is an add, then if it already + * exists in the object, then we violoate the + * single-value rule */ + if (a && a->flags & LDB_ATTR_FLAG_SINGLE_VALUE) { + return LDB_ERR_CONSTRAINT_VIOLATION; + } + el2 = &msg2->elements[idx]; /* An attribute with this name already exists, @@ -649,12 +671,12 @@ int ltdb_modify_internal(struct ldb_module *module, const struct ldb_message *ms for (j=0;jnum_values;j++) { if (ldb_msg_find_val(el2, &el->values[j])) { - ldb_asprintf_errstring(module->ldb, "%s: value #%d already exists", el->name, j); + ldb_asprintf_errstring(ldb, "%s: value #%d already exists", el->name, j); ret = LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS; goto failed; } if (ldb_msg_find_val(el, &el->values[j]) != &el->values[j]) { - ldb_asprintf_errstring(module->ldb, "%s: value #%d provided more than once", el->name, j); + ldb_asprintf_errstring(ldb, "%s: value #%d provided more than once", el->name, j); ret = LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS; goto failed; } @@ -679,13 +701,20 @@ int ltdb_modify_internal(struct ldb_module *module, const struct ldb_message *ms break; case LDB_FLAG_MOD_REPLACE: + if (a && a->flags & LDB_ATTR_FLAG_SINGLE_VALUE) { + if (el->num_values > 1) { + ldb_asprintf_errstring(ldb, "SINGLE-VALUE attribute %s on %s speicified more than once", + el->name, ldb_dn_get_linearized(msg->dn)); + return LDB_ERR_CONSTRAINT_VIOLATION; + } + } /* replace all elements of this attribute name with the elements listed. The attribute not existing is not an error */ msg_delete_attribute(module, ldb, msg2, el->name); for (j=0;jnum_values;j++) { if (ldb_msg_find_val(el, &el->values[j]) != &el->values[j]) { - ldb_asprintf_errstring(module->ldb, "%s: value #%d provided more than once", el->name, j); + ldb_asprintf_errstring(ldb, "%s: value #%d provided more than once", el->name, j); ret = LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS; goto failed; } @@ -712,7 +741,7 @@ int ltdb_modify_internal(struct ldb_module *module, const struct ldb_message *ms if (msg->elements[i].num_values == 0) { if (msg_delete_attribute(module, ldb, msg2, msg->elements[i].name) != 0) { - ldb_asprintf_errstring(module->ldb, "No such attribute: %s for delete on %s", msg->elements[i].name, dn); + ldb_asprintf_errstring(ldb, "No such attribute: %s for delete on %s", msg->elements[i].name, dn); ret = LDB_ERR_NO_SUCH_ATTRIBUTE; goto failed; } @@ -723,7 +752,7 @@ int ltdb_modify_internal(struct ldb_module *module, const struct ldb_message *ms msg2, msg->elements[i].name, &msg->elements[i].values[j]) != 0) { - ldb_asprintf_errstring(module->ldb, "No matching attribute value when deleting attribute: %s on %s", msg->elements[i].name, dn); + ldb_asprintf_errstring(ldb, "No matching attribute value when deleting attribute: %s on %s", msg->elements[i].name, dn); ret = LDB_ERR_NO_SUCH_ATTRIBUTE; goto failed; } @@ -734,15 +763,17 @@ int ltdb_modify_internal(struct ldb_module *module, const struct ldb_message *ms } break; default: - ldb_asprintf_errstring(module->ldb, "Invalid ldb_modify flags on %s: 0x%x", - msg->elements[i].name, - msg->elements[i].flags & LDB_FLAG_MOD_MASK); + ldb_asprintf_errstring(ldb, + "Invalid ldb_modify flags on %s: 0x%x", + msg->elements[i].name, + msg->elements[i].flags & LDB_FLAG_MOD_MASK); ret = LDB_ERR_PROTOCOL_ERROR; goto failed; } } - /* we've made all the mods - save the modified record back into the database */ + /* we've made all the mods + * save the modified record back into the database */ ret = ltdb_store(module, msg2, TDB_MODIFY); if (ret != LDB_SUCCESS) { goto failed; @@ -766,79 +797,50 @@ failed: /* modify a record */ -static int ltdb_modify(struct ldb_module *module, struct ldb_request *req) +static int ltdb_modify(struct ltdb_context *ctx) { - struct ltdb_private *ltdb = talloc_get_type(module->private_data, struct ltdb_private); - struct ltdb_context *ltdb_ac; - int tret, ret = LDB_SUCCESS; - - if (check_critical_controls(req->controls)) { - return LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION; - } - - req->handle = NULL; + struct ldb_module *module = ctx->module; + struct ldb_request *req = ctx->req; + int tret; - req->handle = init_ltdb_handle(ltdb, module, req); - if (req->handle == NULL) { - return LDB_ERR_OPERATIONS_ERROR; - } - ltdb_ac = talloc_get_type(req->handle->private_data, struct ltdb_context); + ldb_request_set_state(req, LDB_ASYNC_PENDING); tret = ltdb_check_special_dn(module, req->op.mod.message); if (tret != LDB_SUCCESS) { - req->handle->status = tret; - goto done; + return tret; } - + if (ltdb_cache_load(module) != 0) { - ret = LDB_ERR_OPERATIONS_ERROR; - goto done; + return LDB_ERR_OPERATIONS_ERROR; } tret = ltdb_modify_internal(module, req->op.mod.message); if (tret != LDB_SUCCESS) { - req->handle->status = tret; - goto done; + return tret; } - if (ltdb_ac->callback) { - ret = ltdb_ac->callback(module->ldb, ltdb_ac->context, NULL); - } -done: - req->handle->state = LDB_ASYNC_DONE; - return ret; + return LDB_SUCCESS; } /* rename a record */ -static int ltdb_rename(struct ldb_module *module, struct ldb_request *req) +static int ltdb_rename(struct ltdb_context *ctx) { - struct ltdb_private *ltdb = talloc_get_type(module->private_data, struct ltdb_private); - struct ltdb_context *ltdb_ac; + struct ldb_module *module = ctx->module; + struct ldb_request *req = ctx->req; struct ldb_message *msg; - int tret, ret = LDB_SUCCESS; + int tret; - if (check_critical_controls(req->controls)) { - return LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION; - } - - req->handle = NULL; + ldb_request_set_state(req, LDB_ASYNC_PENDING); - if (ltdb_cache_load(module) != 0) { + if (ltdb_cache_load(ctx->module) != 0) { return LDB_ERR_OPERATIONS_ERROR; } - req->handle = init_ltdb_handle(ltdb, module, req); - if (req->handle == NULL) { - return LDB_ERR_OPERATIONS_ERROR; - } - ltdb_ac = talloc_get_type(req->handle->private_data, struct ltdb_context); - - msg = talloc(ltdb_ac, struct ldb_message); + msg = talloc(ctx, struct ldb_message); if (msg == NULL) { - ret = LDB_ERR_OPERATIONS_ERROR; - goto done; + return LDB_ERR_OPERATIONS_ERROR; } /* in case any attribute of the message was indexed, we need @@ -846,187 +848,439 @@ static int ltdb_rename(struct ldb_module *module, struct ldb_request *req) tret = ltdb_search_dn1(module, req->op.rename.olddn, msg); if (tret != LDB_SUCCESS) { /* not finding the old record is an error */ - req->handle->status = tret; - goto done; + return tret; } msg->dn = ldb_dn_copy(msg, req->op.rename.newdn); if (!msg->dn) { - ret = LDB_ERR_OPERATIONS_ERROR; - goto done; + return LDB_ERR_OPERATIONS_ERROR; } - if (ldb_dn_compare(req->op.rename.olddn, req->op.rename.newdn) == 0) { - /* The rename operation is apparently only changing case - - the DNs are the same. Delete the old DN before adding - the new one to avoid a TDB_ERR_EXISTS error. - - The only drawback to this is that if the delete - succeeds but the add fails, we rely on the - transaction to roll this all back. */ - ret = ltdb_delete_internal(module, req->op.rename.olddn); - if (ret != LDB_SUCCESS) { - goto done; - } - - ret = ltdb_add_internal(module, msg); - if (ret != LDB_SUCCESS) { - goto done; - } - } else { - /* The rename operation is changing DNs. Try to add the new - DN first to avoid clobbering another DN not related to - this rename operation. */ - ret = ltdb_add_internal(module, msg); - if (ret != LDB_SUCCESS) { - goto done; - } - - tret = ltdb_delete_internal(module, req->op.rename.olddn); - if (tret != LDB_SUCCESS) { - ltdb_delete_internal(module, req->op.rename.newdn); - ret = LDB_ERR_OPERATIONS_ERROR; - goto done; - } + /* Always delete first then add, to avoid conflicts with + * unique indexes. We rely on the transaction to make this + * atomic + */ + tret = ltdb_delete_internal(module, req->op.rename.olddn); + if (tret != LDB_SUCCESS) { + return tret; } - if (ltdb_ac->callback) { - ret = ltdb_ac->callback(module->ldb, ltdb_ac->context, NULL); + tret = ltdb_add_internal(module, msg); + if (tret != LDB_SUCCESS) { + return tret; } -done: - req->handle->state = LDB_ASYNC_DONE; - return ret; + + return LDB_SUCCESS; } static int ltdb_start_trans(struct ldb_module *module) { - struct ltdb_private *ltdb = - talloc_get_type(module->private_data, struct ltdb_private); + void *data = ldb_module_get_private(module); + struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private); if (tdb_transaction_start(ltdb->tdb) != 0) { return ltdb_err_map(tdb_error(ltdb->tdb)); } + ltdb->in_transaction++; + + ltdb_index_transaction_start(module); + return LDB_SUCCESS; } -static int ltdb_end_trans(struct ldb_module *module) +static int ltdb_prepare_commit(struct ldb_module *module) { - struct ltdb_private *ltdb = - talloc_get_type(module->private_data, struct ltdb_private); + void *data = ldb_module_get_private(module); + struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private); - if (tdb_transaction_commit(ltdb->tdb) != 0) { + if (ltdb->in_transaction != 1) { + return LDB_SUCCESS; + } + + if (ltdb_index_transaction_commit(module) != 0) { + tdb_transaction_cancel(ltdb->tdb); + ltdb->in_transaction--; return ltdb_err_map(tdb_error(ltdb->tdb)); } + if (tdb_transaction_prepare_commit(ltdb->tdb) != 0) { + ltdb->in_transaction--; + return ltdb_err_map(tdb_error(ltdb->tdb)); + } + + ltdb->prepared_commit = true; + return LDB_SUCCESS; } -static int ltdb_del_trans(struct ldb_module *module) +static int ltdb_end_trans(struct ldb_module *module) { - struct ltdb_private *ltdb = - talloc_get_type(module->private_data, struct ltdb_private); + void *data = ldb_module_get_private(module); + struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private); - if (tdb_transaction_cancel(ltdb->tdb) != 0) { + if (!ltdb->prepared_commit) { + int ret = ltdb_prepare_commit(module); + if (ret != LDB_SUCCESS) { + return ret; + } + } + + ltdb->in_transaction--; + ltdb->prepared_commit = false; + + if (tdb_transaction_commit(ltdb->tdb) != 0) { return ltdb_err_map(tdb_error(ltdb->tdb)); } return LDB_SUCCESS; } -static int ltdb_wait(struct ldb_handle *handle, enum ldb_wait_type type) +static int ltdb_del_trans(struct ldb_module *module) { - return handle->status; -} + void *data = ldb_module_get_private(module); + struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private); -static int ltdb_request(struct ldb_module *module, struct ldb_request *req) -{ - /* check for oustanding critical controls and return an error if found */ - if (check_critical_controls(req->controls)) { - return LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION; + ltdb->in_transaction--; + + if (ltdb_index_transaction_cancel(module) != 0) { + tdb_transaction_cancel(ltdb->tdb); + return ltdb_err_map(tdb_error(ltdb->tdb)); + } + + if (tdb_transaction_cancel(ltdb->tdb) != 0) { + return ltdb_err_map(tdb_error(ltdb->tdb)); } - - /* search, add, modify, delete, rename are handled by their own, no other op supported */ - return LDB_ERR_OPERATIONS_ERROR; + + return LDB_SUCCESS; } /* return sequenceNumber from @BASEINFO */ -static int ltdb_sequence_number(struct ldb_module *module, struct ldb_request *req) +static int ltdb_sequence_number(struct ltdb_context *ctx, + struct ldb_extended **ext) { - TALLOC_CTX *tmp_ctx = talloc_new(req); + struct ldb_context *ldb; + struct ldb_module *module = ctx->module; + struct ldb_request *req = ctx->req; + TALLOC_CTX *tmp_ctx; + struct ldb_seqnum_request *seq; + struct ldb_seqnum_result *res; struct ldb_message *msg = NULL; - struct ldb_dn *dn = ldb_dn_new(tmp_ctx, module->ldb, LTDB_BASEINFO); - int tret; + struct ldb_dn *dn; + const char *date; + int ret; - if (tmp_ctx == NULL) { - talloc_free(tmp_ctx); + 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; } - msg = talloc(tmp_ctx, struct ldb_message); - if (msg == NULL) { - talloc_free(tmp_ctx); + ldb_request_set_state(req, LDB_ASYNC_PENDING); + + if (ltdb_lock_read(module) != 0) { return LDB_ERR_OPERATIONS_ERROR; } - req->op.seq_num.flags = 0; + 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; + } - tret = ltdb_search_dn1(module, dn, msg); - if (tret != LDB_SUCCESS) { - talloc_free(tmp_ctx); - /* zero is as good as anything when we don't know */ - req->op.seq_num.seq_num = 0; - return LDB_SUCCESS; + dn = ldb_dn_new(tmp_ctx, ldb, LTDB_BASEINFO); + + msg = talloc(tmp_ctx, struct ldb_message); + if (msg == NULL) { + ret = LDB_ERR_OPERATIONS_ERROR; + goto done; + } + + ret = ltdb_search_dn1(module, dn, msg); + if (ret != LDB_SUCCESS) { + goto done; } - switch (req->op.seq_num.type) { + switch (seq->type) { case LDB_SEQ_HIGHEST_SEQ: - req->op.seq_num.seq_num = ldb_msg_find_attr_as_uint64(msg, LTDB_SEQUENCE_NUMBER, 0); + res->seq_num = ldb_msg_find_attr_as_uint64(msg, LTDB_SEQUENCE_NUMBER, 0); break; case LDB_SEQ_NEXT: - req->op.seq_num.seq_num = ldb_msg_find_attr_as_uint64(msg, LTDB_SEQUENCE_NUMBER, 0); - req->op.seq_num.seq_num++; + res->seq_num = ldb_msg_find_attr_as_uint64(msg, LTDB_SEQUENCE_NUMBER, 0); + res->seq_num++; break; case LDB_SEQ_HIGHEST_TIMESTAMP: - { - const char *date = ldb_msg_find_attr_as_string(msg, LTDB_MOD_TIMESTAMP, NULL); + date = ldb_msg_find_attr_as_string(msg, LTDB_MOD_TIMESTAMP, NULL); if (date) { - req->op.seq_num.seq_num = ldb_string_to_time(date); + res->seq_num = ldb_string_to_time(date); } else { - req->op.seq_num.seq_num = 0; + 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); + + ret = LDB_SUCCESS; + +done: talloc_free(tmp_ctx); + ltdb_unlock_read(module); + return ret; +} + +static void ltdb_request_done(struct ltdb_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 ltdb_timeout(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval t, + void *private_data) +{ + struct ltdb_context *ctx; + ctx = talloc_get_type(private_data, struct ltdb_context); + + if (!ctx->request_terminated) { + /* request is done now */ + ltdb_request_done(ctx, LDB_ERR_TIME_LIMIT_EXCEEDED); + } + + if (!ctx->request_terminated) { + /* neutralize the spy */ + ctx->spy->ctx = NULL; + } + talloc_free(ctx); +} + +static void ltdb_request_extended_done(struct ltdb_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 ltdb_handle_extended(struct ltdb_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 = ltdb_sequence_number(ctx, &ext); + } else { + /* not recognized */ + ret = LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION; + } + + ltdb_request_extended_done(ctx, ext, ret); +} + +static void ltdb_callback(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval t, + void *private_data) +{ + struct ltdb_context *ctx; + int ret; + + ctx = talloc_get_type(private_data, struct ltdb_context); + + if (ctx->request_terminated) { + goto done; + } + + switch (ctx->req->operation) { + case LDB_SEARCH: + ret = ltdb_search(ctx); + break; + case LDB_ADD: + ret = ltdb_add(ctx); + break; + case LDB_MODIFY: + ret = ltdb_modify(ctx); + break; + case LDB_DELETE: + ret = ltdb_delete(ctx); + break; + case LDB_RENAME: + ret = ltdb_rename(ctx); + break; + case LDB_EXTENDED: + ltdb_handle_extended(ctx); + goto done; + default: + /* no other op supported */ + ret = LDB_ERR_UNWILLING_TO_PERFORM; + } + + if (!ctx->request_terminated) { + /* request is done now */ + ltdb_request_done(ctx, ret); + } + +done: + if (!ctx->request_terminated) { + /* neutralize the spy */ + ctx->spy->ctx = NULL; + } + talloc_free(ctx); +} + +static int ltdb_request_destructor(void *ptr) +{ + struct ltdb_req_spy *spy = talloc_get_type(ptr, struct ltdb_req_spy); + + if (spy->ctx != NULL) { + spy->ctx->request_terminated = true; + } + + return 0; +} + +static int ltdb_handle_request(struct ldb_module *module, + struct ldb_request *req) +{ + struct ldb_context *ldb; + struct tevent_context *ev; + struct ltdb_context *ac; + struct tevent_timer *te; + struct timeval tv; + + if (check_critical_controls(req->controls)) { + return LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION; + } + + ldb = ldb_module_get_ctx(module); + + if (req->starttime == 0 || req->timeout == 0) { + ldb_set_errstring(ldb, "Invalid timeout settings"); + return LDB_ERR_TIME_LIMIT_EXCEEDED; + } + + ev = ldb_get_event_context(ldb); + + ac = talloc_zero(ldb, struct ltdb_context); + if (ac == NULL) { + ldb_set_errstring(ldb, "Out of Memory"); + 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, ltdb_callback, ac); + if (NULL == te) { + talloc_free(ac); + return LDB_ERR_OPERATIONS_ERROR; + } + + tv.tv_sec = req->starttime + req->timeout; + ac->timeout_event = tevent_add_timer(ev, ac, tv, ltdb_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 ltdb_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, ltdb_request_destructor); + return LDB_SUCCESS; } static const struct ldb_module_ops ltdb_ops = { .name = "tdb", - .search = ltdb_search, - .add = ltdb_add, - .modify = ltdb_modify, - .del = ltdb_delete, - .rename = ltdb_rename, - .request = ltdb_request, + .search = ltdb_handle_request, + .add = ltdb_handle_request, + .modify = ltdb_handle_request, + .del = ltdb_handle_request, + .rename = ltdb_handle_request, + .extended = ltdb_handle_request, .start_transaction = ltdb_start_trans, .end_transaction = ltdb_end_trans, + .prepare_commit = ltdb_prepare_commit, .del_transaction = ltdb_del_trans, - .wait = ltdb_wait, - .sequence_number = ltdb_sequence_number }; /* connect to the database */ -static int ltdb_connect(struct ldb_context *ldb, const char *url, +static int ltdb_connect(struct ldb_context *ldb, const char *url, unsigned int flags, const char *options[], - struct ldb_module **module) + struct ldb_module **_module) { + struct ldb_module *module; const char *path; int tdb_flags, open_flags; struct ltdb_private *ltdb; @@ -1034,7 +1288,8 @@ static int ltdb_connect(struct ldb_context *ldb, const char *url, /* parse the url */ if (strchr(url, ':')) { if (strncmp(url, "tdb://", 6) != 0) { - ldb_debug(ldb, LDB_DEBUG_ERROR, "Invalid tdb URL '%s'", url); + ldb_debug(ldb, LDB_DEBUG_ERROR, + "Invalid tdb URL '%s'", url); return -1; } path = url+6; @@ -1067,39 +1322,36 @@ static int ltdb_connect(struct ldb_context *ldb, const char *url, } /* note that we use quite a large default hash size */ - ltdb->tdb = ltdb_wrap_open(ltdb, path, 10000, - tdb_flags, open_flags, - ldb->create_perms, ldb); + ltdb->tdb = ltdb_wrap_open(ltdb, path, 10000, + tdb_flags, open_flags, + ldb_get_create_perms(ldb), ldb); if (!ltdb->tdb) { - ldb_debug(ldb, LDB_DEBUG_ERROR, "Unable to open tdb '%s'\n", path); + ldb_debug(ldb, LDB_DEBUG_ERROR, + "Unable to open tdb '%s'", path); talloc_free(ltdb); return -1; } ltdb->sequence_number = 0; - *module = talloc(ldb, struct ldb_module); + module = ldb_module_new(ldb, ldb, "ldb_tdb backend", <db_ops); if (!module) { - ldb_oom(ldb); talloc_free(ltdb); return -1; } - talloc_set_name_const(*module, "ldb_tdb backend"); - (*module)->ldb = ldb; - (*module)->prev = (*module)->next = NULL; - (*module)->private_data = ltdb; - (*module)->ops = <db_ops; + ldb_module_set_private(module, ltdb); - if (ltdb_cache_load(*module) != 0) { - talloc_free(*module); + if (ltdb_cache_load(module) != 0) { + talloc_free(module); talloc_free(ltdb); return -1; } + *_module = module; return 0; } -int ldb_tdb_init(void) -{ - return ldb_register_backend("tdb", ltdb_connect); -} +const struct ldb_backend_ops ldb_tdb_backend_ops = { + .name = "tdb", + .connect_fn = ltdb_connect +};