summaryrefslogtreecommitdiff
path: root/source4/lib/ldb/modules/objectclass.c
diff options
context:
space:
mode:
Diffstat (limited to 'source4/lib/ldb/modules/objectclass.c')
-rw-r--r--source4/lib/ldb/modules/objectclass.c440
1 files changed, 342 insertions, 98 deletions
diff --git a/source4/lib/ldb/modules/objectclass.c b/source4/lib/ldb/modules/objectclass.c
index a9c51341a8..4423f82aed 100644
--- a/source4/lib/ldb/modules/objectclass.c
+++ b/source4/lib/ldb/modules/objectclass.c
@@ -1,7 +1,8 @@
/*
ldb database library
- Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
+ Copyright (C) Simo Sorce 2006
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2006
** NOTE! The following LGPL license applies to the ldb
** library. This does NOT imply that all of Samba is released
@@ -35,98 +36,256 @@
#include "includes.h"
#include "ldb/include/includes.h"
-/* It turns out the MMC assumes that the last objectClass in the list
- * is the most specific subclass. As such, we must sort the list,
- * according to the schema.
- *
- * For performance, we do this on the add/modify, not on the search
- *
- * We perform the original add/modify, then search for that is now in
- * the objectClass list. We can then then replace that with the new
- * sorted list. The backend is expected to preserve ordering for
- * subsequent searches.
- *
- * We are in a transaction, so this is all perfectly safe...
- */
+struct oc_async_context {
+
+ enum oc_step {OC_DO_REQ, OC_SEARCH_SELF, OC_DO_MOD} step;
+
+ struct ldb_module *module;
+ struct ldb_request *orig_req;
+
+ struct ldb_request *down_req;
+
+ struct ldb_request *search_req;
+ struct ldb_async_result *search_res;
+
+ struct ldb_request *mod_req;
+};
-static int objectclass_handle(struct ldb_module *module, struct ldb_request *req, const struct ldb_message *msg)
+static struct ldb_async_handle *oc_init_handle(struct ldb_request *req, struct ldb_module *module)
{
- TALLOC_CTX *mem_ctx;
- int ret;
- struct ldb_request *search_request;
- struct ldb_request *modify_request;
- struct ldb_message *modify_msg;
- struct ldb_result *res;
- const char *attrs[] = { "objectClass", NULL };
- struct class_list {
- struct class_list *prev, *next;
- const char *objectclass;
- };
- struct class_list *sorted = NULL, *parent_class = NULL,
- *subclass = NULL, *unsorted = NULL, *current, *poss_subclass;
- int i;
- int layer;
+ struct oc_async_context *ac;
+ struct ldb_async_handle *h;
- struct ldb_message_element *objectclass_element;
+ h = talloc_zero(req, struct ldb_async_handle);
+ if (h == NULL) {
+ ldb_set_errstring(module->ldb, talloc_asprintf(module, "Out of Memory"));
+ return NULL;
+ }
+
+ h->module = module;
+
+ ac = talloc_zero(h, struct oc_async_context);
+ if (ac == NULL) {
+ ldb_set_errstring(module->ldb, talloc_asprintf(module, "Out of Memory"));
+ talloc_free(h);
+ return NULL;
+ }
+
+ h->private_data = (void *)ac;
+
+ h->state = LDB_ASYNC_INIT;
+ h->status = LDB_SUCCESS;
+
+ ac->module = module;
+ ac->orig_req = req;
+
+ return h;
+}
+
+static int objectclass_add(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_async_handle *h;
+ struct oc_async_context *ac;
+ struct ldb_message_element *objectClassAttr;
- ldb_debug(module->ldb, LDB_DEBUG_TRACE, "objectclass_handle\n");
+ ldb_debug(module->ldb, LDB_DEBUG_TRACE, "objectclass_add\n");
- if (ldb_dn_is_special(msg->dn)) { /* do not manipulate our control entries */
+ if (ldb_dn_is_special(req->op.add.message->dn)) { /* do not manipulate our control entries */
return ldb_next_request(module, req);
}
+
+ objectClassAttr = ldb_msg_find_element(req->op.add.message, "objectClass");
+
+ /* If no part of this touches the objectClass, then we don't
+ * need to make any changes. */
+ /* If the only operation is the deletion of the objectClass then go on */
+ if (!objectClassAttr) {
+ ldb_set_errstring(module->ldb, talloc_asprintf(ac, "Object class violation: no objectClass present"));
+ return LDB_ERR_OBJECT_CLASS_VIOLATION;
+ }
+
+ h = oc_init_handle(req, module);
+ if (!h) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ac = talloc_get_type(h->private_data, struct oc_async_context);
- ret = ldb_next_request(module, req);
+ /* return or own handle to deal with this call */
+ req->async.handle = h;
- if (ret) {
- return ret;
+ /* prepare the first operation */
+ ac->down_req = talloc_zero(ac, struct ldb_request);
+ if (ac->down_req == NULL) {
+ ldb_set_errstring(module->ldb, talloc_asprintf(module->ldb, "Out of memory!"));
+ return LDB_ERR_OPERATIONS_ERROR;
}
- if (ldb_msg_find_element(msg, "objectClass") == NULL ) {
- /* No sign of the objectClass: no change, nothing to see here */
- return ret;
+ *(ac->down_req) = *req; /* copy the request */
+
+ ac->down_req->async.context = NULL;
+ ac->down_req->async.callback = NULL;
+
+ ac->step = OC_DO_REQ;
+
+ return ldb_next_request(module, ac->down_req);
+}
+
+static int objectclass_modify(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_async_handle *h;
+ struct oc_async_context *ac;
+ struct ldb_message_element *objectClassAttr;
+
+ ldb_debug(module->ldb, LDB_DEBUG_TRACE, "objectclass_modify\n");
+
+ if (ldb_dn_is_special(req->op.mod.message->dn)) { /* do not manipulate our control entries */
+ return ldb_next_request(module, req);
}
+
+ objectClassAttr = ldb_msg_find_element(req->op.mod.message, "objectClass");
- /* Thanks to transactions: Now do a search, find the full list
- * of objectClasses and do the sort */
+ /* If no part of this touches the objectClass, then we don't
+ * need to make any changes. */
+ /* If the only operation is the deletion of the objectClass then go on */
+ if (!objectClassAttr) {
+ return ldb_next_request(module, req);
+ }
- mem_ctx = talloc_new(module);
- if (!mem_ctx) {
+ h = oc_init_handle(req, module);
+ if (!h) {
return LDB_ERR_OPERATIONS_ERROR;
}
+ ac = talloc_get_type(h->private_data, struct oc_async_context);
- search_request = talloc(mem_ctx, struct ldb_request);
- if (!search_request) {
- talloc_free(mem_ctx);
+ /* return or own handle to deal with this call */
+ req->async.handle = h;
+
+ /* prepare the first operation */
+ ac->down_req = talloc_zero(ac, struct ldb_request);
+ if (ac->down_req == NULL) {
+ ldb_set_errstring(module->ldb, talloc_asprintf(module->ldb, "Out of memory!"));
return LDB_ERR_OPERATIONS_ERROR;
}
- search_request->operation = LDB_REQ_SEARCH;
- search_request->op.search.base = msg->dn;
- search_request->op.search.scope = LDB_SCOPE_BASE;
- search_request->op.search.tree = ldb_parse_tree(module->ldb, NULL);
- search_request->op.search.attrs = attrs;
- search_request->controls = NULL;
+ *(ac->down_req) = *req; /* copy the request */
- ret = ldb_next_request(module, search_request);
- if (ret) {
- return ret;
+ ac->down_req->async.context = NULL;
+ ac->down_req->async.callback = NULL;
+
+ ac->step = OC_DO_REQ;
+
+ return ldb_next_request(module, ac->down_req);
+}
+
+static int get_self_callback(struct ldb_context *ldb, void *context, struct ldb_async_result *ares)
+{
+ struct oc_async_context *ac;
+
+ if (!context || !ares) {
+ ldb_set_errstring(ldb, talloc_asprintf(ldb, "NULL Context or Result in callback"));
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac = talloc_get_type(context, struct oc_async_context);
+
+ /* we are interested only in the single reply (base search) we receive here */
+ if (ares->type == LDB_REPLY_ENTRY) {
+ if (ac->search_res != NULL) {
+ ldb_set_errstring(ldb, talloc_asprintf(ldb, "Too many results"));
+ talloc_free(ares);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->search_res = talloc_steal(ac, ares);
+ } else {
+ talloc_free(ares);
+ }
+
+ return LDB_SUCCESS;
+}
+
+static int objectclass_search_self(struct ldb_async_handle *h) {
+
+ struct oc_async_context *ac;
+ static const char * const attrs[] = { "objectClass", NULL };
+
+ ac = talloc_get_type(h->private_data, struct oc_async_context);
+
+ /* prepare the search operation */
+ ac->search_req = talloc_zero(ac, struct ldb_request);
+ if (ac->search_req == NULL) {
+ ldb_debug(ac->module->ldb, LDB_DEBUG_ERROR, "Out of Memory!\n");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->search_req->operation = LDB_SEARCH;
+ ac->search_req->op.search.base = ac->orig_req->op.mod.message->dn;
+ ac->search_req->op.search.scope = LDB_SCOPE_BASE;
+ ac->search_req->op.search.tree = ldb_parse_tree(ac->module->ldb, NULL);
+ if (ac->search_req->op.search.tree == NULL) {
+ ldb_set_errstring(ac->module->ldb, talloc_asprintf(ac, "objectclass: Internal error producing null search"));
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ac->search_req->op.search.attrs = attrs;
+ ac->search_req->controls = NULL;
+ ac->search_req->async.context = ac;
+ ac->search_req->async.callback = get_self_callback;
+ ac->search_req->async.timeout = ac->orig_req->async.timeout;
+
+ ac->step = OC_SEARCH_SELF;
+
+ return ldb_next_request(ac->module, ac->search_req);
+}
+
+static int objectclass_do_mod(struct ldb_async_handle *h) {
+
+ struct oc_async_context *ac;
+ struct ldb_message_element *objectclass_element;
+ struct ldb_message *msg;
+ TALLOC_CTX *mem_ctx;
+ struct class_list {
+ struct class_list *prev, *next;
+ const char *objectclass;
+ };
+ struct class_list *sorted = NULL, *parent_class = NULL,
+ *subclass = NULL, *unsorted = NULL, *current, *poss_subclass;
+ int i;
+ int layer;
+ int ret;
+
+ ac = talloc_get_type(h->private_data, struct oc_async_context);
+
+ mem_ctx = talloc_new(ac);
+ if (mem_ctx == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->mod_req = talloc(ac, struct ldb_request);
+ if (ac->mod_req == NULL) {
+ talloc_free(mem_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
}
- res = search_request->op.search.res;
- talloc_steal(mem_ctx, res);
- if (res->count != 1) {
- ldb_set_errstring(module->ldb,
- talloc_asprintf(mem_ctx, "objectClass_handle: "
- "search for %s found %d != 1 objects, for entry we just added/modified",
- ldb_dn_linearize(mem_ctx, msg->dn),
- res->count));
- /* What happened? The above add/modify worked... */
+ ac->mod_req->operation = LDB_MODIFY;
+ ac->mod_req->controls = NULL;
+ ac->mod_req->async.context = ac;
+ ac->mod_req->async.callback = NULL;
+ ac->mod_req->async.timeout = ac->orig_req->async.timeout;
+
+ /* use a new message structure */
+ ac->mod_req->op.mod.message = msg = ldb_msg_new(ac->mod_req);
+ if (msg == NULL) {
+ ldb_set_errstring(ac->module->ldb, talloc_asprintf(ac, "objectclass: could not create new modify msg"));
talloc_free(mem_ctx);
- return LDB_ERR_NO_SUCH_OBJECT;
+ return LDB_ERR_OPERATIONS_ERROR;
}
+ /* modify dn */
+ msg->dn = ac->orig_req->op.mod.message->dn;
+
/* This is now the objectClass list from the database */
- objectclass_element = ldb_msg_find_element(res->msgs[0], "objectClass");
+ objectclass_element = ldb_msg_find_element(ac->search_res->message,
+ "objectClass");
if (!objectclass_element) {
/* Perhaps the above was a remove? Move along now, nothing to see here */
talloc_free(mem_ctx);
@@ -166,6 +325,7 @@ static int objectclass_handle(struct ldb_module *module, struct ldb_request *req
for (i=0; i < objectclass_element->num_values; i++) {
current = talloc(mem_ctx, struct class_list);
if (!current) {
+ ldb_set_errstring(ac->module->ldb, talloc_asprintf(ac, "objectclass: out of memory allocating objectclass list"));
talloc_free(mem_ctx);
return LDB_ERR_OPERATIONS_ERROR;
}
@@ -189,7 +349,7 @@ static int objectclass_handle(struct ldb_module *module, struct ldb_request *req
/* Ensure we don't bother if there are no unsorted entries left */
for (current = parent_class; unsorted && current; current = current->next) {
- const char **subclasses = ldb_subclass_list(module->ldb, current->objectclass);
+ const char **subclasses = ldb_subclass_list(ac->module->ldb, current->objectclass);
/* Walk the list of possible subclasses in unsorted */
for (poss_subclass = unsorted; poss_subclass; ) {
@@ -231,78 +391,162 @@ static int objectclass_handle(struct ldb_module *module, struct ldb_request *req
*/
DLIST_CONCATENATE(sorted, unsorted, struct class_list *);
- modify_msg = ldb_msg_new(mem_ctx);
- if (!modify_msg) {
- talloc_free(mem_ctx);
- return LDB_ERR_OPERATIONS_ERROR;
- }
- modify_msg->dn = talloc_reference(modify_msg, msg->dn);
-
/* We must completely replace the existing objectClass entry.
* We could do a constrained add/del, but we are meant to be
* in a transaction... */
- ret = ldb_msg_add_empty(modify_msg, "objectClass", LDB_FLAG_MOD_REPLACE);
+ ret = ldb_msg_add_empty(msg, "objectClass", LDB_FLAG_MOD_REPLACE);
if (ret != LDB_SUCCESS) {
+ ldb_set_errstring(ac->module->ldb, talloc_asprintf(ac, "objectclass: could not clear objectclass in modify msg"));
talloc_free(mem_ctx);
return ret;
}
/* Move from the linked list back into an ldb msg */
for (current = sorted; current; current = current->next) {
- ret = ldb_msg_add_string(modify_msg, "objectClass", current->objectclass);
+ ret = ldb_msg_add_string(msg, "objectClass", current->objectclass);
if (ret != LDB_SUCCESS) {
+ ldb_set_errstring(ac->module->ldb, talloc_asprintf(ac, "objectclass: could not re-add sorted objectclass to modify msg"));
talloc_free(mem_ctx);
return ret;
}
}
- ret = ldb_msg_sanity_check(modify_msg);
+ ret = ldb_msg_sanity_check(msg);
if (ret != LDB_SUCCESS) {
talloc_free(mem_ctx);
return ret;
}
- modify_request = talloc(mem_ctx, struct ldb_request);
- if (!modify_request) {
- talloc_free(mem_ctx);
+
+ h->state = LDB_ASYNC_INIT;
+ h->status = LDB_SUCCESS;
+
+ ac->step = OC_DO_MOD;
+
+ talloc_free(mem_ctx);
+ /* perform the search */
+ return ldb_next_request(ac->module, ac->mod_req);
+}
+
+static int oc_async_wait(struct ldb_async_handle *handle) {
+ struct oc_async_context *ac;
+ int ret;
+
+ if (!handle || !handle->private_data) {
return LDB_ERR_OPERATIONS_ERROR;
}
- modify_request->operation = LDB_REQ_MODIFY;
- modify_request->op.mod.message = modify_msg;
- modify_request->controls = NULL;
+ if (handle->state == LDB_ASYNC_DONE) {
+ return handle->status;
+ }
- /* And now push the write into the database */
- ret = ldb_next_request(module, modify_request);
-
- talloc_free(mem_ctx);
+ handle->state = LDB_ASYNC_PENDING;
+ handle->status = LDB_SUCCESS;
+
+ ac = talloc_get_type(handle->private_data, struct oc_async_context);
+
+ switch (ac->step) {
+ case OC_DO_REQ:
+ ret = ldb_async_wait(ac->down_req->async.handle, LDB_WAIT_NONE);
+
+ if (ret != LDB_SUCCESS) {
+ handle->status = ret;
+ goto done;
+ }
+ if (ac->down_req->async.handle->status != LDB_SUCCESS) {
+ handle->status = ac->down_req->async.handle->status;
+ goto done;
+ }
+
+ if (ac->down_req->async.handle->state != LDB_ASYNC_DONE) {
+ return LDB_SUCCESS;
+ }
+
+ /* mods done, go on */
+ return objectclass_search_self(handle);
+
+ case OC_SEARCH_SELF:
+ ret = ldb_async_wait(ac->search_req->async.handle, LDB_WAIT_NONE);
+
+ if (ret != LDB_SUCCESS) {
+ handle->status = ret;
+ goto done;
+ }
+ if (ac->search_req->async.handle->status != LDB_SUCCESS) {
+ handle->status = ac->search_req->async.handle->status;
+ goto done;
+ }
+
+ if (ac->search_req->async.handle->state != LDB_ASYNC_DONE) {
+ return LDB_SUCCESS;
+ }
+
+ /* self search done, go on */
+ return objectclass_do_mod(handle);
+
+ case OC_DO_MOD:
+ ret = ldb_async_wait(ac->mod_req->async.handle, LDB_WAIT_NONE);
+
+ if (ret != LDB_SUCCESS) {
+ handle->status = ret;
+ goto done;
+ }
+ if (ac->mod_req->async.handle->status != LDB_SUCCESS) {
+ handle->status = ac->mod_req->async.handle->status;
+ goto done;
+ }
+
+ if (ac->mod_req->async.handle->state != LDB_ASYNC_DONE) {
+ return LDB_SUCCESS;
+ }
+
+ break;
+
+ default:
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ goto done;
+ }
+
+ ret = LDB_SUCCESS;
+
+done:
+ handle->state = LDB_ASYNC_DONE;
return ret;
}
-static int objectclass_request(struct ldb_module *module, struct ldb_request *req)
-{
- switch (req->operation) {
+static int oc_async_wait_all(struct ldb_async_handle *handle) {
- /* only care about add and modify requests */
- case LDB_REQ_ADD:
- return objectclass_handle(module, req, req->op.add.message);
+ int ret;
- case LDB_REQ_MODIFY:
- return objectclass_handle(module, req, req->op.mod.message);
+ while (handle->state != LDB_ASYNC_DONE) {
+ ret = oc_async_wait(handle);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
- default:
- return ldb_next_request(module, req);
+ return handle->status;
+}
+static int objectclass_async_wait(struct ldb_async_handle *handle, enum ldb_async_wait_type type)
+{
+ if (type == LDB_WAIT_ALL) {
+ return oc_async_wait_all(handle);
+ } else {
+ return oc_async_wait(handle);
}
}
static const struct ldb_module_ops objectclass_ops = {
.name = "objectclass",
- .request = objectclass_request,
+ .add = objectclass_add,
+ .modify = objectclass_modify,
+ .async_wait = objectclass_async_wait
};
int ldb_objectclass_init(void)
{
return ldb_register_module(&objectclass_ops);
}
+