From 7c721a1f49d576e0a47c35e465206ade1c05d5a9 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 29 Oct 2007 10:54:06 +0100 Subject: r25747: Implement linked attributes, for add operations. Much more work is still required here, particularly to handle this better during the provision, and to handle modifies and deletes, but this is a start. Andrew Bartlett (This used to be commit 2ba99d58e9fe1f8e4b15a58a2fdfce6e876f99b4) --- source4/dsdb/samdb/ldb_modules/config.mk | 12 + source4/dsdb/samdb/ldb_modules/linked_attributes.c | 312 +++++++++++++++++++++ 2 files changed, 324 insertions(+) create mode 100644 source4/dsdb/samdb/ldb_modules/linked_attributes.c (limited to 'source4/dsdb/samdb/ldb_modules') diff --git a/source4/dsdb/samdb/ldb_modules/config.mk b/source4/dsdb/samdb/ldb_modules/config.mk index f35f26371b..a93dea7db7 100644 --- a/source4/dsdb/samdb/ldb_modules/config.mk +++ b/source4/dsdb/samdb/ldb_modules/config.mk @@ -244,3 +244,15 @@ OBJ_FILES = \ # End MODULE ldb_subtree_rename ################################################ +################################################ +# Start MODULE ldb_linked_attributes +[MODULE::ldb_linked_attributes] +INIT_FUNCTION = ldb_linked_attributes_init +CFLAGS = -Ilib/ldb/include +PRIVATE_DEPENDENCIES = LIBTALLOC SAMDB +SUBSYSTEM = LIBLDB +OBJ_FILES = \ + linked_attributes.o +# End MODULE ldb_linked_attributes +################################################ + diff --git a/source4/dsdb/samdb/ldb_modules/linked_attributes.c b/source4/dsdb/samdb/ldb_modules/linked_attributes.c new file mode 100644 index 0000000000..f386795643 --- /dev/null +++ b/source4/dsdb/samdb/ldb_modules/linked_attributes.c @@ -0,0 +1,312 @@ +/* + ldb database library + + Copyright (C) Andrew Bartlett 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* + * Name: ldb + * + * Component: ldb linked_attributes module + * + * Description: Module to ensure linked attribute pairs remain in sync + * + * Author: Andrew Bartlett + */ + +#include "includes.h" +#include "ldb/include/ldb.h" +#include "ldb/include/ldb_errors.h" +#include "ldb/include/ldb_private.h" +#include "dsdb/samdb/samdb.h" + +struct linked_attributes_context { + struct ldb_module *module; + struct ldb_handle *handle; + struct ldb_request *orig_req; + + struct ldb_request **down_req; + int num_requests; + int finished_requests; +}; + +static struct linked_attributes_context *linked_attributes_init_handle(struct ldb_request *req, + struct ldb_module *module) +{ + struct linked_attributes_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; + } + + h->module = module; + + ac = talloc_zero(h, struct linked_attributes_context); + if (ac == NULL) { + ldb_set_errstring(module->ldb, "Out of Memory"); + talloc_free(h); + return NULL; + } + + h->private_data = ac; + + ac->module = module; + ac->handle = h; + ac->orig_req = req; + + req->handle = h; + + return ac; +} + +/* add */ +static int linked_attributes_add(struct ldb_module *module, struct ldb_request *req) +{ + int i, j, ret; + struct linked_attributes_context *ac; + + const struct dsdb_schema *schema = dsdb_get_schema(module->ldb); + if (!schema) { + /* without schema, this doesn't make any sense */ + return ldb_next_request(module, req); + } + + if (ldb_dn_is_special(req->op.mod.message->dn)) { + /* do not manipulate our control entries */ + return ldb_next_request(module, req); + } + + + ac = linked_attributes_init_handle(req, module); + if (!ac) { + return LDB_ERR_OPERATIONS_ERROR; + } + + /* prepare the first operation */ + ac->down_req = talloc_realloc(ac, ac->down_req, + struct ldb_request *, 1); + if (!ac->down_req) { + ldb_oom(ac->module->ldb); + return LDB_ERR_OPERATIONS_ERROR; + } + + ac->down_req[0] = talloc(ac->down_req, struct ldb_request); + if (!ac->down_req[0]) { + ldb_oom(ac->module->ldb); + return LDB_ERR_OPERATIONS_ERROR; + } + *(ac->down_req[0]) = *req; /* copy the request */ + + ac->num_requests++; + + /* Run the original request */ + ret = ldb_next_request(module, req); + if (ret != LDB_SUCCESS) { + return ret; + } + + for (i=0; i < req->op.add.message->num_elements; i++) { + const struct dsdb_attribute *target_attr; + const struct ldb_message_element *el = &req->op.add.message->elements[i]; + const struct dsdb_attribute *schema_attr + = dsdb_attribute_by_lDAPDisplayName(schema, el->name); + if (!schema_attr) { + ldb_asprintf_errstring(module->ldb, + "attribute %s is not a valid attribute in schema", req->op.add.message->elements[i].name); + return LDB_ERR_OBJECT_CLASS_VIOLATION; + } + /* We have a valid attribute, not find out if it is linked */ + if (schema_attr->linkID == 0) { + continue; + } + + if ((schema_attr->linkID & 1) == 1) { + /* Odd is for the target. Illigal to modify */ + ldb_asprintf_errstring(module->ldb, + "attribute %s must not be modified directly, it is a linked attribute", req->op.add.message->elements[i].name); + return LDB_ERR_UNWILLING_TO_PERFORM; + } + + /* Even link IDs are for the originating attribute */ + + /* Now find the target attribute */ + target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID + 1); + if (!target_attr) { + ldb_asprintf_errstring(module->ldb, + "attribute %s does not have valid link target", req->op.add.message->elements[i].name); + return LDB_ERR_OBJECT_CLASS_VIOLATION; + } + + /* Prepare the modify (add element) on the targets */ + + /* For each value being added, we need to setup the modify */ + for (j=0; j < el->num_values; j++) { + struct ldb_request *new_req; + /* Create the modify request */ + struct ldb_message *new_msg = ldb_msg_new(ac->down_req); + if (!new_msg) { + ldb_oom(module->ldb); + return LDB_ERR_OPERATIONS_ERROR; + } + new_msg->dn = ldb_dn_new(new_msg, module->ldb, (char *)el->values[j].data); + if (!new_msg->dn) { + ldb_asprintf_errstring(module->ldb, + "attribute %s value %s was not a valid DN", req->op.add.message->elements[i].name, + el->values[j].data); + return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX; + } + + ret = ldb_msg_add_empty(new_msg, target_attr->lDAPDisplayName, + LDB_FLAG_MOD_ADD, NULL); + if (ret != LDB_SUCCESS) { + return ret; + } + + ret = ldb_msg_add_string(new_msg, target_attr->lDAPDisplayName, + ldb_dn_get_linearized(ac->orig_req->op.add.message->dn)); + if (ret != LDB_SUCCESS) { + return ret; + } + + ret = ldb_build_mod_req(&new_req, module->ldb, ac->down_req, + new_msg, + NULL, + NULL, + NULL); + if (ret != LDB_SUCCESS) { + return ret; + } + + talloc_steal(new_req, new_msg); + + ldb_set_timeout_from_prev_req(module->ldb, req, new_req); + + /* Now add it to the list */ + ac->down_req = talloc_realloc(ac, ac->down_req, + struct ldb_request *, ac->num_requests + 1); + if (!ac->down_req) { + ldb_oom(ac->module->ldb); + return LDB_ERR_OPERATIONS_ERROR; + } + ac->down_req[ac->num_requests] = new_req; + ac->num_requests++; + + /* Run the new request */ + ret = ldb_next_request(module, new_req); + if (ret != LDB_SUCCESS) { + return ret; + } + } + } + return ret; +} + +/* modify */ +static int linked_attributes_modify(struct ldb_module *module, struct ldb_request *req) +{ + return ldb_next_request(module, req); +} + +/* delete */ +static int linked_attributes_delete(struct ldb_module *module, struct ldb_request *req) +{ + return ldb_next_request(module, req); +} + +/* rename */ +static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req) +{ + return ldb_next_request(module, req); +} + +static int linked_attributes_wait_none(struct ldb_handle *handle) { + struct linked_attributes_context *ac; + int i, ret = LDB_ERR_OPERATIONS_ERROR; + if (!handle || !handle->private_data) { + return LDB_ERR_OPERATIONS_ERROR; + } + + if (handle->state == LDB_ASYNC_DONE) { + return handle->status; + } + + handle->state = LDB_ASYNC_PENDING; + handle->status = LDB_SUCCESS; + + ac = talloc_get_type(handle->private_data, struct linked_attributes_context); + + for (i=0; i < ac->num_requests; i++) { + ret = ldb_wait(ac->down_req[i]->handle, LDB_WAIT_NONE); + + if (ret != LDB_SUCCESS) { + handle->status = ret; + goto done; + } + if (ac->down_req[i]->handle->status != LDB_SUCCESS) { + handle->status = ac->down_req[i]->handle->status; + goto done; + } + + if (ac->down_req[i]->handle->state != LDB_ASYNC_DONE) { + return LDB_SUCCESS; + } + } + +done: + handle->state = LDB_ASYNC_DONE; + return ret; + +} + +static int linked_attributes_wait_all(struct ldb_handle *handle) { + + int ret; + + while (handle->state != LDB_ASYNC_DONE) { + ret = linked_attributes_wait_none(handle); + if (ret != LDB_SUCCESS) { + return ret; + } + } + + return handle->status; +} + +static int linked_attributes_wait(struct ldb_handle *handle, enum ldb_wait_type type) +{ + if (type == LDB_WAIT_ALL) { + return linked_attributes_wait_all(handle); + } else { + return linked_attributes_wait_none(handle); + } +} + +static const struct ldb_module_ops linked_attributes_ops = { + .name = "linked_attributes", + .add = linked_attributes_add, + .modify = linked_attributes_modify, + .del = linked_attributes_delete, + .rename = linked_attributes_rename, + .wait = linked_attributes_wait, +}; + +int ldb_linked_attributes_init(void) +{ + return ldb_register_module(&linked_attributes_ops); +} -- cgit