summaryrefslogtreecommitdiff
path: root/ldb/ldb_map
diff options
context:
space:
mode:
authorSimo Sorce <idra@samba.org>2008-09-05 10:13:10 -0400
committerSimo Sorce <idra@samba.org>2008-09-05 10:13:10 -0400
commit23b61265b85f321ae2ceddc6c75771f8881566d8 (patch)
treebb43d44fb09247c8e12d58371e56af911a7c0db9 /ldb/ldb_map
downloadsssd-23b61265b85f321ae2ceddc6c75771f8881566d8.tar.gz
sssd-23b61265b85f321ae2ceddc6c75771f8881566d8.tar.bz2
sssd-23b61265b85f321ae2ceddc6c75771f8881566d8.zip
Initial Import
Diffstat (limited to 'ldb/ldb_map')
-rw-r--r--ldb/ldb_map/ldb_map.c1380
-rw-r--r--ldb/ldb_map/ldb_map.h173
-rw-r--r--ldb/ldb_map/ldb_map_inbound.c723
-rw-r--r--ldb/ldb_map/ldb_map_outbound.c1289
-rw-r--r--ldb/ldb_map/ldb_map_private.h110
5 files changed, 3675 insertions, 0 deletions
diff --git a/ldb/ldb_map/ldb_map.c b/ldb/ldb_map/ldb_map.c
new file mode 100644
index 00000000..b3f639dc
--- /dev/null
+++ b/ldb/ldb_map/ldb_map.c
@@ -0,0 +1,1380 @@
+/*
+ ldb database mapping module
+
+ Copyright (C) Jelmer Vernooij 2005
+ Copyright (C) Martin Kuehl <mkhl@samba.org> 2006
+
+ ** 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
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb ldb_map module
+ *
+ * Description: Map portions of data into a different format on a
+ * remote partition.
+ *
+ * Author: Jelmer Vernooij, Martin Kuehl
+ */
+
+#include "ldb_includes.h"
+
+#include "ldb_map.h"
+#include "ldb_map_private.h"
+
+#ifndef _PUBLIC_
+#define _PUBLIC_
+#endif
+
+/* Description of the provided ldb requests:
+ - special attribute 'isMapped'
+
+ - search:
+ - if parse tree can be split
+ - search remote records w/ remote attrs and parse tree
+ - otherwise
+ - enumerate all remote records
+ - for each remote result
+ - map remote result to local message
+ - search local result
+ - is present
+ - merge local into remote result
+ - run callback on merged result
+ - otherwise
+ - run callback on remote result
+
+ - add:
+ - split message into local and remote part
+ - if local message is not empty
+ - add isMapped to local message
+ - add local message
+ - add remote message
+
+ - modify:
+ - split message into local and remote part
+ - if local message is not empty
+ - add isMapped to local message
+ - search for local record
+ - if present
+ - modify local record
+ - otherwise
+ - add local message
+ - modify remote record
+
+ - delete:
+ - search for local record
+ - if present
+ - delete local record
+ - delete remote record
+
+ - rename:
+ - search for local record
+ - if present
+ - rename local record
+ - modify local isMapped
+ - rename remote record
+*/
+
+
+
+/* Private data structures
+ * ======================= */
+
+/* Global private data */
+/* Extract mappings from private data. */
+const struct ldb_map_context *map_get_context(struct ldb_module *module)
+{
+ const struct map_private *data = talloc_get_type(module->private_data, struct map_private);
+ return data->context;
+}
+
+/* Create a generic request context. */
+static struct map_context *map_init_context(struct ldb_handle *h, struct ldb_request *req)
+{
+ struct map_context *ac;
+
+ ac = talloc_zero(h, struct map_context);
+ if (ac == NULL) {
+ map_oom(h->module);
+ return NULL;
+ }
+
+ ac->module = h->module;
+ ac->orig_req = req;
+
+ return ac;
+}
+
+/* Create a search request context. */
+struct map_search_context *map_init_search_context(struct map_context *ac, struct ldb_reply *ares)
+{
+ struct map_search_context *sc;
+
+ sc = talloc_zero(ac, struct map_search_context);
+ if (sc == NULL) {
+ map_oom(ac->module);
+ return NULL;
+ }
+
+ sc->ac = ac;
+ sc->local_res = NULL;
+ sc->remote_res = ares;
+
+ return sc;
+}
+
+/* Create a request context and handle. */
+struct ldb_handle *map_init_handle(struct ldb_request *req, struct ldb_module *module)
+{
+ struct map_context *ac;
+ struct ldb_handle *h;
+
+ h = talloc_zero(req, struct ldb_handle);
+ if (h == NULL) {
+ map_oom(module);
+ return NULL;
+ }
+
+ h->module = module;
+
+ ac = map_init_context(h, req);
+ if (ac == NULL) {
+ talloc_free(h);
+ return NULL;
+ }
+
+ h->private_data = (void *)ac;
+
+ h->state = LDB_ASYNC_INIT;
+ h->status = LDB_SUCCESS;
+
+ return h;
+}
+
+
+/* Dealing with DNs for different partitions
+ * ========================================= */
+
+/* Check whether any data should be stored in the local partition. */
+bool map_check_local_db(struct ldb_module *module)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+
+ if (!data->remote_base_dn || !data->local_base_dn) {
+ return false;
+ }
+
+ return true;
+}
+
+/* Copy a DN with the base DN of the local partition. */
+static struct ldb_dn *ldb_dn_rebase_local(void *mem_ctx, const struct ldb_map_context *data, struct ldb_dn *dn)
+{
+ struct ldb_dn *new_dn;
+
+ new_dn = ldb_dn_copy(mem_ctx, dn);
+ if ( ! ldb_dn_validate(new_dn)) {
+ talloc_free(new_dn);
+ return NULL;
+ }
+
+ /* may be we don't need to rebase at all */
+ if ( ! data->remote_base_dn || ! data->local_base_dn) {
+ return new_dn;
+ }
+
+ if ( ! ldb_dn_remove_base_components(new_dn, ldb_dn_get_comp_num(data->remote_base_dn))) {
+ talloc_free(new_dn);
+ return NULL;
+ }
+
+ if ( ! ldb_dn_add_base(new_dn, data->local_base_dn)) {
+ talloc_free(new_dn);
+ return NULL;
+ }
+
+ return new_dn;
+}
+
+/* Copy a DN with the base DN of the remote partition. */
+static struct ldb_dn *ldb_dn_rebase_remote(void *mem_ctx, const struct ldb_map_context *data, struct ldb_dn *dn)
+{
+ struct ldb_dn *new_dn;
+
+ new_dn = ldb_dn_copy(mem_ctx, dn);
+ if ( ! ldb_dn_validate(new_dn)) {
+ talloc_free(new_dn);
+ return NULL;
+ }
+
+ /* may be we don't need to rebase at all */
+ if ( ! data->remote_base_dn || ! data->local_base_dn) {
+ return new_dn;
+ }
+
+ if ( ! ldb_dn_remove_base_components(new_dn, ldb_dn_get_comp_num(data->local_base_dn))) {
+ talloc_free(new_dn);
+ return NULL;
+ }
+
+ if ( ! ldb_dn_add_base(new_dn, data->remote_base_dn)) {
+ talloc_free(new_dn);
+ return NULL;
+ }
+
+ return new_dn;
+}
+
+/* Run a request and make sure it targets the remote partition. */
+/* TODO: free old DNs and messages? */
+int ldb_next_remote_request(struct ldb_module *module, struct ldb_request *request)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+ struct ldb_message *msg;
+
+ switch (request->operation) {
+ case LDB_SEARCH:
+ if (request->op.search.base) {
+ request->op.search.base = ldb_dn_rebase_remote(request, data, request->op.search.base);
+ } else {
+ request->op.search.base = data->remote_base_dn;
+ /* TODO: adjust scope? */
+ }
+ break;
+
+ case LDB_ADD:
+ msg = ldb_msg_copy_shallow(request, request->op.add.message);
+ msg->dn = ldb_dn_rebase_remote(msg, data, msg->dn);
+ request->op.add.message = msg;
+ break;
+
+ case LDB_MODIFY:
+ msg = ldb_msg_copy_shallow(request, request->op.mod.message);
+ msg->dn = ldb_dn_rebase_remote(msg, data, msg->dn);
+ request->op.mod.message = msg;
+ break;
+
+ case LDB_DELETE:
+ request->op.del.dn = ldb_dn_rebase_remote(request, data, request->op.del.dn);
+ break;
+
+ case LDB_RENAME:
+ request->op.rename.olddn = ldb_dn_rebase_remote(request, data, request->op.rename.olddn);
+ request->op.rename.newdn = ldb_dn_rebase_remote(request, data, request->op.rename.newdn);
+ break;
+
+ default:
+ ldb_debug(module->ldb, LDB_DEBUG_ERROR, "ldb_map: "
+ "Invalid remote request!\n");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ return ldb_next_request(module, request);
+}
+
+
+/* Finding mappings for attributes and objectClasses
+ * ================================================= */
+
+/* Find an objectClass mapping by the local name. */
+static const struct ldb_map_objectclass *map_objectclass_find_local(const struct ldb_map_context *data, const char *name)
+{
+ int i;
+
+ for (i = 0; data->objectclass_maps && data->objectclass_maps[i].local_name; i++) {
+ if (ldb_attr_cmp(data->objectclass_maps[i].local_name, name) == 0) {
+ return &data->objectclass_maps[i];
+ }
+ }
+
+ return NULL;
+}
+
+/* Find an objectClass mapping by the remote name. */
+static const struct ldb_map_objectclass *map_objectclass_find_remote(const struct ldb_map_context *data, const char *name)
+{
+ int i;
+
+ for (i = 0; data->objectclass_maps && data->objectclass_maps[i].remote_name; i++) {
+ if (ldb_attr_cmp(data->objectclass_maps[i].remote_name, name) == 0) {
+ return &data->objectclass_maps[i];
+ }
+ }
+
+ return NULL;
+}
+
+/* Find an attribute mapping by the local name. */
+const struct ldb_map_attribute *map_attr_find_local(const struct ldb_map_context *data, const char *name)
+{
+ int i;
+
+ for (i = 0; data->attribute_maps[i].local_name; i++) {
+ if (ldb_attr_cmp(data->attribute_maps[i].local_name, name) == 0) {
+ return &data->attribute_maps[i];
+ }
+ }
+ for (i = 0; data->attribute_maps[i].local_name; i++) {
+ if (ldb_attr_cmp(data->attribute_maps[i].local_name, "*") == 0) {
+ return &data->attribute_maps[i];
+ }
+ }
+
+ return NULL;
+}
+
+/* Find an attribute mapping by the remote name. */
+const struct ldb_map_attribute *map_attr_find_remote(const struct ldb_map_context *data, const char *name)
+{
+ const struct ldb_map_attribute *map;
+ const struct ldb_map_attribute *wildcard = NULL;
+ int i, j;
+
+ for (i = 0; data->attribute_maps[i].local_name; i++) {
+ map = &data->attribute_maps[i];
+ if (ldb_attr_cmp(map->local_name, "*") == 0) {
+ wildcard = &data->attribute_maps[i];
+ }
+
+ switch (map->type) {
+ case MAP_IGNORE:
+ break;
+
+ case MAP_KEEP:
+ if (ldb_attr_cmp(map->local_name, name) == 0) {
+ return map;
+ }
+ break;
+
+ case MAP_RENAME:
+ case MAP_CONVERT:
+ if (ldb_attr_cmp(map->u.rename.remote_name, name) == 0) {
+ return map;
+ }
+ break;
+
+ case MAP_GENERATE:
+ for (j = 0; map->u.generate.remote_names && map->u.generate.remote_names[j]; j++) {
+ if (ldb_attr_cmp(map->u.generate.remote_names[j], name) == 0) {
+ return map;
+ }
+ }
+ break;
+ }
+ }
+
+ /* We didn't find it, so return the wildcard record if one was configured */
+ return wildcard;
+}
+
+
+/* Mapping attributes
+ * ================== */
+
+/* Check whether an attribute will be mapped into the remote partition. */
+bool map_attr_check_remote(const struct ldb_map_context *data, const char *attr)
+{
+ const struct ldb_map_attribute *map = map_attr_find_local(data, attr);
+
+ if (map == NULL) {
+ return false;
+ }
+ if (map->type == MAP_IGNORE) {
+ return false;
+ }
+
+ return true;
+}
+
+/* Map an attribute name into the remote partition. */
+const char *map_attr_map_local(void *mem_ctx, const struct ldb_map_attribute *map, const char *attr)
+{
+ if (map == NULL) {
+ return talloc_strdup(mem_ctx, attr);
+ }
+
+ switch (map->type) {
+ case MAP_KEEP:
+ return talloc_strdup(mem_ctx, attr);
+
+ case MAP_RENAME:
+ case MAP_CONVERT:
+ return talloc_strdup(mem_ctx, map->u.rename.remote_name);
+
+ default:
+ return NULL;
+ }
+}
+
+/* Map an attribute name back into the local partition. */
+const char *map_attr_map_remote(void *mem_ctx, const struct ldb_map_attribute *map, const char *attr)
+{
+ if (map == NULL) {
+ return talloc_strdup(mem_ctx, attr);
+ }
+
+ if (map->type == MAP_KEEP) {
+ return talloc_strdup(mem_ctx, attr);
+ }
+
+ return talloc_strdup(mem_ctx, map->local_name);
+}
+
+
+/* Merge two lists of attributes into a single one. */
+int map_attrs_merge(struct ldb_module *module, void *mem_ctx,
+ const char ***attrs, const char * const *more_attrs)
+{
+ int i, j, k;
+
+ for (i = 0; *attrs && (*attrs)[i]; i++) /* noop */ ;
+ for (j = 0; more_attrs && more_attrs[j]; j++) /* noop */ ;
+
+ *attrs = talloc_realloc(mem_ctx, *attrs, const char *, i+j+1);
+ if (*attrs == NULL) {
+ map_oom(module);
+ return -1;
+ }
+
+ for (k = 0; k < j; k++) {
+ (*attrs)[i + k] = more_attrs[k];
+ }
+
+ (*attrs)[i+k] = NULL;
+
+ return 0;
+}
+
+/* Mapping ldb values
+ * ================== */
+
+/* Map an ldb value into the remote partition. */
+struct ldb_val ldb_val_map_local(struct ldb_module *module, void *mem_ctx,
+ const struct ldb_map_attribute *map, const struct ldb_val *val)
+{
+ if (map && (map->type == MAP_CONVERT) && (map->u.convert.convert_local)) {
+ return map->u.convert.convert_local(module, mem_ctx, val);
+ }
+
+ return ldb_val_dup(mem_ctx, val);
+}
+
+/* Map an ldb value back into the local partition. */
+struct ldb_val ldb_val_map_remote(struct ldb_module *module, void *mem_ctx,
+ const struct ldb_map_attribute *map, const struct ldb_val *val)
+{
+ if (map && (map->type == MAP_CONVERT) && (map->u.convert.convert_remote)) {
+ return map->u.convert.convert_remote(module, mem_ctx, val);
+ }
+
+ return ldb_val_dup(mem_ctx, val);
+}
+
+
+/* Mapping DNs
+ * =========== */
+
+/* Check whether a DN is below the local baseDN. */
+bool ldb_dn_check_local(struct ldb_module *module, struct ldb_dn *dn)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+
+ if (!data->local_base_dn) {
+ return true;
+ }
+
+ return ldb_dn_compare_base(data->local_base_dn, dn) == 0;
+}
+
+/* Map a DN into the remote partition. */
+struct ldb_dn *ldb_dn_map_local(struct ldb_module *module, void *mem_ctx, struct ldb_dn *dn)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+ struct ldb_dn *newdn;
+ const struct ldb_map_attribute *map;
+ enum ldb_map_attr_type map_type;
+ const char *name;
+ struct ldb_val value;
+ int i, ret;
+
+ if (dn == NULL) {
+ return NULL;
+ }
+
+ newdn = ldb_dn_copy(mem_ctx, dn);
+ if (newdn == NULL) {
+ map_oom(module);
+ return NULL;
+ }
+
+ /* For each RDN, map the component name and possibly the value */
+ for (i = 0; i < ldb_dn_get_comp_num(newdn); i++) {
+ map = map_attr_find_local(data, ldb_dn_get_component_name(dn, i));
+
+ /* Unknown attribute - leave this RDN as is and hope the best... */
+ if (map == NULL) {
+ map_type = MAP_KEEP;
+ } else {
+ map_type = map->type;
+ }
+
+ switch (map_type) {
+ case MAP_IGNORE:
+ case MAP_GENERATE:
+ ldb_debug(module->ldb, LDB_DEBUG_ERROR, "ldb_map: "
+ "MAP_IGNORE/MAP_GENERATE attribute '%s' "
+ "used in DN!\n", ldb_dn_get_component_name(dn, i));
+ goto failed;
+
+ case MAP_CONVERT:
+ if (map->u.convert.convert_local == NULL) {
+ ldb_debug(module->ldb, LDB_DEBUG_ERROR, "ldb_map: "
+ "'convert_local' not set for attribute '%s' "
+ "used in DN!\n", ldb_dn_get_component_name(dn, i));
+ goto failed;
+ }
+ /* fall through */
+ case MAP_KEEP:
+ case MAP_RENAME:
+ name = map_attr_map_local(newdn, map, ldb_dn_get_component_name(dn, i));
+ if (name == NULL) goto failed;
+
+ value = ldb_val_map_local(module, newdn, map, ldb_dn_get_component_val(dn, i));
+ if (value.data == NULL) goto failed;
+
+ ret = ldb_dn_set_component(newdn, i, name, value);
+ if (ret != LDB_SUCCESS) {
+ goto failed;
+ }
+
+ break;
+ }
+ }
+
+ return newdn;
+
+failed:
+ talloc_free(newdn);
+ return NULL;
+}
+
+/* Map a DN into the local partition. */
+struct ldb_dn *ldb_dn_map_remote(struct ldb_module *module, void *mem_ctx, struct ldb_dn *dn)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+ struct ldb_dn *newdn;
+ const struct ldb_map_attribute *map;
+ enum ldb_map_attr_type map_type;
+ const char *name;
+ struct ldb_val value;
+ int i, ret;
+
+ if (dn == NULL) {
+ return NULL;
+ }
+
+ newdn = ldb_dn_copy(mem_ctx, dn);
+ if (newdn == NULL) {
+ map_oom(module);
+ return NULL;
+ }
+
+ /* For each RDN, map the component name and possibly the value */
+ for (i = 0; i < ldb_dn_get_comp_num(newdn); i++) {
+ map = map_attr_find_remote(data, ldb_dn_get_component_name(dn, i));
+
+ /* Unknown attribute - leave this RDN as is and hope the best... */
+ if (map == NULL) {
+ map_type = MAP_KEEP;
+ } else {
+ map_type = map->type;
+ }
+
+ switch (map_type) {
+ case MAP_IGNORE:
+ case MAP_GENERATE:
+ ldb_debug(module->ldb, LDB_DEBUG_ERROR, "ldb_map: "
+ "MAP_IGNORE/MAP_GENERATE attribute '%s' "
+ "used in DN!\n", ldb_dn_get_component_name(dn, i));
+ goto failed;
+
+ case MAP_CONVERT:
+ if (map->u.convert.convert_remote == NULL) {
+ ldb_debug(module->ldb, LDB_DEBUG_ERROR, "ldb_map: "
+ "'convert_remote' not set for attribute '%s' "
+ "used in DN!\n", ldb_dn_get_component_name(dn, i));
+ goto failed;
+ }
+ /* fall through */
+ case MAP_KEEP:
+ case MAP_RENAME:
+ name = map_attr_map_remote(newdn, map, ldb_dn_get_component_name(dn, i));
+ if (name == NULL) goto failed;
+
+ value = ldb_val_map_remote(module, newdn, map, ldb_dn_get_component_val(dn, i));
+ if (value.data == NULL) goto failed;
+
+ ret = ldb_dn_set_component(newdn, i, name, value);
+ if (ret != LDB_SUCCESS) {
+ goto failed;
+ }
+
+ break;
+ }
+ }
+
+ return newdn;
+
+failed:
+ talloc_free(newdn);
+ return NULL;
+}
+
+/* Map a DN and its base into the local partition. */
+/* TODO: This should not be required with GUIDs. */
+struct ldb_dn *ldb_dn_map_rebase_remote(struct ldb_module *module, void *mem_ctx, struct ldb_dn *dn)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+ struct ldb_dn *dn1, *dn2;
+
+ dn1 = ldb_dn_rebase_local(mem_ctx, data, dn);
+ dn2 = ldb_dn_map_remote(module, mem_ctx, dn1);
+
+ talloc_free(dn1);
+ return dn2;
+}
+
+
+/* Converting DNs and objectClasses (as ldb values)
+ * ================================================ */
+
+/* Map a DN contained in an ldb value into the remote partition. */
+static struct ldb_val ldb_dn_convert_local(struct ldb_module *module, void *mem_ctx, const struct ldb_val *val)
+{
+ struct ldb_dn *dn, *newdn;
+ struct ldb_val newval;
+
+ dn = ldb_dn_new(mem_ctx, module->ldb, (char *)val->data);
+ if (! ldb_dn_validate(dn)) {
+ newval.length = 0;
+ newval.data = NULL;
+ talloc_free(dn);
+ return newval;
+ }
+ newdn = ldb_dn_map_local(module, mem_ctx, dn);
+ talloc_free(dn);
+
+ newval.length = 0;
+ newval.data = (uint8_t *)ldb_dn_alloc_linearized(mem_ctx, newdn);
+ if (newval.data) {
+ newval.length = strlen((char *)newval.data);
+ }
+ talloc_free(newdn);
+
+ return newval;
+}
+
+/* Map a DN contained in an ldb value into the local partition. */
+static struct ldb_val ldb_dn_convert_remote(struct ldb_module *module, void *mem_ctx, const struct ldb_val *val)
+{
+ struct ldb_dn *dn, *newdn;
+ struct ldb_val newval;
+
+ dn = ldb_dn_new(mem_ctx, module->ldb, (char *)val->data);
+ if (! ldb_dn_validate(dn)) {
+ newval.length = 0;
+ newval.data = NULL;
+ talloc_free(dn);
+ return newval;
+ }
+ newdn = ldb_dn_map_remote(module, mem_ctx, dn);
+ talloc_free(dn);
+
+ newval.length = 0;
+ newval.data = (uint8_t *)ldb_dn_alloc_linearized(mem_ctx, newdn);
+ if (newval.data) {
+ newval.length = strlen((char *)newval.data);
+ }
+ talloc_free(newdn);
+
+ return newval;
+}
+
+/* Map an objectClass into the remote partition. */
+static struct ldb_val map_objectclass_convert_local(struct ldb_module *module, void *mem_ctx, const struct ldb_val *val)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+ const char *name = (char *)val->data;
+ const struct ldb_map_objectclass *map = map_objectclass_find_local(data, name);
+ struct ldb_val newval;
+
+ if (map) {
+ newval.data = (uint8_t*)talloc_strdup(mem_ctx, map->remote_name);
+ newval.length = strlen((char *)newval.data);
+ return newval;
+ }
+
+ return ldb_val_dup(mem_ctx, val);
+}
+
+/* Generate a remote message with a mapped objectClass. */
+static void map_objectclass_generate_remote(struct ldb_module *module, const char *local_attr, const struct ldb_message *old, struct ldb_message *remote, struct ldb_message *local)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+ struct ldb_message_element *el, *oc;
+ struct ldb_val val;
+ bool found_extensibleObject = false;
+ int i;
+
+ /* Find old local objectClass */
+ oc = ldb_msg_find_element(old, "objectClass");
+ if (oc == NULL) {
+ return;
+ }
+
+ /* Prepare new element */
+ el = talloc_zero(remote, struct ldb_message_element);
+ if (el == NULL) {
+ ldb_oom(module->ldb);
+ return; /* TODO: fail? */
+ }
+
+ /* Copy local objectClass element, reverse space for an extra value */
+ el->num_values = oc->num_values + 1;
+ el->values = talloc_array(el, struct ldb_val, el->num_values);
+ if (el->values == NULL) {
+ talloc_free(el);
+ ldb_oom(module->ldb);
+ return; /* TODO: fail? */
+ }
+
+ /* Copy local element name "objectClass" */
+ el->name = talloc_strdup(el, local_attr);
+
+ /* Convert all local objectClasses */
+ for (i = 0; i < el->num_values - 1; i++) {
+ el->values[i] = map_objectclass_convert_local(module, el->values, &oc->values[i]);
+ if (ldb_attr_cmp((char *)el->values[i].data, data->add_objectclass) == 0) {
+ found_extensibleObject = true;
+ }
+ }
+
+ if (!found_extensibleObject) {
+ val.data = (uint8_t *)talloc_strdup(el->values, data->add_objectclass);
+ val.length = strlen((char *)val.data);
+
+ /* Append additional objectClass data->add_objectclass */
+ el->values[i] = val;
+ } else {
+ el->num_values--;
+ }
+
+ /* Add new objectClass to remote message */
+ ldb_msg_add(remote, el, 0);
+}
+
+/* Map an objectClass into the local partition. */
+static struct ldb_val map_objectclass_convert_remote(struct ldb_module *module, void *mem_ctx, const struct ldb_val *val)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+ const char *name = (char *)val->data;
+ const struct ldb_map_objectclass *map = map_objectclass_find_remote(data, name);
+ struct ldb_val newval;
+
+ if (map) {
+ newval.data = (uint8_t*)talloc_strdup(mem_ctx, map->local_name);
+ newval.length = strlen((char *)newval.data);
+ return newval;
+ }
+
+ return ldb_val_dup(mem_ctx, val);
+}
+
+/* Generate a local message with a mapped objectClass. */
+static struct ldb_message_element *map_objectclass_generate_local(struct ldb_module *module, void *mem_ctx, const char *local_attr, const struct ldb_message *remote)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+ struct ldb_message_element *el, *oc;
+ struct ldb_val val;
+ int i;
+
+ /* Find old remote objectClass */
+ oc = ldb_msg_find_element(remote, "objectClass");
+ if (oc == NULL) {
+ return NULL;
+ }
+
+ /* Prepare new element */
+ el = talloc_zero(mem_ctx, struct ldb_message_element);
+ if (el == NULL) {
+ ldb_oom(module->ldb);
+ return NULL;
+ }
+
+ /* Copy remote objectClass element */
+ el->num_values = oc->num_values;
+ el->values = talloc_array(el, struct ldb_val, el->num_values);
+ if (el->values == NULL) {
+ talloc_free(el);
+ ldb_oom(module->ldb);
+ return NULL;
+ }
+
+ /* Copy remote element name "objectClass" */
+ el->name = talloc_strdup(el, local_attr);
+
+ /* Convert all remote objectClasses */
+ for (i = 0; i < el->num_values; i++) {
+ el->values[i] = map_objectclass_convert_remote(module, el->values, &oc->values[i]);
+ }
+
+ val.data = (uint8_t *)talloc_strdup(el->values, data->add_objectclass);
+ val.length = strlen((char *)val.data);
+
+ /* Remove last value if it was the string in data->add_objectclass (eg samba4top, extensibleObject) */
+ if (ldb_val_equal_exact(&val, &el->values[i-1])) {
+ el->num_values--;
+ el->values = talloc_realloc(el, el->values, struct ldb_val, el->num_values);
+ if (el->values == NULL) {
+ talloc_free(el);
+ ldb_oom(module->ldb);
+ return NULL;
+ }
+ }
+
+ return el;
+}
+
+static const struct ldb_map_attribute objectclass_convert_map = {
+ .local_name = "objectClass",
+ .type = MAP_CONVERT,
+ .u = {
+ .convert = {
+ .remote_name = "objectClass",
+ .convert_local = map_objectclass_convert_local,
+ .convert_remote = map_objectclass_convert_remote,
+ },
+ },
+};
+
+
+/* Mappings for searches on objectClass= assuming a one-to-one
+ * mapping. Needed because this is a generate operator for the
+ * add/modify code */
+static int map_objectclass_convert_operator(struct ldb_module *module, void *mem_ctx,
+ struct ldb_parse_tree **new, const struct ldb_parse_tree *tree)
+{
+
+ return map_subtree_collect_remote_simple(module, mem_ctx, new, tree, &objectclass_convert_map);
+}
+
+/* Auxiliary request construction
+ * ============================== */
+
+/* Store the DN of a single search result in context. */
+static int map_search_self_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
+{
+ struct map_context *ac;
+
+ if (context == NULL || ares == NULL) {
+ ldb_set_errstring(ldb, talloc_asprintf(ldb, "NULL Context or Result in callback"));
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac = talloc_get_type(context, struct map_context);
+
+ /* We are interested only in the single reply */
+ if (ares->type != LDB_REPLY_ENTRY) {
+ talloc_free(ares);
+ return LDB_SUCCESS;
+ }
+
+ /* We have already found a remote DN */
+ if (ac->local_dn) {
+ ldb_set_errstring(ldb, talloc_asprintf(ldb, "Too many results to base search"));
+ talloc_free(ares);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Store local DN */
+ ac->local_dn = ares->message->dn;
+
+ return LDB_SUCCESS;
+}
+
+/* Build a request to search a record by its DN. */
+struct ldb_request *map_search_base_req(struct map_context *ac, struct ldb_dn *dn, const char * const *attrs, const struct ldb_parse_tree *tree, void *context, ldb_search_callback callback)
+{
+ struct ldb_request *req;
+
+ req = talloc_zero(ac, struct ldb_request);
+ if (req == NULL) {
+ map_oom(ac->module);
+ return NULL;
+ }
+
+ req->operation = LDB_SEARCH;
+ req->op.search.base = dn;
+ req->op.search.scope = LDB_SCOPE_BASE;
+ req->op.search.attrs = attrs;
+
+ if (tree) {
+ req->op.search.tree = tree;
+ } else {
+ req->op.search.tree = ldb_parse_tree(req, NULL);
+ if (req->op.search.tree == NULL) {
+ talloc_free(req);
+ return NULL;
+ }
+ }
+
+ req->controls = NULL;
+ req->context = context;
+ req->callback = callback;
+ ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, req);
+
+ return req;
+}
+
+/* Build a request to search the local record by its DN. */
+struct ldb_request *map_search_self_req(struct map_context *ac, struct ldb_dn *dn)
+{
+ /* attrs[] is returned from this function in
+ * ac->search_req->op.search.attrs, so it must be static, as
+ * otherwise the compiler can put it on the stack */
+ static const char * const attrs[] = { IS_MAPPED, NULL };
+ struct ldb_parse_tree *tree;
+
+ /* Limit search to records with 'IS_MAPPED' present */
+ /* TODO: `tree = ldb_parse_tree(ac, IS_MAPPED);' won't do. */
+ tree = talloc_zero(ac, struct ldb_parse_tree);
+ if (tree == NULL) {
+ map_oom(ac->module);
+ return NULL;
+ }
+
+ tree->operation = LDB_OP_PRESENT;
+ tree->u.present.attr = talloc_strdup(tree, IS_MAPPED);
+
+ return map_search_base_req(ac, dn, attrs, tree, ac, map_search_self_callback);
+}
+
+/* Build a request to update the 'IS_MAPPED' attribute */
+struct ldb_request *map_build_fixup_req(struct map_context *ac, struct ldb_dn *olddn, struct ldb_dn *newdn)
+{
+ struct ldb_request *req;
+ struct ldb_message *msg;
+ const char *dn;
+
+ /* Prepare request */
+ req = talloc_zero(ac, struct ldb_request);
+ if (req == NULL) {
+ map_oom(ac->module);
+ return NULL;
+ }
+
+ /* Prepare message */
+ msg = ldb_msg_new(req);
+ if (msg == NULL) {
+ map_oom(ac->module);
+ goto failed;
+ }
+
+ /* Update local 'IS_MAPPED' to the new remote DN */
+ msg->dn = ldb_dn_copy(msg, olddn);
+ dn = ldb_dn_alloc_linearized(msg, newdn);
+ if ( ! dn || ! ldb_dn_validate(msg->dn)) {
+ goto failed;
+ }
+ if (ldb_msg_add_empty(msg, IS_MAPPED, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
+ goto failed;
+ }
+ if (ldb_msg_add_string(msg, IS_MAPPED, dn) != 0) {
+ goto failed;
+ }
+
+ req->operation = LDB_MODIFY;
+ req->op.mod.message = msg;
+ req->controls = NULL;
+ req->handle = NULL;
+ req->context = NULL;
+ req->callback = NULL;
+
+ return req;
+
+failed:
+ talloc_free(req);
+ return NULL;
+}
+
+
+/* Asynchronous call structure
+ * =========================== */
+
+/* Figure out which request is currently pending. */
+static struct ldb_request *map_get_req(struct map_context *ac)
+{
+ switch (ac->step) {
+ case MAP_SEARCH_SELF_MODIFY:
+ case MAP_SEARCH_SELF_DELETE:
+ case MAP_SEARCH_SELF_RENAME:
+ return ac->search_req;
+
+ case MAP_ADD_REMOTE:
+ case MAP_MODIFY_REMOTE:
+ case MAP_DELETE_REMOTE:
+ case MAP_RENAME_REMOTE:
+ return ac->remote_req;
+
+ case MAP_RENAME_FIXUP:
+ return ac->down_req;
+
+ case MAP_ADD_LOCAL:
+ case MAP_MODIFY_LOCAL:
+ case MAP_DELETE_LOCAL:
+ case MAP_RENAME_LOCAL:
+ return ac->local_req;
+
+ case MAP_SEARCH_REMOTE:
+ /* Can't happen */
+ break;
+ }
+
+ return NULL; /* unreachable; silences a warning */
+}
+
+typedef int (*map_next_function)(struct ldb_handle *handle);
+
+/* Figure out the next request to run. */
+static map_next_function map_get_next(struct map_context *ac)
+{
+ switch (ac->step) {
+ case MAP_SEARCH_REMOTE:
+ return NULL;
+
+ case MAP_ADD_LOCAL:
+ return map_add_do_remote;
+ case MAP_ADD_REMOTE:
+ return NULL;
+
+ case MAP_SEARCH_SELF_MODIFY:
+ return map_modify_do_local;
+ case MAP_MODIFY_LOCAL:
+ return map_modify_do_remote;
+ case MAP_MODIFY_REMOTE:
+ return NULL;
+
+ case MAP_SEARCH_SELF_DELETE:
+ return map_delete_do_local;
+ case MAP_DELETE_LOCAL:
+ return map_delete_do_remote;
+ case MAP_DELETE_REMOTE:
+ return NULL;
+
+ case MAP_SEARCH_SELF_RENAME:
+ return map_rename_do_local;
+ case MAP_RENAME_LOCAL:
+ return map_rename_do_fixup;
+ case MAP_RENAME_FIXUP:
+ return map_rename_do_remote;
+ case MAP_RENAME_REMOTE:
+ return NULL;
+ }
+
+ return NULL; /* unreachable; silences a warning */
+}
+
+/* Wait for the current pending request to finish and continue with the next. */
+static int map_wait_next(struct ldb_handle *handle)
+{
+ struct map_context *ac;
+ struct ldb_request *req;
+ map_next_function next;
+ int ret;
+
+ if (handle == NULL || handle->private_data == NULL) {
+ 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 map_context);
+
+ if (ac->step == MAP_SEARCH_REMOTE) {
+ int i;
+ for (i = 0; i < ac->num_searches; i++) {
+ req = ac->search_reqs[i];
+ ret = ldb_wait(req->handle, LDB_WAIT_NONE);
+
+ if (ret != LDB_SUCCESS) {
+ handle->status = ret;
+ goto done;
+ }
+ if (req->handle->status != LDB_SUCCESS) {
+ handle->status = req->handle->status;
+ goto done;
+ }
+ if (req->handle->state != LDB_ASYNC_DONE) {
+ return LDB_SUCCESS;
+ }
+ }
+ } else {
+
+ req = map_get_req(ac);
+
+ ret = ldb_wait(req->handle, LDB_WAIT_NONE);
+
+ if (ret != LDB_SUCCESS) {
+ handle->status = ret;
+ goto done;
+ }
+ if (req->handle->status != LDB_SUCCESS) {
+ handle->status = req->handle->status;
+ goto done;
+ }
+ if (req->handle->state != LDB_ASYNC_DONE) {
+ return LDB_SUCCESS;
+ }
+
+ next = map_get_next(ac);
+ if (next) {
+ return next(handle);
+ }
+ }
+
+ ret = LDB_SUCCESS;
+
+done:
+ handle->state = LDB_ASYNC_DONE;
+ return ret;
+}
+
+/* Wait for all current pending requests to finish. */
+static int map_wait_all(struct ldb_handle *handle)
+{
+ int ret;
+
+ while (handle->state != LDB_ASYNC_DONE) {
+ ret = map_wait_next(handle);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+
+ return handle->status;
+}
+
+/* Wait for pending requests to finish. */
+int map_wait(struct ldb_handle *handle, enum ldb_wait_type type)
+{
+ if (type == LDB_WAIT_ALL) {
+ return map_wait_all(handle);
+ } else {
+ return map_wait_next(handle);
+ }
+}
+
+
+/* Module initialization
+ * ===================== */
+
+
+/* Builtin mappings for DNs and objectClasses */
+static const struct ldb_map_attribute builtin_attribute_maps[] = {
+ {
+ .local_name = "dn",
+ .type = MAP_CONVERT,
+ .u = {
+ .convert = {
+ .remote_name = "dn",
+ .convert_local = ldb_dn_convert_local,
+ .convert_remote = ldb_dn_convert_remote,
+ },
+ },
+ },
+ {
+ .local_name = NULL,
+ }
+};
+
+static const struct ldb_map_attribute objectclass_attribute_map = {
+ .local_name = "objectClass",
+ .type = MAP_GENERATE,
+ .convert_operator = map_objectclass_convert_operator,
+ .u = {
+ .generate = {
+ .remote_names = { "objectClass", NULL },
+ .generate_local = map_objectclass_generate_local,
+ .generate_remote = map_objectclass_generate_remote,
+ },
+ },
+};
+
+
+/* Find the special 'MAP_DN_NAME' record and store local and remote
+ * base DNs in private data. */
+static int map_init_dns(struct ldb_module *module, struct ldb_map_context *data, const char *name)
+{
+ static const char * const attrs[] = { MAP_DN_FROM, MAP_DN_TO, NULL };
+ struct ldb_dn *dn;
+ struct ldb_message *msg;
+ struct ldb_result *res;
+ int ret;
+
+ if (!name) {
+ data->local_base_dn = NULL;
+ data->remote_base_dn = NULL;
+ return LDB_SUCCESS;
+ }
+
+ dn = ldb_dn_new_fmt(data, module->ldb, "%s=%s", MAP_DN_NAME, name);
+ if ( ! ldb_dn_validate(dn)) {
+ ldb_debug(module->ldb, LDB_DEBUG_ERROR, "ldb_map: "
+ "Failed to construct '%s' DN!\n", MAP_DN_NAME);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldb_search(module->ldb, dn, LDB_SCOPE_BASE, NULL, attrs, &res);
+ talloc_free(dn);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ if (res->count == 0) {
+ ldb_debug(module->ldb, LDB_DEBUG_ERROR, "ldb_map: "
+ "No results for '%s=%s'!\n", MAP_DN_NAME, name);
+ talloc_free(res);
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+ if (res->count > 1) {
+ ldb_debug(module->ldb, LDB_DEBUG_ERROR, "ldb_map: "
+ "Too many results for '%s=%s'!\n", MAP_DN_NAME, name);
+ talloc_free(res);
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+
+ msg = res->msgs[0];
+ data->local_base_dn = ldb_msg_find_attr_as_dn(module->ldb, data, msg, MAP_DN_FROM);
+ data->remote_base_dn = ldb_msg_find_attr_as_dn(module->ldb, data, msg, MAP_DN_TO);
+ talloc_free(res);
+
+ return LDB_SUCCESS;
+}
+
+/* Store attribute maps and objectClass maps in private data. */
+static int map_init_maps(struct ldb_module *module, struct ldb_map_context *data,
+ const struct ldb_map_attribute *attrs,
+ const struct ldb_map_objectclass *ocls,
+ const char * const *wildcard_attributes)
+{
+ int i, j, last;
+ last = 0;
+
+ /* Count specified attribute maps */
+ for (i = 0; attrs[i].local_name; i++) /* noop */ ;
+ /* Count built-in attribute maps */
+ for (j = 0; builtin_attribute_maps[j].local_name; j++) /* noop */ ;
+
+ /* Store list of attribute maps */
+ data->attribute_maps = talloc_array(data, struct ldb_map_attribute, i+j+2);
+ if (data->attribute_maps == NULL) {
+ map_oom(module);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Specified ones go first */
+ for (i = 0; attrs[i].local_name; i++) {
+ data->attribute_maps[last] = attrs[i];
+ last++;
+ }
+
+ /* Built-in ones go last */
+ for (i = 0; builtin_attribute_maps[i].local_name; i++) {
+ data->attribute_maps[last] = builtin_attribute_maps[i];
+ last++;
+ }
+
+ if (data->add_objectclass) {
+ /* ObjectClass one is very last, if required */
+ data->attribute_maps[last] = objectclass_attribute_map;
+ last++;
+ } else if (ocls) {
+ data->attribute_maps[last] = objectclass_convert_map;
+ last++;
+ }
+
+ /* Ensure 'local_name == NULL' for the last entry */
+ memset(&data->attribute_maps[last], 0, sizeof(struct ldb_map_attribute));
+
+ /* Store list of objectClass maps */
+ data->objectclass_maps = ocls;
+
+ data->wildcard_attributes = wildcard_attributes;
+
+ return LDB_SUCCESS;
+}
+
+/* Initialize global private data. */
+_PUBLIC_ int ldb_map_init(struct ldb_module *module, const struct ldb_map_attribute *attrs,
+ const struct ldb_map_objectclass *ocls,
+ const char * const *wildcard_attributes,
+ const char *add_objectclass,
+ const char *name)
+{
+ struct map_private *data;
+ int ret;
+
+ /* Prepare private data */
+ data = talloc_zero(module, struct map_private);
+ if (data == NULL) {
+ map_oom(module);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ module->private_data = data;
+
+ data->context = talloc_zero(data, struct ldb_map_context);
+ if (!data->context) {
+ map_oom(module);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Store local and remote baseDNs */
+ ret = map_init_dns(module, data->context, name);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(data);
+ return ret;
+ }
+
+ data->context->add_objectclass = add_objectclass;
+
+ /* Store list of attribute and objectClass maps */
+ ret = map_init_maps(module, data->context, attrs, ocls, wildcard_attributes);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(data);
+ return ret;
+ }
+
+ return LDB_SUCCESS;
+}
diff --git a/ldb/ldb_map/ldb_map.h b/ldb/ldb_map/ldb_map.h
new file mode 100644
index 00000000..e40bb9cd
--- /dev/null
+++ b/ldb/ldb_map/ldb_map.h
@@ -0,0 +1,173 @@
+/*
+ ldb database mapping module
+
+ Copyright (C) Jelmer Vernooij 2005
+ Copyright (C) Martin Kuehl <mkhl@samba.org> 2006
+
+ ** 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
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#ifndef __LDB_MAP_H__
+#define __LDB_MAP_H__
+
+/* ldb_map is a skeleton LDB module that can be used for any other modules
+ * that need to map attributes.
+ *
+ * The term 'remote' in this header refers to the connection where the
+ * original schema is used on while 'local' means the local connection
+ * that any upper layers will use.
+ *
+ * All local attributes will have to have a definition. Not all remote
+ * attributes need a definition as LDB is a lot less strict than LDAP
+ * (in other words, sending unknown attributes to an LDAP server hurts us,
+ * while returning too many attributes in ldb_search() doesn't)
+ */
+
+
+/* Name of the internal attribute pointing from the local to the
+ * remote part of a record */
+#define IS_MAPPED "isMapped"
+
+
+struct ldb_map_context;
+
+/* convert a local ldb_val to a remote ldb_val */
+typedef struct ldb_val (*ldb_map_convert_func) (struct ldb_module *module, void *mem_ctx, const struct ldb_val *val);
+
+#define LDB_MAP_MAX_REMOTE_NAMES 10
+
+/* map from local to remote attribute */
+struct ldb_map_attribute {
+ const char *local_name; /* local name */
+
+ enum ldb_map_attr_type {
+ MAP_IGNORE, /* Ignore this local attribute. Doesn't exist remotely. */
+ MAP_KEEP, /* Keep as is. Same name locally and remotely. */
+ MAP_RENAME, /* Simply rename the attribute. Name changes, data is the same */
+ MAP_CONVERT, /* Rename + convert data */
+ MAP_GENERATE /* Use generate function for generating new name/data.
+ Used for generating attributes based on
+ multiple remote attributes. */
+ } type;
+
+ /* if set, will be called for search expressions that contain this attribute */
+ int (*convert_operator)(struct ldb_module *, TALLOC_CTX *ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *);
+
+ union {
+ struct {
+ const char *remote_name;
+ } rename;
+
+ struct {
+ const char *remote_name;
+
+ /* Convert local to remote data */
+ ldb_map_convert_func convert_local;
+
+ /* Convert remote to local data */
+ /* an entry can have convert_remote set to NULL, as long as there as an entry with the same local_name
+ * that is non-NULL before it. */
+ ldb_map_convert_func convert_remote;
+ } convert;
+
+ struct {
+ /* Generate the local attribute from remote message */
+ struct ldb_message_element *(*generate_local)(struct ldb_module *, TALLOC_CTX *mem_ctx, const char *remote_attr, const struct ldb_message *remote);
+
+ /* Update remote message with information from local message */
+ void (*generate_remote)(struct ldb_module *, const char *local_attr, const struct ldb_message *old, struct ldb_message *remote, struct ldb_message *local);
+
+ /* Name(s) for this attribute on the remote server. This is an array since
+ * one local attribute's data can be split up into several attributes
+ * remotely */
+ const char *remote_names[LDB_MAP_MAX_REMOTE_NAMES];
+
+ /* Names of additional remote attributes
+ * required for the generation. NULL
+ * indicates that `local_attr' suffices. */
+ /*
+#define LDB_MAP_MAX_SELF_ATTRIBUTES 10
+ const char *self_attrs[LDB_MAP_MAX_SELF_ATTRIBUTES];
+ */
+ } generate;
+ } u;
+};
+
+
+#define LDB_MAP_MAX_SUBCLASSES 10
+#define LDB_MAP_MAX_MUSTS 10
+#define LDB_MAP_MAX_MAYS 50
+
+/* map from local to remote objectClass */
+struct ldb_map_objectclass {
+ const char *local_name;
+ const char *remote_name;
+ const char *base_classes[LDB_MAP_MAX_SUBCLASSES];
+ const char *musts[LDB_MAP_MAX_MUSTS];
+ const char *mays[LDB_MAP_MAX_MAYS];
+};
+
+
+/* private context data */
+struct ldb_map_context {
+ struct ldb_map_attribute *attribute_maps;
+ /* NOTE: Always declare base classes first here */
+ const struct ldb_map_objectclass *objectclass_maps;
+
+ /* Remote (often operational) attributes that should be added
+ * to any wildcard search */
+ const char * const *wildcard_attributes;
+
+ /* ObjectClass (if any) to be added to remote attributes on add */
+ const char *add_objectclass;
+
+ /* struct ldb_context *mapped_ldb; */
+ struct ldb_dn *local_base_dn;
+ struct ldb_dn *remote_base_dn;
+};
+
+/* Global private data */
+struct map_private {
+ void *caller_private;
+ struct ldb_map_context *context;
+};
+
+/* Initialize global private data. */
+int ldb_map_init(struct ldb_module *module, const struct ldb_map_attribute *attrs,
+ const struct ldb_map_objectclass *ocls,
+ const char * const *wildcard_attributes,
+ const char *add_objectclass,
+ const char *name);
+
+int map_add(struct ldb_module *module, struct ldb_request *req);
+int map_search(struct ldb_module *module, struct ldb_request *req);
+int map_rename(struct ldb_module *module, struct ldb_request *req);
+int map_delete(struct ldb_module *module, struct ldb_request *req);
+int map_modify(struct ldb_module *module, struct ldb_request *req);
+int map_wait(struct ldb_handle *handle, enum ldb_wait_type type);
+
+#define LDB_MAP_OPS \
+ .add = map_add, \
+ .modify = map_modify, \
+ .del = map_delete, \
+ .rename = map_rename, \
+ .search = map_search, \
+ .wait = map_wait,
+
+#endif /* __LDB_MAP_H__ */
diff --git a/ldb/ldb_map/ldb_map_inbound.c b/ldb/ldb_map/ldb_map_inbound.c
new file mode 100644
index 00000000..7fc3ac4e
--- /dev/null
+++ b/ldb/ldb_map/ldb_map_inbound.c
@@ -0,0 +1,723 @@
+/*
+ ldb database mapping module
+
+ Copyright (C) Jelmer Vernooij 2005
+ Copyright (C) Martin Kuehl <mkhl@samba.org> 2006
+
+ ** 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
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "ldb_includes.h"
+
+#include "ldb_map.h"
+#include "ldb_map_private.h"
+
+
+/* Mapping message elements
+ * ======================== */
+
+/* Map a message element into the remote partition. */
+static struct ldb_message_element *ldb_msg_el_map_local(struct ldb_module *module, void *mem_ctx, const struct ldb_map_attribute *map, const struct ldb_message_element *old)
+{
+ struct ldb_message_element *el;
+ int i;
+
+ el = talloc_zero(mem_ctx, struct ldb_message_element);
+ if (el == NULL) {
+ map_oom(module);
+ return NULL;
+ }
+
+ el->num_values = old->num_values;
+ el->values = talloc_array(el, struct ldb_val, el->num_values);
+ if (el->values == NULL) {
+ talloc_free(el);
+ map_oom(module);
+ return NULL;
+ }
+
+ el->name = map_attr_map_local(el, map, old->name);
+
+ for (i = 0; i < el->num_values; i++) {
+ el->values[i] = ldb_val_map_local(module, el->values, map, &old->values[i]);
+ }
+
+ return el;
+}
+
+/* Add a message element either to a local or to a remote message,
+ * depending on whether it goes into the local or remote partition. */
+static int ldb_msg_el_partition(struct ldb_module *module, struct ldb_message *local, struct ldb_message *remote, const struct ldb_message *msg, const char *attr_name, /* const char * const names[], */ const struct ldb_message_element *old)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+ const struct ldb_map_attribute *map = map_attr_find_local(data, attr_name);
+ struct ldb_message_element *el=NULL;
+
+ /* Unknown attribute: ignore */
+ if (map == NULL) {
+ ldb_debug(module->ldb, LDB_DEBUG_WARNING, "ldb_map: "
+ "Not mapping attribute '%s': no mapping found\n",
+ old->name);
+ goto local;
+ }
+
+ switch (map->type) {
+ case MAP_IGNORE:
+ goto local;
+
+ case MAP_CONVERT:
+ if (map->u.convert.convert_local == NULL) {
+ ldb_debug(module->ldb, LDB_DEBUG_WARNING, "ldb_map: "
+ "Not mapping attribute '%s': "
+ "'convert_local' not set\n",
+ map->local_name);
+ goto local;
+ }
+ /* fall through */
+ case MAP_KEEP:
+ case MAP_RENAME:
+ el = ldb_msg_el_map_local(module, remote, map, old);
+ break;
+
+ case MAP_GENERATE:
+ if (map->u.generate.generate_remote == NULL) {
+ ldb_debug(module->ldb, LDB_DEBUG_WARNING, "ldb_map: "
+ "Not mapping attribute '%s': "
+ "'generate_remote' not set\n",
+ map->local_name);
+ goto local;
+ }
+
+ /* TODO: if this attr requires context:
+ * make sure all context attrs are mappable (in 'names')
+ * make sure all context attrs have already been mapped?
+ * maybe postpone generation until they have been mapped?
+ */
+
+ map->u.generate.generate_remote(module, map->local_name, msg, remote, local);
+ return 0;
+ }
+
+ if (el == NULL) {
+ return -1;
+ }
+
+ return ldb_msg_add(remote, el, old->flags);
+
+local:
+ el = talloc(local, struct ldb_message_element);
+ if (el == NULL) {
+ map_oom(module);
+ return -1;
+ }
+
+ *el = *old; /* copy the old element */
+
+ return ldb_msg_add(local, el, old->flags);
+}
+
+/* Mapping messages
+ * ================ */
+
+/* Check whether a message will be (partially) mapped into the remote partition. */
+static bool ldb_msg_check_remote(struct ldb_module *module, const struct ldb_message *msg)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+ bool ret;
+ int i;
+
+ for (i = 0; i < msg->num_elements; i++) {
+ ret = map_attr_check_remote(data, msg->elements[i].name);
+ if (ret) {
+ return ret;
+ }
+ }
+
+ return false;
+}
+
+/* Split message elements that stay in the local partition from those
+ * that are mapped into the remote partition. */
+static int ldb_msg_partition(struct ldb_module *module, struct ldb_message *local, struct ldb_message *remote, const struct ldb_message *msg)
+{
+ /* const char * const names[]; */
+ int i, ret;
+
+ for (i = 0; i < msg->num_elements; i++) {
+ /* Skip 'IS_MAPPED' */
+ if (ldb_attr_cmp(msg->elements[i].name, IS_MAPPED) == 0) {
+ ldb_debug(module->ldb, LDB_DEBUG_WARNING, "ldb_map: "
+ "Skipping attribute '%s'\n",
+ msg->elements[i].name);
+ continue;
+ }
+
+ ret = ldb_msg_el_partition(module, local, remote, msg, msg->elements[i].name, &msg->elements[i]);
+ if (ret) {
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+
+/* Inbound requests: add, modify, rename, delete
+ * ============================================= */
+
+/* Add the remote record. */
+int map_add_do_remote(struct ldb_handle *handle)
+{
+ struct map_context *ac;
+
+ ac = talloc_get_type(handle->private_data, struct map_context);
+
+ ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->remote_req);
+
+ ac->step = MAP_ADD_REMOTE;
+
+ handle->state = LDB_ASYNC_INIT;
+ handle->status = LDB_SUCCESS;
+
+ return ldb_next_remote_request(ac->module, ac->remote_req);
+}
+
+/* Add the local record. */
+int map_add_do_local(struct ldb_handle *handle)
+{
+ struct map_context *ac;
+
+ ac = talloc_get_type(handle->private_data, struct map_context);
+
+ ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->local_req);
+
+ ac->step = MAP_ADD_LOCAL;
+
+ handle->state = LDB_ASYNC_INIT;
+ handle->status = LDB_SUCCESS;
+
+ return ldb_next_request(ac->module, ac->local_req);
+}
+
+/* Add a record. */
+int map_add(struct ldb_module *module, struct ldb_request *req)
+{
+ const struct ldb_message *msg = req->op.add.message;
+ struct ldb_handle *h;
+ struct map_context *ac;
+ struct ldb_message *local, *remote;
+ const char *dn;
+
+ /* Do not manipulate our control entries */
+ if (ldb_dn_is_special(msg->dn)) {
+ return ldb_next_request(module, req);
+ }
+
+ /* No mapping requested (perhaps no DN mapping specified), skip to next module */
+ if (!ldb_dn_check_local(module, msg->dn)) {
+ return ldb_next_request(module, req);
+ }
+
+ /* No mapping needed, fail */
+ if (!ldb_msg_check_remote(module, msg)) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Prepare context and handle */
+ h = map_init_handle(req, module);
+ if (h == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ac = talloc_get_type(h->private_data, struct map_context);
+
+ /* Prepare the local operation */
+ ac->local_req = talloc(ac, struct ldb_request);
+ if (ac->local_req == NULL) {
+ goto oom;
+ }
+
+ *(ac->local_req) = *req; /* copy the request */
+
+ ac->local_req->context = NULL;
+ ac->local_req->callback = NULL;
+
+ /* Prepare the remote operation */
+ ac->remote_req = talloc(ac, struct ldb_request);
+ if (ac->remote_req == NULL) {
+ goto oom;
+ }
+
+ *(ac->remote_req) = *req; /* copy the request */
+
+ ac->remote_req->context = NULL;
+ ac->remote_req->callback = NULL;
+
+ /* Prepare the local message */
+ local = ldb_msg_new(ac->local_req);
+ if (local == NULL) {
+ goto oom;
+ }
+ local->dn = msg->dn;
+
+ /* Prepare the remote message */
+ remote = ldb_msg_new(ac->remote_req);
+ if (remote == NULL) {
+ goto oom;
+ }
+ remote->dn = ldb_dn_map_local(ac->module, remote, msg->dn);
+
+ /* Split local from remote message */
+ ldb_msg_partition(module, local, remote, msg);
+ ac->local_req->op.add.message = local;
+ ac->remote_req->op.add.message = remote;
+
+ if ((local->num_elements == 0) || (!map_check_local_db(ac->module))) {
+ /* No local data or db, just run the remote request */
+ talloc_free(ac->local_req);
+ req->handle = h; /* return our own handle to deal with this call */
+ return map_add_do_remote(h);
+ }
+
+ /* Store remote DN in 'IS_MAPPED' */
+ /* TODO: use GUIDs here instead */
+ dn = ldb_dn_alloc_linearized(local, remote->dn);
+ if (ldb_msg_add_string(local, IS_MAPPED, dn) != 0) {
+ goto failed;
+ }
+
+ req->handle = h; /* return our own handle to deal with this call */
+ return map_add_do_local(h);
+
+oom:
+ map_oom(module);
+failed:
+ talloc_free(h);
+ return LDB_ERR_OPERATIONS_ERROR;
+}
+
+/* Modify the remote record. */
+int map_modify_do_remote(struct ldb_handle *handle)
+{
+ struct map_context *ac;
+
+ ac = talloc_get_type(handle->private_data, struct map_context);
+
+ ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->remote_req);
+
+ ac->step = MAP_MODIFY_REMOTE;
+
+ handle->state = LDB_ASYNC_INIT;
+ handle->status = LDB_SUCCESS;
+
+ return ldb_next_remote_request(ac->module, ac->remote_req);
+}
+
+/* Modify the local record. */
+int map_modify_do_local(struct ldb_handle *handle)
+{
+ struct map_context *ac;
+ struct ldb_message *msg;
+ char *dn;
+
+ ac = talloc_get_type(handle->private_data, struct map_context);
+
+ if (ac->local_dn == NULL) {
+ /* No local record present, add it instead */
+ msg = discard_const_p(struct ldb_message, ac->local_req->op.mod.message);
+
+ /* Add local 'IS_MAPPED' */
+ /* TODO: use GUIDs here instead */
+ if (ldb_msg_add_empty(msg, IS_MAPPED, LDB_FLAG_MOD_ADD, NULL) != 0) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ dn = ldb_dn_alloc_linearized(msg, ac->remote_req->op.mod.message->dn);
+ if (ldb_msg_add_string(msg, IS_MAPPED, dn) != 0) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Turn request into 'add' */
+ ac->local_req->operation = LDB_ADD;
+ ac->local_req->op.add.message = msg;
+ /* TODO: Could I just leave msg in there? I think so,
+ * but it looks clearer this way. */
+ }
+
+ ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->local_req);
+
+ ac->step = MAP_MODIFY_LOCAL;
+
+ handle->state = LDB_ASYNC_INIT;
+ handle->status = LDB_SUCCESS;
+
+ return ldb_next_request(ac->module, ac->local_req);
+}
+
+/* Modify a record. */
+int map_modify(struct ldb_module *module, struct ldb_request *req)
+{
+ const struct ldb_message *msg = req->op.mod.message;
+ struct ldb_handle *h;
+ struct map_context *ac;
+ struct ldb_message *local, *remote;
+
+ /* Do not manipulate our control entries */
+ if (ldb_dn_is_special(msg->dn)) {
+ return ldb_next_request(module, req);
+ }
+
+ /* No mapping requested (perhaps no DN mapping specified), skip to next module */
+ if (!ldb_dn_check_local(module, msg->dn)) {
+ return ldb_next_request(module, req);
+ }
+
+ /* No mapping needed, skip to next module */
+ /* TODO: What if the remote part exists, the local doesn't,
+ * and this request wants to modify local data and thus
+ * add the local record? */
+ if (!ldb_msg_check_remote(module, msg)) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Prepare context and handle */
+ h = map_init_handle(req, module);
+ if (h == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ac = talloc_get_type(h->private_data, struct map_context);
+
+ /* Prepare the local operation */
+ ac->local_req = talloc(ac, struct ldb_request);
+ if (ac->local_req == NULL) {
+ goto oom;
+ }
+
+ *(ac->local_req) = *req; /* copy the request */
+
+ ac->local_req->context = NULL;
+ ac->local_req->callback = NULL;
+
+ /* Prepare the remote operation */
+ ac->remote_req = talloc(ac, struct ldb_request);
+ if (ac->remote_req == NULL) {
+ goto oom;
+ }
+
+ *(ac->remote_req) = *req; /* copy the request */
+
+ ac->remote_req->context = NULL;
+ ac->remote_req->callback = NULL;
+
+ /* Prepare the local message */
+ local = ldb_msg_new(ac->local_req);
+ if (local == NULL) {
+ goto oom;
+ }
+ local->dn = msg->dn;
+
+ /* Prepare the remote message */
+ remote = ldb_msg_new(ac->remote_req);
+ if (remote == NULL) {
+ goto oom;
+ }
+ remote->dn = ldb_dn_map_local(ac->module, remote, msg->dn);
+
+ /* Split local from remote message */
+ ldb_msg_partition(module, local, remote, msg);
+ ac->local_req->op.mod.message = local;
+ ac->remote_req->op.mod.message = remote;
+
+ if ((local->num_elements == 0) || (!map_check_local_db(ac->module))) {
+ /* No local data or db, just run the remote request */
+ talloc_free(ac->local_req);
+ req->handle = h; /* return our own handle to deal with this call */
+ return map_modify_do_remote(h);
+ }
+
+ /* prepare the search operation */
+ ac->search_req = map_search_self_req(ac, msg->dn);
+ if (ac->search_req == NULL) {
+ goto failed;
+ }
+
+ ac->step = MAP_SEARCH_SELF_MODIFY;
+
+ req->handle = h; /* return our own handle to deal with this call */
+ return ldb_next_request(module, ac->search_req);
+
+oom:
+ map_oom(module);
+failed:
+ talloc_free(h);
+ return LDB_ERR_OPERATIONS_ERROR;
+}
+
+/* Delete the remote record. */
+int map_delete_do_remote(struct ldb_handle *handle)
+{
+ struct map_context *ac;
+
+ ac = talloc_get_type(handle->private_data, struct map_context);
+
+ ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->remote_req);
+
+ ac->step = MAP_DELETE_REMOTE;
+
+ handle->state = LDB_ASYNC_INIT;
+ handle->status = LDB_SUCCESS;
+
+ return ldb_next_remote_request(ac->module, ac->remote_req);
+}
+
+/* Delete the local record. */
+int map_delete_do_local(struct ldb_handle *handle)
+{
+ struct map_context *ac;
+
+ ac = talloc_get_type(handle->private_data, struct map_context);
+
+ /* No local record, continue remotely */
+ if (ac->local_dn == NULL) {
+ return map_delete_do_remote(handle);
+ }
+
+ ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->local_req);
+
+ ac->step = MAP_DELETE_LOCAL;
+
+ handle->state = LDB_ASYNC_INIT;
+ handle->status = LDB_SUCCESS;
+
+ return ldb_next_request(ac->module, ac->local_req);
+}
+
+/* Delete a record. */
+int map_delete(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_handle *h;
+ struct map_context *ac;
+
+ /* Do not manipulate our control entries */
+ if (ldb_dn_is_special(req->op.del.dn)) {
+ return ldb_next_request(module, req);
+ }
+
+ /* No mapping requested (perhaps no DN mapping specified), skip to next module */
+ if (!ldb_dn_check_local(module, req->op.del.dn)) {
+ return ldb_next_request(module, req);
+ }
+
+ /* Prepare context and handle */
+ h = map_init_handle(req, module);
+ if (h == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ac = talloc_get_type(h->private_data, struct map_context);
+
+ /* Prepare the local operation */
+ ac->local_req = talloc(ac, struct ldb_request);
+ if (ac->local_req == NULL) {
+ goto oom;
+ }
+
+ *(ac->local_req) = *req; /* copy the request */
+ ac->local_req->op.del.dn = req->op.del.dn;
+
+ ac->local_req->context = NULL;
+ ac->local_req->callback = NULL;
+
+ /* Prepare the remote operation */
+ ac->remote_req = talloc(ac, struct ldb_request);
+ if (ac->remote_req == NULL) {
+ goto oom;
+ }
+
+ *(ac->remote_req) = *req; /* copy the request */
+ ac->remote_req->op.del.dn = ldb_dn_map_local(module, ac->remote_req, req->op.del.dn);
+
+ /* No local db, just run the remote request */
+ if (!map_check_local_db(ac->module)) {
+ req->handle = h; /* return our own handle to deal with this call */
+ return map_delete_do_remote(h);
+ }
+
+ ac->remote_req->context = NULL;
+ ac->remote_req->callback = NULL;
+
+ /* Prepare the search operation */
+ ac->search_req = map_search_self_req(ac, req->op.del.dn);
+ if (ac->search_req == NULL) {
+ goto failed;
+ }
+
+ req->handle = h; /* return our own handle to deal with this call */
+
+ ac->step = MAP_SEARCH_SELF_DELETE;
+
+ return ldb_next_request(module, ac->search_req);
+
+oom:
+ map_oom(module);
+failed:
+ talloc_free(h);
+ return LDB_ERR_OPERATIONS_ERROR;
+}
+
+/* Rename the remote record. */
+int map_rename_do_remote(struct ldb_handle *handle)
+{
+ struct map_context *ac;
+
+ ac = talloc_get_type(handle->private_data, struct map_context);
+
+ ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->remote_req);
+
+ ac->step = MAP_RENAME_REMOTE;
+
+ handle->state = LDB_ASYNC_INIT;
+ handle->status = LDB_SUCCESS;
+
+ return ldb_next_remote_request(ac->module, ac->remote_req);
+}
+
+/* Update the local 'IS_MAPPED' attribute. */
+int map_rename_do_fixup(struct ldb_handle *handle)
+{
+ struct map_context *ac;
+
+ ac = talloc_get_type(handle->private_data, struct map_context);
+
+ ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->down_req);
+
+ ac->step = MAP_RENAME_FIXUP;
+
+ handle->state = LDB_ASYNC_INIT;
+ handle->status = LDB_SUCCESS;
+
+ return ldb_next_request(ac->module, ac->down_req);
+}
+
+/* Rename the local record. */
+int map_rename_do_local(struct ldb_handle *handle)
+{
+ struct map_context *ac;
+
+ ac = talloc_get_type(handle->private_data, struct map_context);
+
+ /* No local record, continue remotely */
+ if (ac->local_dn == NULL) {
+ return map_rename_do_remote(handle);
+ }
+
+ ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->local_req);
+
+ ac->step = MAP_RENAME_LOCAL;
+
+ handle->state = LDB_ASYNC_INIT;
+ handle->status = LDB_SUCCESS;
+
+ return ldb_next_request(ac->module, ac->local_req);
+}
+
+/* Rename a record. */
+int map_rename(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_handle *h;
+ struct map_context *ac;
+
+ /* Do not manipulate our control entries */
+ if (ldb_dn_is_special(req->op.rename.olddn)) {
+ return ldb_next_request(module, req);
+ }
+
+ /* No mapping requested (perhaps no DN mapping specified), skip to next module */
+ if ((!ldb_dn_check_local(module, req->op.rename.olddn)) &&
+ (!ldb_dn_check_local(module, req->op.rename.newdn))) {
+ return ldb_next_request(module, req);
+ }
+
+ /* Rename into/out of the mapped partition requested, bail out */
+ if (!ldb_dn_check_local(module, req->op.rename.olddn) ||
+ !ldb_dn_check_local(module, req->op.rename.newdn)) {
+ return LDB_ERR_AFFECTS_MULTIPLE_DSAS;
+ }
+
+ /* Prepare context and handle */
+ h = map_init_handle(req, module);
+ if (h == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ac = talloc_get_type(h->private_data, struct map_context);
+
+ /* Prepare the local operation */
+ ac->local_req = talloc(ac, struct ldb_request);
+ if (ac->local_req == NULL) {
+ goto oom;
+ }
+
+ *(ac->local_req) = *req; /* copy the request */
+ ac->local_req->op.rename.olddn = req->op.rename.olddn;
+ ac->local_req->op.rename.newdn = req->op.rename.newdn;
+
+ ac->local_req->context = NULL;
+ ac->local_req->callback = NULL;
+
+ /* Prepare the remote operation */
+ ac->remote_req = talloc(ac, struct ldb_request);
+ if (ac->remote_req == NULL) {
+ goto oom;
+ }
+
+ *(ac->remote_req) = *req; /* copy the request */
+ ac->remote_req->op.rename.olddn = ldb_dn_map_local(module, ac->remote_req, req->op.rename.olddn);
+ ac->remote_req->op.rename.newdn = ldb_dn_map_local(module, ac->remote_req, req->op.rename.newdn);
+
+ ac->remote_req->context = NULL;
+ ac->remote_req->callback = NULL;
+
+ /* No local db, just run the remote request */
+ if (!map_check_local_db(ac->module)) {
+ req->handle = h; /* return our own handle to deal with this call */
+ return map_rename_do_remote(h);
+ }
+
+ /* Prepare the fixup operation */
+ /* TODO: use GUIDs here instead -- or skip it when GUIDs are used. */
+ ac->down_req = map_build_fixup_req(ac, req->op.rename.newdn, ac->remote_req->op.rename.newdn);
+ if (ac->down_req == NULL) {
+ goto failed;
+ }
+
+ /* Prepare the search operation */
+ ac->search_req = map_search_self_req(ac, req->op.rename.olddn);
+ if (ac->search_req == NULL) {
+ goto failed;
+ }
+
+ req->handle = h; /* return our own handle to deal with this call */
+
+ ac->step = MAP_SEARCH_SELF_RENAME;
+
+ return ldb_next_request(module, ac->search_req);
+
+oom:
+ map_oom(module);
+failed:
+ talloc_free(h);
+ return LDB_ERR_OPERATIONS_ERROR;
+}
diff --git a/ldb/ldb_map/ldb_map_outbound.c b/ldb/ldb_map/ldb_map_outbound.c
new file mode 100644
index 00000000..fbc097f3
--- /dev/null
+++ b/ldb/ldb_map/ldb_map_outbound.c
@@ -0,0 +1,1289 @@
+/*
+ ldb database mapping module
+
+ Copyright (C) Jelmer Vernooij 2005
+ Copyright (C) Martin Kuehl <mkhl@samba.org> 2006
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006
+
+ ** 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
+ version 3 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "ldb_includes.h"
+
+#include "ldb_map.h"
+#include "ldb_map_private.h"
+
+
+/* Mapping attributes
+ * ================== */
+
+/* Select attributes that stay in the local partition. */
+static const char **map_attrs_select_local(struct ldb_module *module, void *mem_ctx, const char * const *attrs)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+ const char **result;
+ int i, last;
+
+ if (attrs == NULL)
+ return NULL;
+
+ last = 0;
+ result = talloc_array(mem_ctx, const char *, 1);
+ if (result == NULL) {
+ goto failed;
+ }
+ result[0] = NULL;
+
+ for (i = 0; attrs[i]; i++) {
+ /* Wildcards and ignored attributes are kept locally */
+ if ((ldb_attr_cmp(attrs[i], "*") == 0) ||
+ (!map_attr_check_remote(data, attrs[i]))) {
+ result = talloc_realloc(mem_ctx, result, const char *, last+2);
+ if (result == NULL) {
+ goto failed;
+ }
+
+ result[last] = talloc_strdup(result, attrs[i]);
+ result[last+1] = NULL;
+ last++;
+ }
+ }
+
+ return result;
+
+failed:
+ talloc_free(result);
+ map_oom(module);
+ return NULL;
+}
+
+/* Collect attributes that are mapped into the remote partition. */
+static const char **map_attrs_collect_remote(struct ldb_module *module, void *mem_ctx,
+ const char * const *attrs)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+ const char **result;
+ const struct ldb_map_attribute *map;
+ const char *name=NULL;
+ int i, j, last;
+ int ret;
+
+ last = 0;
+ result = talloc_array(mem_ctx, const char *, 1);
+ if (result == NULL) {
+ goto failed;
+ }
+ result[0] = NULL;
+
+ for (i = 0; attrs[i]; i++) {
+ /* Wildcards are kept remotely, too */
+ if (ldb_attr_cmp(attrs[i], "*") == 0) {
+ const char **new_attrs = NULL;
+ ret = map_attrs_merge(module, mem_ctx, &new_attrs, attrs);
+ if (ret != LDB_SUCCESS) {
+ goto failed;
+ }
+ ret = map_attrs_merge(module, mem_ctx, &new_attrs, data->wildcard_attributes);
+ if (ret != LDB_SUCCESS) {
+ goto failed;
+ }
+
+ attrs = new_attrs;
+ break;
+ }
+ }
+
+ for (i = 0; attrs[i]; i++) {
+ /* Wildcards are kept remotely, too */
+ if (ldb_attr_cmp(attrs[i], "*") == 0) {
+ /* Add all 'include in wildcard' attributes */
+ name = attrs[i];
+ goto named;
+ }
+
+ /* Add remote names of mapped attrs */
+ map = map_attr_find_local(data, attrs[i]);
+ if (map == NULL) {
+ continue;
+ }
+
+ switch (map->type) {
+ case MAP_IGNORE:
+ continue;
+
+ case MAP_KEEP:
+ name = attrs[i];
+ goto named;
+
+ case MAP_RENAME:
+ case MAP_CONVERT:
+ name = map->u.rename.remote_name;
+ goto named;
+
+ case MAP_GENERATE:
+ /* Add all remote names of "generate" attrs */
+ for (j = 0; map->u.generate.remote_names[j]; j++) {
+ result = talloc_realloc(mem_ctx, result, const char *, last+2);
+ if (result == NULL) {
+ goto failed;
+ }
+
+ result[last] = talloc_strdup(result, map->u.generate.remote_names[j]);
+ result[last+1] = NULL;
+ last++;
+ }
+ continue;
+ }
+
+ named: /* We found a single remote name, add that */
+ result = talloc_realloc(mem_ctx, result, const char *, last+2);
+ if (result == NULL) {
+ goto failed;
+ }
+
+ result[last] = talloc_strdup(result, name);
+ result[last+1] = NULL;
+ last++;
+ }
+
+ return result;
+
+failed:
+ talloc_free(result);
+ map_oom(module);
+ return NULL;
+}
+
+/* Split attributes that stay in the local partition from those that
+ * are mapped into the remote partition. */
+static int map_attrs_partition(struct ldb_module *module, void *mem_ctx, const char ***local_attrs, const char ***remote_attrs, const char * const *attrs)
+{
+ *local_attrs = map_attrs_select_local(module, mem_ctx, attrs);
+ *remote_attrs = map_attrs_collect_remote(module, mem_ctx, attrs);
+
+ return 0;
+}
+
+/* Mapping message elements
+ * ======================== */
+
+/* Add an element to a message, overwriting any old identically named elements. */
+static int ldb_msg_replace(struct ldb_message *msg, const struct ldb_message_element *el)
+{
+ struct ldb_message_element *old;
+
+ old = ldb_msg_find_element(msg, el->name);
+
+ /* no local result, add as new element */
+ if (old == NULL) {
+ if (ldb_msg_add_empty(msg, el->name, 0, &old) != 0) {
+ return -1;
+ }
+ talloc_free(discard_const_p(char, old->name));
+ }
+
+ /* copy new element */
+ *old = *el;
+
+ /* and make sure we reference the contents */
+ if (!talloc_reference(msg->elements, el->name)) {
+ return -1;
+ }
+ if (!talloc_reference(msg->elements, el->values)) {
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Map a message element back into the local partition. */
+static struct ldb_message_element *ldb_msg_el_map_remote(struct ldb_module *module,
+ void *mem_ctx,
+ const struct ldb_map_attribute *map,
+ const char *attr_name,
+ const struct ldb_message_element *old)
+{
+ struct ldb_message_element *el;
+ int i;
+
+ el = talloc_zero(mem_ctx, struct ldb_message_element);
+ if (el == NULL) {
+ map_oom(module);
+ return NULL;
+ }
+
+ el->values = talloc_array(el, struct ldb_val, old->num_values);
+ if (el->values == NULL) {
+ talloc_free(el);
+ map_oom(module);
+ return NULL;
+ }
+
+ el->name = talloc_strdup(el, attr_name);
+ if (el->name == NULL) {
+ talloc_free(el);
+ map_oom(module);
+ return NULL;
+ }
+
+ for (i = 0; i < old->num_values; i++) {
+ el->values[i] = ldb_val_map_remote(module, el->values, map, &old->values[i]);
+ /* Conversions might fail, in which case bail */
+ if (!el->values[i].data) {
+ talloc_free(el);
+ return NULL;
+ }
+ el->num_values++;
+ }
+
+ return el;
+}
+
+/* Merge a remote message element into a local message. */
+static int ldb_msg_el_merge(struct ldb_module *module, struct ldb_message *local,
+ struct ldb_message *remote, const char *attr_name)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+ const struct ldb_map_attribute *map;
+ struct ldb_message_element *old, *el=NULL;
+ const char *remote_name = NULL;
+
+ /* We handle wildcards in ldb_msg_el_merge_wildcard */
+ if (ldb_attr_cmp(attr_name, "*") == 0) {
+ return LDB_SUCCESS;
+ }
+
+ map = map_attr_find_local(data, attr_name);
+
+ /* Unknown attribute in remote message:
+ * skip, attribute was probably auto-generated */
+ if (map == NULL) {
+ return LDB_SUCCESS;
+ }
+
+ switch (map->type) {
+ case MAP_IGNORE:
+ break;
+ case MAP_CONVERT:
+ remote_name = map->u.convert.remote_name;
+ break;
+ case MAP_KEEP:
+ remote_name = attr_name;
+ break;
+ case MAP_RENAME:
+ remote_name = map->u.rename.remote_name;
+ break;
+ case MAP_GENERATE:
+ break;
+ }
+
+ switch (map->type) {
+ case MAP_IGNORE:
+ return LDB_SUCCESS;
+
+ case MAP_CONVERT:
+ if (map->u.convert.convert_remote == NULL) {
+ ldb_debug(module->ldb, LDB_DEBUG_ERROR, "ldb_map: "
+ "Skipping attribute '%s': "
+ "'convert_remote' not set\n",
+ attr_name);
+ return LDB_SUCCESS;
+ }
+ /* fall through */
+ case MAP_KEEP:
+ case MAP_RENAME:
+ old = ldb_msg_find_element(remote, remote_name);
+ if (old) {
+ el = ldb_msg_el_map_remote(module, local, map, attr_name, old);
+ } else {
+ return LDB_ERR_NO_SUCH_ATTRIBUTE;
+ }
+ break;
+
+ case MAP_GENERATE:
+ if (map->u.generate.generate_local == NULL) {
+ ldb_debug(module->ldb, LDB_DEBUG_ERROR, "ldb_map: "
+ "Skipping attribute '%s': "
+ "'generate_local' not set\n",
+ attr_name);
+ return LDB_SUCCESS;
+ }
+
+ el = map->u.generate.generate_local(module, local, attr_name, remote);
+ if (!el) {
+ /* Generation failure is probably due to lack of source attributes */
+ return LDB_ERR_NO_SUCH_ATTRIBUTE;
+ }
+ break;
+ }
+
+ if (el == NULL) {
+ return LDB_ERR_NO_SUCH_ATTRIBUTE;
+ }
+
+ return ldb_msg_replace(local, el);
+}
+
+/* Handle wildcard parts of merging a remote message element into a local message. */
+static int ldb_msg_el_merge_wildcard(struct ldb_module *module, struct ldb_message *local,
+ struct ldb_message *remote)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+ const struct ldb_map_attribute *map = map_attr_find_local(data, "*");
+ struct ldb_message_element *el=NULL;
+ int i, ret;
+
+ /* Perhaps we have a mapping for "*" */
+ if (map && map->type == MAP_KEEP) {
+ /* We copy everything over, and hope that anything with a
+ more specific rule is overwritten */
+ for (i = 0; i < remote->num_elements; i++) {
+ el = ldb_msg_el_map_remote(module, local, map, remote->elements[i].name,
+ &remote->elements[i]);
+ if (el == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldb_msg_replace(local, el);
+ if (ret) {
+ return ret;
+ }
+ }
+ }
+
+ /* Now walk the list of possible mappings, and apply each */
+ for (i = 0; data->attribute_maps[i].local_name; i++) {
+ ret = ldb_msg_el_merge(module, local, remote,
+ data->attribute_maps[i].local_name);
+ if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE) {
+ continue;
+ } else if (ret) {
+ return ret;
+ } else {
+ continue;
+ }
+ }
+
+ return LDB_SUCCESS;
+}
+
+/* Mapping messages
+ * ================ */
+
+/* Merge two local messages into a single one. */
+static int ldb_msg_merge_local(struct ldb_module *module, struct ldb_message *msg1, struct ldb_message *msg2)
+{
+ int i, ret;
+
+ for (i = 0; i < msg2->num_elements; i++) {
+ ret = ldb_msg_replace(msg1, &msg2->elements[i]);
+ if (ret) {
+ return ret;
+ }
+ }
+
+ return LDB_SUCCESS;
+}
+
+/* Merge a local and a remote message into a single local one. */
+static int ldb_msg_merge_remote(struct map_context *ac, struct ldb_message *local,
+ struct ldb_message *remote)
+{
+ int i, ret;
+ const char * const *attrs = ac->all_attrs;
+ if (!attrs) {
+ ret = ldb_msg_el_merge_wildcard(ac->module, local, remote);
+ if (ret) {
+ return ret;
+ }
+ }
+
+ for (i = 0; attrs && attrs[i]; i++) {
+ if (ldb_attr_cmp(attrs[i], "*") == 0) {
+ ret = ldb_msg_el_merge_wildcard(ac->module, local, remote);
+ if (ret) {
+ return ret;
+ }
+ break;
+ }
+ }
+
+ /* Try to map each attribute back;
+ * Add to local message is possible,
+ * Overwrite old local attribute if necessary */
+ for (i = 0; attrs && attrs[i]; i++) {
+ ret = ldb_msg_el_merge(ac->module, local, remote,
+ attrs[i]);
+ if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE) {
+ } else if (ret) {
+ return ret;
+ }
+ }
+
+ return LDB_SUCCESS;
+}
+
+/* Mapping search results
+ * ====================== */
+
+/* Map a search result back into the local partition. */
+static int map_reply_remote(struct map_context *ac, struct ldb_reply *ares)
+{
+ struct ldb_message *msg;
+ struct ldb_dn *dn;
+ int ret;
+
+ /* There is no result message, skip */
+ if (ares->type != LDB_REPLY_ENTRY) {
+ return 0;
+ }
+
+ /* Create a new result message */
+ msg = ldb_msg_new(ares);
+ if (msg == NULL) {
+ map_oom(ac->module);
+ return -1;
+ }
+
+ /* Merge remote message into new message */
+ ret = ldb_msg_merge_remote(ac, msg, ares->message);
+ if (ret) {
+ talloc_free(msg);
+ return ret;
+ }
+
+ /* Create corresponding local DN */
+ dn = ldb_dn_map_rebase_remote(ac->module, msg, ares->message->dn);
+ if (dn == NULL) {
+ talloc_free(msg);
+ return -1;
+ }
+ msg->dn = dn;
+
+ /* Store new message with new DN as the result */
+ talloc_free(ares->message);
+ ares->message = msg;
+
+ return 0;
+}
+
+/* Mapping parse trees
+ * =================== */
+
+/* Check whether a parse tree can safely be split in two. */
+static bool ldb_parse_tree_check_splittable(const struct ldb_parse_tree *tree)
+{
+ const struct ldb_parse_tree *subtree = tree;
+ bool negate = false;
+
+ while (subtree) {
+ switch (subtree->operation) {
+ case LDB_OP_NOT:
+ negate = !negate;
+ subtree = subtree->u.isnot.child;
+ continue;
+
+ case LDB_OP_AND:
+ return !negate; /* if negate: False */
+
+ case LDB_OP_OR:
+ return negate; /* if negate: True */
+
+ default:
+ return true; /* simple parse tree */
+ }
+ }
+
+ return true; /* no parse tree */
+}
+
+/* Collect a list of attributes required to match a given parse tree. */
+static int ldb_parse_tree_collect_attrs(struct ldb_module *module, void *mem_ctx, const char ***attrs, const struct ldb_parse_tree *tree)
+{
+ const char **new_attrs;
+ int i, ret;
+
+ if (tree == NULL) {
+ return 0;
+ }
+
+ switch (tree->operation) {
+ case LDB_OP_OR:
+ case LDB_OP_AND: /* attributes stored in list of subtrees */
+ for (i = 0; i < tree->u.list.num_elements; i++) {
+ ret = ldb_parse_tree_collect_attrs(module, mem_ctx,
+ attrs, tree->u.list.elements[i]);
+ if (ret) {
+ return ret;
+ }
+ }
+ return 0;
+
+ case LDB_OP_NOT: /* attributes stored in single subtree */
+ return ldb_parse_tree_collect_attrs(module, mem_ctx, attrs, tree->u.isnot.child);
+
+ default: /* single attribute in tree */
+ new_attrs = ldb_attr_list_copy_add(mem_ctx, *attrs, tree->u.equality.attr);
+ talloc_free(*attrs);
+ *attrs = new_attrs;
+ return 0;
+ }
+
+ return -1;
+}
+
+static int map_subtree_select_local(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree);
+
+/* Select a negated subtree that queries attributes in the local partition */
+static int map_subtree_select_local_not(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree)
+{
+ struct ldb_parse_tree *child;
+ int ret;
+
+ /* Prepare new tree */
+ *new = talloc_memdup(mem_ctx, tree, sizeof(struct ldb_parse_tree));
+ if (*new == NULL) {
+ map_oom(module);
+ return -1;
+ }
+
+ /* Generate new subtree */
+ ret = map_subtree_select_local(module, *new, &child, tree->u.isnot.child);
+ if (ret) {
+ talloc_free(*new);
+ return ret;
+ }
+
+ /* Prune tree without subtree */
+ if (child == NULL) {
+ talloc_free(*new);
+ *new = NULL;
+ return 0;
+ }
+
+ (*new)->u.isnot.child = child;
+
+ return ret;
+}
+
+/* Select a list of subtrees that query attributes in the local partition */
+static int map_subtree_select_local_list(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree)
+{
+ int i, j, ret=0;
+
+ /* Prepare new tree */
+ *new = talloc_memdup(mem_ctx, tree, sizeof(struct ldb_parse_tree));
+ if (*new == NULL) {
+ map_oom(module);
+ return -1;
+ }
+
+ /* Prepare list of subtrees */
+ (*new)->u.list.num_elements = 0;
+ (*new)->u.list.elements = talloc_array(*new, struct ldb_parse_tree *, tree->u.list.num_elements);
+ if ((*new)->u.list.elements == NULL) {
+ map_oom(module);
+ talloc_free(*new);
+ return -1;
+ }
+
+ /* Generate new list of subtrees */
+ j = 0;
+ for (i = 0; i < tree->u.list.num_elements; i++) {
+ struct ldb_parse_tree *child;
+ ret = map_subtree_select_local(module, *new, &child, tree->u.list.elements[i]);
+ if (ret) {
+ talloc_free(*new);
+ return ret;
+ }
+
+ if (child) {
+ (*new)->u.list.elements[j] = child;
+ j++;
+ }
+ }
+
+ /* Prune tree without subtrees */
+ if (j == 0) {
+ talloc_free(*new);
+ *new = NULL;
+ return 0;
+ }
+
+ /* Fix subtree list size */
+ (*new)->u.list.num_elements = j;
+ (*new)->u.list.elements = talloc_realloc(*new, (*new)->u.list.elements, struct ldb_parse_tree *, (*new)->u.list.num_elements);
+
+ return ret;
+}
+
+/* Select a simple subtree that queries attributes in the local partition */
+static int map_subtree_select_local_simple(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree)
+{
+ /* Prepare new tree */
+ *new = talloc_memdup(mem_ctx, tree, sizeof(struct ldb_parse_tree));
+ if (*new == NULL) {
+ map_oom(module);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Select subtrees that query attributes in the local partition */
+static int map_subtree_select_local(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+
+ if (tree == NULL) {
+ return 0;
+ }
+
+ if (tree->operation == LDB_OP_NOT) {
+ return map_subtree_select_local_not(module, mem_ctx, new, tree);
+ }
+
+ if (tree->operation == LDB_OP_AND || tree->operation == LDB_OP_OR) {
+ return map_subtree_select_local_list(module, mem_ctx, new, tree);
+ }
+
+ if (map_attr_check_remote(data, tree->u.equality.attr)) {
+ *new = NULL;
+ return 0;
+ }
+
+ return map_subtree_select_local_simple(module, mem_ctx, new, tree);
+}
+
+static int map_subtree_collect_remote(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree);
+
+/* Collect a negated subtree that queries attributes in the remote partition */
+static int map_subtree_collect_remote_not(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree)
+{
+ struct ldb_parse_tree *child;
+ int ret;
+
+ /* Prepare new tree */
+ *new = talloc_memdup(mem_ctx, tree, sizeof(struct ldb_parse_tree));
+ if (*new == NULL) {
+ map_oom(module);
+ return -1;
+ }
+
+ /* Generate new subtree */
+ ret = map_subtree_collect_remote(module, *new, &child, tree->u.isnot.child);
+ if (ret) {
+ talloc_free(*new);
+ return ret;
+ }
+
+ /* Prune tree without subtree */
+ if (child == NULL) {
+ talloc_free(*new);
+ *new = NULL;
+ return 0;
+ }
+
+ (*new)->u.isnot.child = child;
+
+ return ret;
+}
+
+/* Collect a list of subtrees that query attributes in the remote partition */
+static int map_subtree_collect_remote_list(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree)
+{
+ int i, j, ret=0;
+
+ /* Prepare new tree */
+ *new = talloc_memdup(mem_ctx, tree, sizeof(struct ldb_parse_tree));
+ if (*new == NULL) {
+ map_oom(module);
+ return -1;
+ }
+
+ /* Prepare list of subtrees */
+ (*new)->u.list.num_elements = 0;
+ (*new)->u.list.elements = talloc_array(*new, struct ldb_parse_tree *, tree->u.list.num_elements);
+ if ((*new)->u.list.elements == NULL) {
+ map_oom(module);
+ talloc_free(*new);
+ return -1;
+ }
+
+ /* Generate new list of subtrees */
+ j = 0;
+ for (i = 0; i < tree->u.list.num_elements; i++) {
+ struct ldb_parse_tree *child;
+ ret = map_subtree_collect_remote(module, *new, &child, tree->u.list.elements[i]);
+ if (ret) {
+ talloc_free(*new);
+ return ret;
+ }
+
+ if (child) {
+ (*new)->u.list.elements[j] = child;
+ j++;
+ }
+ }
+
+ /* Prune tree without subtrees */
+ if (j == 0) {
+ talloc_free(*new);
+ *new = NULL;
+ return 0;
+ }
+
+ /* Fix subtree list size */
+ (*new)->u.list.num_elements = j;
+ (*new)->u.list.elements = talloc_realloc(*new, (*new)->u.list.elements, struct ldb_parse_tree *, (*new)->u.list.num_elements);
+
+ return ret;
+}
+
+/* Collect a simple subtree that queries attributes in the remote partition */
+int map_subtree_collect_remote_simple(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree, const struct ldb_map_attribute *map)
+{
+ const char *attr;
+
+ /* Prepare new tree */
+ *new = talloc(mem_ctx, struct ldb_parse_tree);
+ if (*new == NULL) {
+ map_oom(module);
+ return -1;
+ }
+ **new = *tree;
+
+ if (map->type == MAP_KEEP) {
+ /* Nothing to do here */
+ return 0;
+ }
+
+ /* Store attribute and value in new tree */
+ switch (tree->operation) {
+ case LDB_OP_PRESENT:
+ attr = map_attr_map_local(*new, map, tree->u.present.attr);
+ (*new)->u.present.attr = attr;
+ break;
+ case LDB_OP_SUBSTRING:
+ {
+ attr = map_attr_map_local(*new, map, tree->u.substring.attr);
+ (*new)->u.substring.attr = attr;
+ break;
+ }
+ case LDB_OP_EQUALITY:
+ attr = map_attr_map_local(*new, map, tree->u.equality.attr);
+ (*new)->u.equality.attr = attr;
+ break;
+ case LDB_OP_LESS:
+ case LDB_OP_GREATER:
+ case LDB_OP_APPROX:
+ attr = map_attr_map_local(*new, map, tree->u.comparison.attr);
+ (*new)->u.comparison.attr = attr;
+ break;
+ case LDB_OP_EXTENDED:
+ attr = map_attr_map_local(*new, map, tree->u.extended.attr);
+ (*new)->u.extended.attr = attr;
+ break;
+ default: /* unknown kind of simple subtree */
+ talloc_free(*new);
+ return -1;
+ }
+
+ if (attr == NULL) {
+ talloc_free(*new);
+ *new = NULL;
+ return 0;
+ }
+
+ if (map->type == MAP_RENAME) {
+ /* Nothing more to do here, the attribute has been renamed */
+ return 0;
+ }
+
+ /* Store attribute and value in new tree */
+ switch (tree->operation) {
+ case LDB_OP_PRESENT:
+ break;
+ case LDB_OP_SUBSTRING:
+ {
+ int i;
+ /* Map value */
+ (*new)->u.substring.chunks = NULL;
+ for (i=0; tree->u.substring.chunks[i]; i++) {
+ (*new)->u.substring.chunks = talloc_realloc(*new, (*new)->u.substring.chunks, struct ldb_val *, i+2);
+ if (!(*new)->u.substring.chunks) {
+ talloc_free(*new);
+ *new = NULL;
+ return 0;
+ }
+ (*new)->u.substring.chunks[i] = talloc(*new, struct ldb_val);
+ if (!(*new)->u.substring.chunks[i]) {
+ talloc_free(*new);
+ *new = NULL;
+ return 0;
+ }
+ *(*new)->u.substring.chunks[i] = ldb_val_map_local(module, *new, map, tree->u.substring.chunks[i]);
+ (*new)->u.substring.chunks[i+1] = NULL;
+ }
+ break;
+ }
+ case LDB_OP_EQUALITY:
+ (*new)->u.equality.value = ldb_val_map_local(module, *new, map, &tree->u.equality.value);
+ break;
+ case LDB_OP_LESS:
+ case LDB_OP_GREATER:
+ case LDB_OP_APPROX:
+ (*new)->u.comparison.value = ldb_val_map_local(module, *new, map, &tree->u.comparison.value);
+ break;
+ case LDB_OP_EXTENDED:
+ (*new)->u.extended.value = ldb_val_map_local(module, *new, map, &tree->u.extended.value);
+ (*new)->u.extended.rule_id = talloc_strdup(*new, tree->u.extended.rule_id);
+ break;
+ default: /* unknown kind of simple subtree */
+ talloc_free(*new);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Collect subtrees that query attributes in the remote partition */
+static int map_subtree_collect_remote(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+ const struct ldb_map_attribute *map;
+
+ if (tree == NULL) {
+ return 0;
+ }
+
+ if (tree->operation == LDB_OP_NOT) {
+ return map_subtree_collect_remote_not(module, mem_ctx, new, tree);
+ }
+
+ if ((tree->operation == LDB_OP_AND) || (tree->operation == LDB_OP_OR)) {
+ return map_subtree_collect_remote_list(module, mem_ctx, new, tree);
+ }
+
+ if (!map_attr_check_remote(data, tree->u.equality.attr)) {
+ *new = NULL;
+ return 0;
+ }
+
+ map = map_attr_find_local(data, tree->u.equality.attr);
+ if (map->convert_operator) {
+ return map->convert_operator(module, mem_ctx, new, tree);
+ }
+
+ if (map->type == MAP_GENERATE) {
+ ldb_debug(module->ldb, LDB_DEBUG_WARNING, "ldb_map: "
+ "Skipping attribute '%s': "
+ "'convert_operator' not set\n",
+ tree->u.equality.attr);
+ *new = NULL;
+ return 0;
+ }
+
+ return map_subtree_collect_remote_simple(module, mem_ctx, new, tree, map);
+}
+
+/* Split subtrees that query attributes in the local partition from
+ * those that query the remote partition. */
+static int ldb_parse_tree_partition(struct ldb_module *module, void *local_ctx, void *remote_ctx, struct ldb_parse_tree **local_tree, struct ldb_parse_tree **remote_tree, const struct ldb_parse_tree *tree)
+{
+ int ret;
+
+ *local_tree = NULL;
+ *remote_tree = NULL;
+
+ /* No original tree */
+ if (tree == NULL) {
+ return 0;
+ }
+
+ /* Generate local tree */
+ ret = map_subtree_select_local(module, local_ctx, local_tree, tree);
+ if (ret) {
+ return ret;
+ }
+
+ /* Generate remote tree */
+ ret = map_subtree_collect_remote(module, remote_ctx, remote_tree, tree);
+ if (ret) {
+ talloc_free(*local_tree);
+ return ret;
+ }
+
+ return 0;
+}
+
+/* Collect a list of attributes required either explicitly from a
+ * given list or implicitly from a given parse tree; split the
+ * collected list into local and remote parts. */
+static int map_attrs_collect_and_partition(struct ldb_module *module, struct map_context *ac,
+ const char * const *search_attrs,
+ const struct ldb_parse_tree *tree)
+{
+ void *tmp_ctx;
+ const char **tree_attrs;
+ const char **remote_attrs;
+ const char **local_attrs;
+ int ret;
+
+ /* There is no tree, just partition the searched attributes */
+ if (tree == NULL) {
+ ret = map_attrs_partition(module, ac,
+ &local_attrs, &remote_attrs, search_attrs);
+ if (ret == 0) {
+ ac->local_attrs = local_attrs;
+ ac->remote_attrs = remote_attrs;
+ ac->all_attrs = search_attrs;
+ }
+ return ret;
+ }
+
+ /* Create context for temporary memory */
+ tmp_ctx = talloc_new(ac);
+ if (tmp_ctx == NULL) {
+ goto oom;
+ }
+
+ /* Prepare list of attributes from tree */
+ tree_attrs = talloc_array(tmp_ctx, const char *, 1);
+ if (tree_attrs == NULL) {
+ talloc_free(tmp_ctx);
+ goto oom;
+ }
+ tree_attrs[0] = NULL;
+
+ /* Collect attributes from tree */
+ ret = ldb_parse_tree_collect_attrs(module, tmp_ctx, &tree_attrs, tree);
+ if (ret) {
+ goto done;
+ }
+
+ /* Merge attributes from search operation */
+ ret = map_attrs_merge(module, tmp_ctx, &tree_attrs, search_attrs);
+ if (ret) {
+ goto done;
+ }
+
+ /* Split local from remote attributes */
+ ret = map_attrs_partition(module, ac, &local_attrs,
+ &remote_attrs, tree_attrs);
+
+ if (ret == 0) {
+ ac->local_attrs = local_attrs;
+ ac->remote_attrs = remote_attrs;
+ talloc_steal(ac, tree_attrs);
+ ac->all_attrs = tree_attrs;
+ }
+done:
+ /* Free temporary memory */
+ talloc_free(tmp_ctx);
+ return ret;
+
+oom:
+ map_oom(module);
+ return -1;
+}
+
+
+/* Outbound requests: search
+ * ========================= */
+
+/* Pass a merged search result up the callback chain. */
+int map_up_callback(struct ldb_context *ldb, const struct ldb_request *req, struct ldb_reply *ares)
+{
+ int i;
+
+ /* No callback registered, stop */
+ if (req->callback == NULL) {
+ return LDB_SUCCESS;
+ }
+
+ /* Only records need special treatment */
+ if (ares->type != LDB_REPLY_ENTRY) {
+ return req->callback(ldb, req->context, ares);
+ }
+
+ /* Merged result doesn't match original query, skip */
+ if (!ldb_match_msg(ldb, ares->message, req->op.search.tree, req->op.search.base, req->op.search.scope)) {
+ ldb_debug(ldb, LDB_DEBUG_TRACE, "ldb_map: "
+ "Skipping record '%s': "
+ "doesn't match original search\n",
+ ldb_dn_get_linearized(ares->message->dn));
+ return LDB_SUCCESS;
+ }
+
+ /* Limit result to requested attrs */
+ if ((req->op.search.attrs) && (!ldb_attr_in_list(req->op.search.attrs, "*"))) {
+ for (i = 0; i < ares->message->num_elements; ) {
+ struct ldb_message_element *el = &ares->message->elements[i];
+ if (!ldb_attr_in_list(req->op.search.attrs, el->name)) {
+ ldb_msg_remove_element(ares->message, el);
+ } else {
+ i++;
+ }
+ }
+ }
+
+ return req->callback(ldb, req->context, ares);
+}
+
+/* Merge the remote and local parts of a search result. */
+int map_local_merge_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
+{
+ struct map_search_context *sc;
+ int ret;
+
+ if (context == NULL || ares == NULL) {
+ ldb_set_errstring(ldb, talloc_asprintf(ldb, "ldb_map: "
+ "NULL Context or Result in `map_local_merge_callback`"));
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ sc = talloc_get_type(context, struct map_search_context);
+
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+ /* We have already found a local record */
+ if (sc->local_res) {
+ ldb_set_errstring(ldb, talloc_asprintf(ldb, "ldb_map: "
+ "Too many results to base search for local entry"));
+ talloc_free(ares);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Store local result */
+ sc->local_res = ares;
+
+ /* Merge remote into local message */
+ ret = ldb_msg_merge_local(sc->ac->module, ares->message, sc->remote_res->message);
+ if (ret) {
+ talloc_free(ares);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ return map_up_callback(ldb, sc->ac->orig_req, ares);
+
+ case LDB_REPLY_DONE:
+ /* No local record found, continue with remote record */
+ if (sc->local_res == NULL) {
+ return map_up_callback(ldb, sc->ac->orig_req, sc->remote_res);
+ }
+ return LDB_SUCCESS;
+
+ default:
+ ldb_set_errstring(ldb, talloc_asprintf(ldb, "ldb_map: "
+ "Unexpected result type in base search for local entry"));
+ talloc_free(ares);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+}
+
+/* Search the local part of a remote search result. */
+int map_remote_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
+{
+ struct map_context *ac;
+ struct map_search_context *sc;
+ struct ldb_request *req;
+ int ret;
+
+ if (context == NULL || ares == NULL) {
+ ldb_set_errstring(ldb, talloc_asprintf(ldb, "ldb_map: "
+ "NULL Context or Result in `map_remote_search_callback`"));
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac = talloc_get_type(context, struct map_context);
+
+ /* It's not a record, stop searching */
+ if (ares->type != LDB_REPLY_ENTRY) {
+ return map_up_callback(ldb, ac->orig_req, ares);
+ }
+
+ /* Map result record into a local message */
+ ret = map_reply_remote(ac, ares);
+ if (ret) {
+ talloc_free(ares);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* There is no local db, stop searching */
+ if (!map_check_local_db(ac->module)) {
+ return map_up_callback(ldb, ac->orig_req, ares);
+ }
+
+ /* Prepare local search context */
+ sc = map_init_search_context(ac, ares);
+ if (sc == NULL) {
+ talloc_free(ares);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Prepare local search request */
+ /* TODO: use GUIDs here instead? */
+
+ ac->search_reqs = talloc_realloc(ac, ac->search_reqs, struct ldb_request *, ac->num_searches + 2);
+ if (ac->search_reqs == NULL) {
+ talloc_free(ares);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->search_reqs[ac->num_searches]
+ = req = map_search_base_req(ac, ares->message->dn,
+ NULL, NULL, sc, map_local_merge_callback);
+ if (req == NULL) {
+ talloc_free(sc);
+ talloc_free(ares);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ac->num_searches++;
+ ac->search_reqs[ac->num_searches] = NULL;
+
+ return ldb_next_request(ac->module, req);
+}
+
+/* Search a record. */
+int map_search(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_handle *h;
+ struct map_context *ac;
+ struct ldb_parse_tree *local_tree, *remote_tree;
+ int ret;
+
+ const char *wildcard[] = { "*", NULL };
+ const char * const *attrs;
+
+ if (!module->private_data) /* if we're not yet initialized, go to the next module */
+ return ldb_next_request(module, req);
+
+ /* Do not manipulate our control entries */
+ if (ldb_dn_is_special(req->op.search.base))
+ return ldb_next_request(module, req);
+
+ /* No mapping requested, skip to next module */
+ if ((req->op.search.base) && (!ldb_dn_check_local(module, req->op.search.base))) {
+ return ldb_next_request(module, req);
+ }
+
+ /* TODO: How can we be sure about which partition we are
+ * targetting when there is no search base? */
+
+ /* Prepare context and handle */
+ h = map_init_handle(req, module);
+ if (h == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ac = talloc_get_type(h->private_data, struct map_context);
+
+ ac->search_reqs = talloc_array(ac, struct ldb_request *, 2);
+ if (ac->search_reqs == NULL) {
+ talloc_free(h);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ac->num_searches = 1;
+ ac->search_reqs[1] = NULL;
+
+ /* Prepare the remote operation */
+ ac->search_reqs[0] = talloc(ac, struct ldb_request);
+ if (ac->search_reqs[0] == NULL) {
+ goto oom;
+ }
+
+ *(ac->search_reqs[0]) = *req; /* copy the request */
+
+ ac->search_reqs[0]->handle = h; /* return our own handle to deal with this call */
+
+ ac->search_reqs[0]->context = ac;
+ ac->search_reqs[0]->callback = map_remote_search_callback;
+
+ /* It is easier to deal with the two different ways of
+ * expressing the wildcard in the same codepath */
+ attrs = req->op.search.attrs;
+ if (attrs == NULL) {
+ attrs = wildcard;
+ }
+
+ /* Split local from remote attrs */
+ ret = map_attrs_collect_and_partition(module, ac,
+ attrs, req->op.search.tree);
+ if (ret) {
+ goto failed;
+ }
+
+ ac->search_reqs[0]->op.search.attrs = ac->remote_attrs;
+
+ /* Split local from remote tree */
+ ret = ldb_parse_tree_partition(module, ac, ac->search_reqs[0],
+ &local_tree, &remote_tree,
+ req->op.search.tree);
+ if (ret) {
+ goto failed;
+ }
+
+ if (((local_tree != NULL) && (remote_tree != NULL)) &&
+ (!ldb_parse_tree_check_splittable(req->op.search.tree))) {
+ /* The query can't safely be split, enumerate the remote partition */
+ local_tree = NULL;
+ remote_tree = NULL;
+ }
+
+ if (local_tree == NULL) {
+ /* Construct default local parse tree */
+ local_tree = talloc_zero(ac, struct ldb_parse_tree);
+ if (local_tree == NULL) {
+ map_oom(ac->module);
+ goto failed;
+ }
+
+ local_tree->operation = LDB_OP_PRESENT;
+ local_tree->u.present.attr = talloc_strdup(local_tree, IS_MAPPED);
+ }
+ if (remote_tree == NULL) {
+ /* Construct default remote parse tree */
+ remote_tree = ldb_parse_tree(ac->search_reqs[0], NULL);
+ if (remote_tree == NULL) {
+ goto failed;
+ }
+ }
+
+ ac->local_tree = local_tree;
+ ac->search_reqs[0]->op.search.tree = remote_tree;
+
+ ldb_set_timeout_from_prev_req(module->ldb, req, ac->search_reqs[0]);
+
+ h->state = LDB_ASYNC_INIT;
+ h->status = LDB_SUCCESS;
+
+ ac->step = MAP_SEARCH_REMOTE;
+
+ ret = ldb_next_remote_request(module, ac->search_reqs[0]);
+ if (ret == LDB_SUCCESS) {
+ req->handle = h;
+ }
+ return ret;
+
+oom:
+ map_oom(module);
+failed:
+ talloc_free(h);
+ return LDB_ERR_OPERATIONS_ERROR;
+}
diff --git a/ldb/ldb_map/ldb_map_private.h b/ldb/ldb_map/ldb_map_private.h
new file mode 100644
index 00000000..58a9f270
--- /dev/null
+++ b/ldb/ldb_map/ldb_map_private.h
@@ -0,0 +1,110 @@
+
+/* A handy macro to report Out of Memory conditions */
+#define map_oom(module) ldb_set_errstring(module->ldb, talloc_asprintf(module, "Out of Memory"));
+
+/* The type of search callback functions */
+typedef int (*ldb_search_callback)(struct ldb_context *, void *, struct ldb_reply *);
+
+/* The special DN from which the local and remote base DNs are fetched */
+#define MAP_DN_NAME "@MAP"
+#define MAP_DN_FROM "@FROM"
+#define MAP_DN_TO "@TO"
+
+/* Private data structures
+ * ======================= */
+
+/* Context data for mapped requests */
+struct map_context {
+ enum map_step {
+ MAP_SEARCH_REMOTE,
+ MAP_ADD_REMOTE,
+ MAP_ADD_LOCAL,
+ MAP_SEARCH_SELF_MODIFY,
+ MAP_MODIFY_REMOTE,
+ MAP_MODIFY_LOCAL,
+ MAP_SEARCH_SELF_DELETE,
+ MAP_DELETE_REMOTE,
+ MAP_DELETE_LOCAL,
+ MAP_SEARCH_SELF_RENAME,
+ MAP_RENAME_REMOTE,
+ MAP_RENAME_FIXUP,
+ MAP_RENAME_LOCAL
+ } step;
+
+ struct ldb_module *module;
+
+ struct ldb_dn *local_dn;
+ const struct ldb_parse_tree *local_tree;
+ const char * const *local_attrs;
+ const char * const *remote_attrs;
+ const char * const *all_attrs;
+
+ struct ldb_request *orig_req;
+ struct ldb_request *local_req;
+ struct ldb_request *remote_req;
+ struct ldb_request *down_req;
+ struct ldb_request *search_req;
+
+ /* for search, we may have a lot of contexts */
+ int num_searches;
+ struct ldb_request **search_reqs;
+};
+
+/* Context data for mapped search requests */
+struct map_search_context {
+ struct map_context *ac;
+ struct ldb_reply *local_res;
+ struct ldb_reply *remote_res;
+};
+
+
+/* Common operations
+ * ================= */
+
+/* The following definitions come from lib/ldb/modules/ldb_map.c */
+const struct ldb_map_context *map_get_context(struct ldb_module *module);
+struct map_search_context *map_init_search_context(struct map_context *ac, struct ldb_reply *ares);
+struct ldb_handle *map_init_handle(struct ldb_request *req, struct ldb_module *module);
+
+int ldb_next_remote_request(struct ldb_module *module, struct ldb_request *request);
+
+bool map_check_local_db(struct ldb_module *module);
+bool map_attr_check_remote(const struct ldb_map_context *data, const char *attr);
+bool ldb_dn_check_local(struct ldb_module *module, struct ldb_dn *dn);
+
+const struct ldb_map_attribute *map_attr_find_local(const struct ldb_map_context *data, const char *name);
+const struct ldb_map_attribute *map_attr_find_remote(const struct ldb_map_context *data, const char *name);
+
+const char *map_attr_map_local(void *mem_ctx, const struct ldb_map_attribute *map, const char *attr);
+const char *map_attr_map_remote(void *mem_ctx, const struct ldb_map_attribute *map, const char *attr);
+int map_attrs_merge(struct ldb_module *module, void *mem_ctx, const char ***attrs, const char * const *more_attrs);
+
+struct ldb_val ldb_val_map_local(struct ldb_module *module, void *mem_ctx, const struct ldb_map_attribute *map, const struct ldb_val *val);
+struct ldb_val ldb_val_map_remote(struct ldb_module *module, void *mem_ctx, const struct ldb_map_attribute *map, const struct ldb_val *val);
+
+struct ldb_dn *ldb_dn_map_local(struct ldb_module *module, void *mem_ctx, struct ldb_dn *dn);
+struct ldb_dn *ldb_dn_map_remote(struct ldb_module *module, void *mem_ctx, struct ldb_dn *dn);
+struct ldb_dn *ldb_dn_map_rebase_remote(struct ldb_module *module, void *mem_ctx, struct ldb_dn *dn);
+
+struct ldb_request *map_search_base_req(struct map_context *ac, struct ldb_dn *dn, const char * const *attrs, const struct ldb_parse_tree *tree, void *context, ldb_search_callback callback);
+struct ldb_request *map_search_self_req(struct map_context *ac, struct ldb_dn *dn);
+struct ldb_request *map_build_fixup_req(struct map_context *ac, struct ldb_dn *olddn, struct ldb_dn *newdn);
+
+int map_subtree_collect_remote_simple(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree, const struct ldb_map_attribute *map);
+
+/* LDB Requests
+ * ============ */
+
+/* The following definitions come from lib/ldb/modules/ldb_map_inbound.c */
+int map_add_do_remote(struct ldb_handle *handle);
+int map_add_do_local(struct ldb_handle *handle);
+
+int map_modify_do_remote(struct ldb_handle *handle);
+int map_modify_do_local(struct ldb_handle *handle);
+
+int map_delete_do_remote(struct ldb_handle *handle);
+int map_delete_do_local(struct ldb_handle *handle);
+
+int map_rename_do_remote(struct ldb_handle *handle);
+int map_rename_do_fixup(struct ldb_handle *handle);
+int map_rename_do_local(struct ldb_handle *handle);