* Author: Simo Sorce
*/
-#include "ldb_includes.h"
-
#include "ldb_tdb.h"
*/
int ltdb_lock_read(struct ldb_module *module)
{
- struct ltdb_private *ltdb = (struct ltdb_private *)module->private_data;
+ 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);
}
*/
int ltdb_unlock_read(struct ldb_module *module)
{
- struct ltdb_private *ltdb = (struct ltdb_private *)module->private_data;
+ 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);
}
*/
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;
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) ||
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;
}
}
*/
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;
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 LDB_ERR_OPERATIONS_ERROR;
}
+ for (i=0;i<msg->num_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,
+ ldb_asprintf_errstring(ldb,
"Entry %s already exists",
ldb_dn_get_linearized(msg->dn));
return ret;
struct ldb_request *req = ctx->req;
int tret;
- req->handle->state = LDB_ASYNC_PENDING;
+ ldb_request_set_state(req, LDB_ASYNC_PENDING);
tret = ltdb_add_internal(module, req->op.add.message);
if (tret != LDB_SUCCESS) {
*/
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;
struct ldb_request *req = ctx->req;
int tret;
- req->handle->state = LDB_ASYNC_PENDING;
+ ldb_request_set_state(req, LDB_ASYNC_PENDING);
if (ltdb_cache_load(module) != 0) {
return LDB_ERR_OPERATIONS_ERROR;
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;
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;
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;
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,
for (j=0;j<el->num_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;
}
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;j<el->num_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;
}
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;
}
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;
}
}
break;
default:
- ldb_asprintf_errstring(module->ldb,
+ ldb_asprintf_errstring(ldb,
"Invalid ldb_modify flags on %s: 0x%x",
msg->elements[i].name,
msg->elements[i].flags & LDB_FLAG_MOD_MASK);
struct ldb_request *req = ctx->req;
int tret;
- req->handle->state = LDB_ASYNC_PENDING;
+ ldb_request_set_state(req, LDB_ASYNC_PENDING);
tret = ltdb_check_special_dn(module, req->op.mod.message);
if (tret != LDB_SUCCESS) {
struct ldb_message *msg;
int tret;
- req->handle->state = LDB_ASYNC_PENDING;
+ ldb_request_set_state(req, LDB_ASYNC_PENDING);
if (ltdb_cache_load(ctx->module) != 0) {
return LDB_ERR_OPERATIONS_ERROR;
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. */
- tret = ltdb_delete_internal(module, req->op.rename.olddn);
- if (tret != LDB_SUCCESS) {
- return tret;
- }
-
- tret = ltdb_add_internal(module, msg);
- if (tret != LDB_SUCCESS) {
- return tret;
- }
- } 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. */
- tret = ltdb_add_internal(module, msg);
- if (tret != LDB_SUCCESS) {
- return tret;
- }
+ /* 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;
+ }
- tret = ltdb_delete_internal(module, req->op.rename.olddn);
- if (tret != LDB_SUCCESS) {
- ltdb_delete_internal(module, req->op.rename.newdn);
- return LDB_ERR_OPERATIONS_ERROR;
- }
+ tret = ltdb_add_internal(module, msg);
+ if (tret != LDB_SUCCESS) {
+ return tret;
}
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));
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);
- ltdb->in_transaction--;
+ 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_end_trans(struct ldb_module *module)
+{
+ void *data = ldb_module_get_private(module);
+ struct ltdb_private *ltdb = talloc_get_type(data, struct ltdb_private);
+
+ 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));
}
static int ltdb_del_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);
ltdb->in_transaction--;
if (ltdb_index_transaction_cancel(module) != 0) {
+ tdb_transaction_cancel(ltdb->tdb);
return ltdb_err_map(tdb_error(ltdb->tdb));
}
static int ltdb_sequence_number(struct ltdb_context *ctx,
struct ldb_extended **ext)
{
+ struct ldb_context *ldb;
struct ldb_module *module = ctx->module;
struct ldb_request *req = ctx->req;
TALLOC_CTX *tmp_ctx;
const char *date;
int ret;
+ 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;
}
- req->handle->state = LDB_ASYNC_PENDING;
+ ldb_request_set_state(req, LDB_ASYNC_PENDING);
if (ltdb_lock_read(module) != 0) {
return LDB_ERR_OPERATIONS_ERROR;
goto done;
}
- dn = ldb_dn_new(tmp_ctx, module->ldb, LTDB_BASEINFO);
+ dn = ldb_dn_new(tmp_ctx, ldb, LTDB_BASEINFO);
msg = talloc(tmp_ctx, struct ldb_message);
if (msg == NULL) {
return ret;
}
-static void ltdb_request_done(struct ldb_request *req, int error)
+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 (req->handle->status != LDB_SUCCESS) {
+ if (ldb_request_get_status(req) != LDB_SUCCESS) {
return;
}
ares = talloc_zero(req, struct ldb_reply);
if (!ares) {
- ldb_oom(req->handle->ldb);
+ ldb_oom(ldb);
req->callback(req, NULL);
return;
}
struct ltdb_context *ctx;
ctx = talloc_get_type(private_data, struct ltdb_context);
- ltdb_request_done(ctx->req, LDB_ERR_TIME_LIMIT_EXCEEDED);
+ 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 ldb_request *req,
+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 (req->handle->status != LDB_SUCCESS) {
+ if (ldb_request_get_status(req) != LDB_SUCCESS) {
return;
}
ares = talloc_zero(req, struct ldb_reply);
if (!ares) {
- ldb_oom(req->handle->ldb);
+ ldb_oom(ldb);
req->callback(req, NULL);
return;
}
ret = LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION;
}
- ltdb_request_extended_done(ctx->req, ext, ret);
+ ltdb_request_extended_done(ctx, ext, ret);
}
static void ltdb_callback(struct tevent_context *ev,
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_EXTENDED:
ltdb_handle_extended(ctx);
- return;
+ goto done;
default:
/* no other op supported */
ret = LDB_ERR_UNWILLING_TO_PERFORM;
}
- if (!ctx->callback_failed) {
- /* Once we are done, we do not need timeout events */
- talloc_free(ctx->timeout_event);
- ltdb_request_done(ctx->req, ret);
+ 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;
return LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION;
}
+ ldb = ldb_module_get_ctx(module);
+
if (req->starttime == 0 || req->timeout == 0) {
- ldb_set_errstring(module->ldb, "Invalid timeout settings");
+ ldb_set_errstring(ldb, "Invalid timeout settings");
return LDB_ERR_TIME_LIMIT_EXCEEDED;
}
- ev = ldb_get_event_context(module->ldb);
+ ev = ldb_get_event_context(ldb);
- ac = talloc_zero(req, struct ltdb_context);
+ ac = talloc_zero(ldb, struct ltdb_context);
if (ac == NULL) {
- ldb_set_errstring(module->ldb, "Out of Memory");
+ ldb_set_errstring(ldb, "Out of Memory");
return LDB_ERR_OPERATIONS_ERROR;
}
tv.tv_sec = 0;
tv.tv_usec = 0;
- te = event_add_timed(ev, ac, tv, ltdb_callback, ac);
+ 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 = event_add_timed(ev, ac, tv, ltdb_timeout, ac);
+ 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;
}
.extended = ltdb_handle_request,
.start_transaction = ltdb_start_trans,
.end_transaction = ltdb_end_trans,
+ .prepare_commit = ltdb_prepare_commit,
.del_transaction = ltdb_del_trans,
};
*/
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;
/* 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);
+ ldb_get_create_perms(ldb), ldb);
if (!ltdb->tdb) {
ldb_debug(ldb, LDB_DEBUG_ERROR,
- "Unable to open tdb '%s'\n", path);
+ "Unable to open tdb '%s'", path);
talloc_free(ltdb);
return -1;
}
ltdb->sequence_number = 0;
- *module = talloc(ldb, struct ldb_module);
- if ((*module) == NULL) {
- ldb_oom(ldb);
+ module = ldb_module_new(ldb, ldb, "ldb_tdb backend", <db_ops);
+ if (!module) {
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;
}