4 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007
5 Copyright (C) Simo Sorce <idra@samba.org> 2008
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 * Component: ldb linked_attributes module
26 * Description: Module to ensure linked attribute pairs remain in sync
28 * Author: Andrew Bartlett
32 #include "ldb_module.h"
33 #include "util/dlinklist.h"
34 #include "dsdb/samdb/samdb.h"
35 #include "librpc/gen_ndr/ndr_misc.h"
36 #include "dsdb/samdb/ldb_modules/util.h"
39 struct la_context *la_list;
43 struct la_op_store *next;
44 struct la_op_store *prev;
45 enum la_op {LA_OP_ADD, LA_OP_DEL} op;
51 struct replace_context {
52 struct la_context *ac;
53 unsigned int num_elements;
54 struct ldb_message_element *el;
58 struct la_context *next, *prev;
59 const struct dsdb_schema *schema;
60 struct ldb_module *module;
61 struct ldb_request *req;
62 struct ldb_dn *add_dn;
63 struct ldb_dn *del_dn;
64 struct replace_context *rc;
65 struct la_op_store *ops;
66 struct ldb_extended *op_response;
67 struct ldb_control **op_controls;
70 static struct la_context *linked_attributes_init(struct ldb_module *module,
71 struct ldb_request *req)
73 struct ldb_context *ldb;
74 struct la_context *ac;
76 ldb = ldb_module_get_ctx(module);
78 ac = talloc_zero(req, struct la_context);
84 ac->schema = dsdb_get_schema(ldb, ac);
94 static int la_guid_from_dn(struct la_context *ac, struct ldb_dn *dn, struct GUID *guid)
99 status = dsdb_get_extended_dn_guid(dn, guid, "GUID");
100 if (NT_STATUS_IS_OK(status)) {
103 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
104 DEBUG(4,(__location__ ": Unable to parse GUID for dn %s\n",
105 ldb_dn_get_linearized(dn)));
106 return LDB_ERR_OPERATIONS_ERROR;
109 ret = dsdb_find_guid_by_dn(ldb_module_get_ctx(ac->module), dn, guid);
110 if (ret != LDB_SUCCESS) {
111 DEBUG(4,(__location__ ": Failed to find GUID for dn %s\n",
112 ldb_dn_get_linearized(dn)));
119 /* Common routine to handle reading the attributes and creating a
120 * series of modify requests */
121 static int la_store_op(struct la_context *ac,
122 enum la_op op, struct ldb_val *dn,
125 struct ldb_context *ldb;
126 struct la_op_store *os;
127 struct ldb_dn *op_dn;
130 ldb = ldb_module_get_ctx(ac->module);
132 op_dn = ldb_dn_from_ldb_val(ac, ldb, dn);
134 ldb_asprintf_errstring(ldb,
135 "could not parse attribute as a DN");
136 return LDB_ERR_INVALID_DN_SYNTAX;
139 os = talloc_zero(ac, struct la_op_store);
142 return LDB_ERR_OPERATIONS_ERROR;
147 ret = la_guid_from_dn(ac, op_dn, &os->guid);
149 if (ret == LDB_ERR_NO_SUCH_OBJECT && ac->req->operation == LDB_DELETE) {
150 /* we are deleting an object, and we've found it has a
151 * forward link to a target that no longer
152 * exists. This is not an error in the delete, and we
153 * should just not do the deferred delete of the
159 if (ret != LDB_SUCCESS) {
163 os->name = talloc_strdup(os, name);
166 return LDB_ERR_OPERATIONS_ERROR;
169 /* Do deletes before adds */
170 if (op == LA_OP_ADD) {
171 DLIST_ADD_END(ac->ops, os, struct la_op_store *);
173 /* By adding to the head of the list, we do deletes before
174 * adds when processing a replace */
175 DLIST_ADD(ac->ops, os);
181 static int la_queue_mod_request(struct la_context *ac);
182 static int la_down_req(struct la_context *ac);
187 static int linked_attributes_add(struct ldb_module *module, struct ldb_request *req)
189 struct ldb_context *ldb;
190 const struct dsdb_attribute *target_attr;
191 struct la_context *ac;
192 const char *attr_name;
193 struct ldb_control *ctrl;
197 ldb = ldb_module_get_ctx(module);
199 if (ldb_dn_is_special(req->op.add.message->dn)) {
200 /* do not manipulate our control entries */
201 return ldb_next_request(module, req);
204 if (!(ctrl = ldb_request_get_control(req, DSDB_CONTROL_APPLY_LINKS))) {
205 /* don't do anything special for linked attributes, repl_meta_data has done it */
206 return ldb_next_request(module, req);
208 ctrl->critical = false;
210 ac = linked_attributes_init(module, req);
212 return LDB_ERR_OPERATIONS_ERROR;
216 /* without schema, this doesn't make any sense */
218 return ldb_next_request(module, req);
221 /* Need to ensure we only have forward links being specified */
222 for (i=0; i < req->op.add.message->num_elements; i++) {
223 const struct ldb_message_element *el = &req->op.add.message->elements[i];
224 const struct dsdb_attribute *schema_attr
225 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
227 ldb_asprintf_errstring(ldb,
228 "attribute %s is not a valid attribute in schema", el->name);
229 return LDB_ERR_OBJECT_CLASS_VIOLATION;
231 /* We have a valid attribute, now find out if it is a forward link */
232 if ((schema_attr->linkID == 0)) {
236 if ((schema_attr->linkID & 1) == 1) {
237 unsigned int functional_level;
239 functional_level = dsdb_functional_level(ldb);
240 SMB_ASSERT(functional_level > DS_DOMAIN_FUNCTION_2000);
243 /* Even link IDs are for the originating attribute */
244 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
247 * windows 2003 has a broken schema where
248 * the definition of msDS-IsDomainFor
249 * is missing (which is supposed to be
250 * the backlink of the msDS-HasDomainNCs
256 attr_name = target_attr->lDAPDisplayName;
258 for (j = 0; j < el->num_values; j++) {
259 ret = la_store_op(ac, LA_OP_ADD,
262 if (ret != LDB_SUCCESS) {
268 /* if no linked attributes are present continue */
269 if (ac->ops == NULL) {
270 /* nothing to do for this module, proceed */
272 return ldb_next_request(module, req);
275 /* start with the original request */
276 return la_down_req(ac);
279 /* For a delete or rename, we need to find out what linked attributes
280 * are currently on this DN, and then deal with them. This is the
281 * callback to the base search */
283 static int la_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares)
285 struct ldb_context *ldb;
286 const struct dsdb_attribute *schema_attr;
287 const struct dsdb_attribute *target_attr;
288 struct ldb_message_element *search_el;
289 struct replace_context *rc;
290 struct la_context *ac;
291 const char *attr_name;
293 int ret = LDB_SUCCESS;
295 ac = talloc_get_type(req->context, struct la_context);
296 ldb = ldb_module_get_ctx(ac->module);
300 return ldb_module_done(ac->req, NULL, NULL,
301 LDB_ERR_OPERATIONS_ERROR);
303 if (ares->error != LDB_SUCCESS) {
304 return ldb_module_done(ac->req, ares->controls,
305 ares->response, ares->error);
308 /* Only entries are interesting, and we only want the olddn */
309 switch (ares->type) {
310 case LDB_REPLY_ENTRY:
312 if (ldb_dn_compare(ares->message->dn, ac->req->op.mod.message->dn) != 0) {
313 ldb_asprintf_errstring(ldb,
314 "linked_attributes: %s is not the DN we were looking for",
315 ldb_dn_get_linearized(ares->message->dn));
316 /* Guh? We only asked for this DN */
318 return ldb_module_done(ac->req, NULL, NULL,
319 LDB_ERR_OPERATIONS_ERROR);
322 ac->add_dn = ac->del_dn = talloc_steal(ac, ares->message->dn);
324 /* We don't populate 'rc' for ADD - it can't be deleting elements anyway */
325 for (i = 0; rc && i < rc->num_elements; i++) {
327 schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rc->el[i].name);
329 ldb_asprintf_errstring(ldb,
330 "attribute %s is not a valid attribute in schema",
333 return ldb_module_done(ac->req, NULL, NULL,
334 LDB_ERR_OBJECT_CLASS_VIOLATION);
337 search_el = ldb_msg_find_element(ares->message,
340 /* See if this element already exists */
341 /* otherwise just ignore as
342 * the add has already been scheduled */
347 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
350 * windows 2003 has a broken schema where
351 * the definition of msDS-IsDomainFor
352 * is missing (which is supposed to be
353 * the backlink of the msDS-HasDomainNCs
358 attr_name = target_attr->lDAPDisplayName;
360 /* Now we know what was there, we can remove it for the re-add */
361 for (j = 0; j < search_el->num_values; j++) {
362 ret = la_store_op(ac, LA_OP_DEL,
363 &search_el->values[j],
365 if (ret != LDB_SUCCESS) {
367 return ldb_module_done(ac->req,
375 case LDB_REPLY_REFERRAL:
383 if (ac->req->operation == LDB_ADD) {
384 /* Start the modifies to the backlinks */
385 ret = la_queue_mod_request(ac);
387 if (ret != LDB_SUCCESS) {
388 return ldb_module_done(ac->req, NULL, NULL,
392 /* Start with the original request */
393 ret = la_down_req(ac);
394 if (ret != LDB_SUCCESS) {
395 return ldb_module_done(ac->req, NULL, NULL, ret);
407 static int linked_attributes_modify(struct ldb_module *module, struct ldb_request *req)
409 /* Look over list of modifications */
410 /* Find if any are for linked attributes */
411 /* Determine the effect of the modification */
412 /* Apply the modify to the linked entry */
414 struct ldb_context *ldb;
416 struct la_context *ac;
417 struct ldb_request *search_req;
419 struct ldb_control *ctrl;
423 ldb = ldb_module_get_ctx(module);
425 if (ldb_dn_is_special(req->op.mod.message->dn)) {
426 /* do not manipulate our control entries */
427 return ldb_next_request(module, req);
430 if (!(ctrl = ldb_request_get_control(req, DSDB_CONTROL_APPLY_LINKS))) {
431 /* don't do anything special for linked attributes, repl_meta_data has done it */
432 return ldb_next_request(module, req);
434 ctrl->critical = false;
436 ac = linked_attributes_init(module, req);
438 return LDB_ERR_OPERATIONS_ERROR;
442 /* without schema, this doesn't make any sense */
443 return ldb_next_request(module, req);
446 ac->rc = talloc_zero(ac, struct replace_context);
449 return LDB_ERR_OPERATIONS_ERROR;
452 for (i=0; i < req->op.mod.message->num_elements; i++) {
453 bool store_el = false;
454 const char *attr_name;
455 const struct dsdb_attribute *target_attr;
456 const struct ldb_message_element *el = &req->op.mod.message->elements[i];
457 const struct dsdb_attribute *schema_attr
458 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
460 ldb_asprintf_errstring(ldb,
461 "attribute %s is not a valid attribute in schema", el->name);
462 return LDB_ERR_OBJECT_CLASS_VIOLATION;
464 /* We have a valid attribute, now find out if it is a forward link
465 (Even link IDs are for the originating attribute) */
466 if (schema_attr->linkID == 0) {
470 if ((schema_attr->linkID & 1) == 1) {
471 unsigned int functional_level;
473 functional_level = dsdb_functional_level(ldb);
474 SMB_ASSERT(functional_level > DS_DOMAIN_FUNCTION_2000);
476 /* Now find the target attribute */
477 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
480 * windows 2003 has a broken schema where
481 * the definition of msDS-IsDomainFor
482 * is missing (which is supposed to be
483 * the backlink of the msDS-HasDomainNCs
489 attr_name = target_attr->lDAPDisplayName;
491 switch (el->flags & LDB_FLAG_MOD_MASK) {
492 case LDB_FLAG_MOD_REPLACE:
493 /* treat as just a normal add the delete part is handled by the callback */
496 /* break intentionally missing */
498 case LDB_FLAG_MOD_ADD:
500 /* For each value being added, we need to setup the adds */
501 for (j = 0; j < el->num_values; j++) {
502 ret = la_store_op(ac, LA_OP_ADD,
505 if (ret != LDB_SUCCESS) {
511 case LDB_FLAG_MOD_DELETE:
513 if (el->num_values) {
514 /* For each value being deleted, we need to setup the delete */
515 for (j = 0; j < el->num_values; j++) {
516 ret = la_store_op(ac, LA_OP_DEL,
519 if (ret != LDB_SUCCESS) {
524 /* Flag that there was a DELETE
525 * without a value specified, so we
526 * need to look for the old value */
534 struct ldb_message_element *search_el;
536 search_el = talloc_realloc(ac->rc, ac->rc->el,
537 struct ldb_message_element,
538 ac->rc->num_elements +1);
541 return LDB_ERR_OPERATIONS_ERROR;
543 ac->rc->el = search_el;
545 ac->rc->el[ac->rc->num_elements] = *el;
546 ac->rc->num_elements++;
550 if (ac->ops || ac->rc->el) {
551 /* both replace and delete without values are handled in the callback
552 * after the search on the entry to be modified is performed */
554 attrs = talloc_array(ac->rc, const char *, ac->rc->num_elements + 1);
557 return LDB_ERR_OPERATIONS_ERROR;
559 for (i = 0; ac->rc && i < ac->rc->num_elements; i++) {
560 attrs[i] = ac->rc->el[i].name;
564 /* The callback does all the hard work here */
565 ret = ldb_build_search_req(&search_req, ldb, ac,
566 req->op.mod.message->dn,
568 "(objectClass=*)", attrs,
570 ac, la_mod_search_callback,
573 /* We need to figure out our own extended DN, to fill in as the backlink target */
574 if (ret == LDB_SUCCESS) {
575 ret = ldb_request_add_control(search_req,
576 LDB_CONTROL_EXTENDED_DN_OID,
579 if (ret == LDB_SUCCESS) {
580 talloc_steal(search_req, attrs);
582 ret = ldb_next_request(module, search_req);
586 /* nothing to do for this module, proceed */
588 ret = ldb_next_request(module, req);
594 static int linked_attributes_fix_links(struct ldb_module *module,
595 struct ldb_dn *old_dn, struct ldb_dn *new_dn,
596 struct ldb_message_element *el, struct dsdb_schema *schema,
597 const struct dsdb_attribute *schema_attr)
600 TALLOC_CTX *tmp_ctx = talloc_new(module);
601 struct ldb_context *ldb = ldb_module_get_ctx(module);
602 const struct dsdb_attribute *target;
603 const char *attrs[2];
605 target = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
606 if (target == NULL) {
607 /* there is no counterpart link to change */
611 attrs[0] = target->lDAPDisplayName;
614 for (i=0; i<el->num_values; i++) {
615 struct dsdb_dn *dsdb_dn;
618 struct ldb_result *res;
619 struct ldb_message *msg;
620 struct ldb_message_element *el2;
622 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], schema_attr->syntax->ldap_oid);
623 if (dsdb_dn == NULL) {
624 talloc_free(tmp_ctx);
625 return LDB_ERR_INVALID_DN_SYNTAX;
628 ret = dsdb_module_search_dn(module, tmp_ctx, &res, dsdb_dn->dn,
630 DSDB_SEARCH_SHOW_DELETED |
631 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
632 DSDB_SEARCH_REVEAL_INTERNALS);
633 if (ret != LDB_SUCCESS) {
634 ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - remote not found - %s",
635 el->name, target->lDAPDisplayName,
636 ldb_dn_get_linearized(old_dn),
637 ldb_dn_get_linearized(dsdb_dn->dn),
639 talloc_free(tmp_ctx);
644 if (msg->num_elements != 1 ||
645 ldb_attr_cmp(msg->elements[0].name, target->lDAPDisplayName) != 0) {
646 ldb_set_errstring(ldb, "Bad msg elements in linked_attributes_fix_links");
647 talloc_free(tmp_ctx);
648 return LDB_ERR_OPERATIONS_ERROR;
650 el2 = &msg->elements[0];
652 el2->flags = LDB_FLAG_MOD_REPLACE;
654 /* find our DN in the values */
655 for (j=0; j<el2->num_values; j++) {
656 struct dsdb_dn *dsdb_dn2;
657 dsdb_dn2 = dsdb_dn_parse(msg, ldb, &el2->values[j], target->syntax->ldap_oid);
658 if (dsdb_dn2 == NULL) {
659 talloc_free(tmp_ctx);
660 return LDB_ERR_INVALID_DN_SYNTAX;
662 if (ldb_dn_compare(old_dn, dsdb_dn2->dn) != 0) {
665 ret = ldb_dn_update_components(dsdb_dn2->dn, new_dn);
666 if (ret != LDB_SUCCESS) {
667 talloc_free(tmp_ctx);
671 el2->values[j] = data_blob_string_const(
672 dsdb_dn_get_extended_linearized(el2->values, dsdb_dn2, 1));
675 ret = dsdb_check_single_valued_link(target, el2);
676 if (ret != LDB_SUCCESS) {
677 talloc_free(tmp_ctx);
681 ret = dsdb_module_modify(module, msg, DSDB_MODIFY_RELAX);
682 if (ret != LDB_SUCCESS) {
683 ldb_asprintf_errstring(ldb, "Linked attribute %s->%s between %s and %s - update failed - %s",
684 el->name, target->lDAPDisplayName,
685 ldb_dn_get_linearized(old_dn),
686 ldb_dn_get_linearized(dsdb_dn->dn),
688 talloc_free(tmp_ctx);
693 talloc_free(tmp_ctx);
699 static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req)
701 struct ldb_result *res;
702 struct ldb_message *msg;
705 struct ldb_context *ldb = ldb_module_get_ctx(module);
706 struct dsdb_schema *schema;
708 - load the current msg
709 - find any linked attributes
710 - if its a link then find the target object
711 - modify the target linked attributes with the new DN
713 ret = dsdb_module_search_dn(module, req, &res, req->op.rename.olddn,
714 NULL, DSDB_SEARCH_SHOW_DELETED);
715 if (ret != LDB_SUCCESS) {
719 schema = dsdb_get_schema(ldb, res);
722 return LDB_ERR_OPERATIONS_ERROR;
727 for (i=0; i<msg->num_elements; i++) {
728 struct ldb_message_element *el = &msg->elements[i];
729 const struct dsdb_attribute *schema_attr
730 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
731 if (!schema_attr || schema_attr->linkID == 0) {
734 ret = linked_attributes_fix_links(module, msg->dn, req->op.rename.newdn, el,
735 schema, schema_attr);
736 if (ret != LDB_SUCCESS) {
744 return ldb_next_request(module, req);
748 /* queue a linked attributes modify request in the la_private
750 static int la_queue_mod_request(struct la_context *ac)
752 struct la_private *la_private =
753 talloc_get_type(ldb_module_get_private(ac->module), struct la_private);
755 if (la_private == NULL) {
756 ldb_debug(ldb_module_get_ctx(ac->module), LDB_DEBUG_ERROR, __location__ ": No la_private transaction setup\n");
757 return LDB_ERR_OPERATIONS_ERROR;
760 talloc_steal(la_private, ac);
761 DLIST_ADD(la_private->la_list, ac);
763 return ldb_module_done(ac->req, ac->op_controls,
764 ac->op_response, LDB_SUCCESS);
767 /* Having done the original operation, then try to fix up all the linked attributes for modify and delete */
768 static int la_mod_del_callback(struct ldb_request *req, struct ldb_reply *ares)
771 struct la_context *ac;
772 struct ldb_context *ldb;
774 ac = talloc_get_type(req->context, struct la_context);
775 ldb = ldb_module_get_ctx(ac->module);
778 return ldb_module_done(ac->req, NULL, NULL,
779 LDB_ERR_OPERATIONS_ERROR);
781 if (ares->error != LDB_SUCCESS) {
782 return ldb_module_done(ac->req, ares->controls,
783 ares->response, ares->error);
786 if (ares->type != LDB_REPLY_DONE) {
787 ldb_set_errstring(ldb,
788 "invalid ldb_reply_type in callback");
790 return ldb_module_done(ac->req, NULL, NULL,
791 LDB_ERR_OPERATIONS_ERROR);
794 ac->op_controls = talloc_steal(ac, ares->controls);
795 ac->op_response = talloc_steal(ac, ares->response);
797 /* If we have modfies to make, this is the time to do them for modify and delete */
798 ret = la_queue_mod_request(ac);
800 if (ret != LDB_SUCCESS) {
801 return ldb_module_done(ac->req, NULL, NULL, ret);
805 /* la_queue_mod_request has already sent the callbacks */
810 /* Having done the original add, then try to fix up all the linked attributes
812 This is done after the add so the links can get the extended DNs correctly.
814 static int la_add_callback(struct ldb_request *req, struct ldb_reply *ares)
817 struct la_context *ac;
818 struct ldb_context *ldb;
820 ac = talloc_get_type(req->context, struct la_context);
821 ldb = ldb_module_get_ctx(ac->module);
824 return ldb_module_done(ac->req, NULL, NULL,
825 LDB_ERR_OPERATIONS_ERROR);
827 if (ares->error != LDB_SUCCESS) {
828 return ldb_module_done(ac->req, ares->controls,
829 ares->response, ares->error);
832 if (ares->type != LDB_REPLY_DONE) {
833 ldb_set_errstring(ldb,
834 "invalid ldb_reply_type in callback");
836 return ldb_module_done(ac->req, NULL, NULL,
837 LDB_ERR_OPERATIONS_ERROR);
841 struct ldb_request *search_req;
842 static const char *attrs[] = { NULL };
844 /* The callback does all the hard work here - we need
845 * the objectGUID and SID of the added record */
846 ret = ldb_build_search_req(&search_req, ldb, ac,
847 ac->req->op.add.message->dn,
849 "(objectClass=*)", attrs,
851 ac, la_mod_search_callback,
854 if (ret == LDB_SUCCESS) {
855 ret = ldb_request_add_control(search_req,
856 LDB_CONTROL_EXTENDED_DN_OID,
859 if (ret != LDB_SUCCESS) {
860 return ldb_module_done(ac->req, NULL, NULL,
864 ac->op_controls = talloc_steal(ac, ares->controls);
865 ac->op_response = talloc_steal(ac, ares->response);
867 return ldb_next_request(ac->module, search_req);
870 return ldb_module_done(ac->req, ares->controls,
871 ares->response, ares->error);
875 /* Reconstruct the original request, but pointing at our local callback to finish things off */
876 static int la_down_req(struct la_context *ac)
878 struct ldb_request *down_req;
880 struct ldb_context *ldb;
882 ldb = ldb_module_get_ctx(ac->module);
884 switch (ac->req->operation) {
886 ret = ldb_build_add_req(&down_req, ldb, ac,
887 ac->req->op.add.message,
893 ret = ldb_build_mod_req(&down_req, ldb, ac,
894 ac->req->op.mod.message,
896 ac, la_mod_del_callback,
900 ret = LDB_ERR_OPERATIONS_ERROR;
902 if (ret != LDB_SUCCESS) {
906 return ldb_next_request(ac->module, down_req);
910 use the GUID part of an extended DN to find the target DN, in case
913 static int la_find_dn_target(struct ldb_module *module, struct la_context *ac,
914 struct GUID *guid, struct ldb_dn **dn)
916 return dsdb_find_dn_by_guid(ldb_module_get_ctx(ac->module), ac, guid, dn);
919 /* apply one la_context op change */
920 static int la_do_op_request(struct ldb_module *module, struct la_context *ac, struct la_op_store *op)
922 struct ldb_message_element *ret_el;
923 struct ldb_message *new_msg;
924 struct ldb_context *ldb;
927 ldb = ldb_module_get_ctx(ac->module);
929 /* Create the modify request */
930 new_msg = ldb_msg_new(ac);
933 return LDB_ERR_OPERATIONS_ERROR;
936 ret = la_find_dn_target(module, ac, &op->guid, &new_msg->dn);
937 if (ret != LDB_SUCCESS) {
941 if (op->op == LA_OP_ADD) {
942 ret = ldb_msg_add_empty(new_msg, op->name,
943 LDB_FLAG_MOD_ADD, &ret_el);
945 ret = ldb_msg_add_empty(new_msg, op->name,
946 LDB_FLAG_MOD_DELETE, &ret_el);
948 if (ret != LDB_SUCCESS) {
951 ret_el->values = talloc_array(new_msg, struct ldb_val, 1);
952 if (!ret_el->values) {
954 return LDB_ERR_OPERATIONS_ERROR;
956 ret_el->num_values = 1;
957 if (op->op == LA_OP_ADD) {
958 ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->add_dn, 1));
960 ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->del_dn, 1));
964 ldb_debug(ldb, LDB_DEBUG_WARNING,
965 "link on %s %s: %s %s\n",
966 ldb_dn_get_linearized(new_msg->dn), ret_el->name,
967 ret_el->values[0].data, ac->ops->op == LA_OP_ADD ? "added" : "deleted");
971 DEBUG(4,("Applying linked attribute change:\n%s\n",
972 ldb_ldif_message_string(ldb, op, LDB_CHANGETYPE_MODIFY, new_msg)));
975 ret = dsdb_module_modify(module, new_msg, 0);
976 if (ret != LDB_SUCCESS) {
977 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
979 ldb_ldif_message_string(ldb, op, LDB_CHANGETYPE_MODIFY, new_msg));
985 /* apply one set of la_context changes */
986 static int la_do_mod_request(struct ldb_module *module, struct la_context *ac)
988 struct la_op_store *op;
990 for (op = ac->ops; op; op=op->next) {
991 int ret = la_do_op_request(module, ac, op);
992 if (ret != LDB_SUCCESS) {
993 if (ret != LDB_ERR_NO_SUCH_OBJECT) {
1004 we hook into the transaction operations to allow us to
1005 perform the linked attribute updates at the end of the whole
1006 transaction. This allows a forward linked attribute to be created
1007 before the target is created, as long as the target is created
1008 in the same transaction
1010 static int linked_attributes_start_transaction(struct ldb_module *module)
1012 /* create our private structure for this transaction */
1013 struct la_private *la_private = talloc_get_type(ldb_module_get_private(module),
1015 talloc_free(la_private);
1016 la_private = talloc(module, struct la_private);
1017 if (la_private == NULL) {
1018 return LDB_ERR_OPERATIONS_ERROR;
1020 la_private->la_list = NULL;
1021 ldb_module_set_private(module, la_private);
1022 return ldb_next_start_trans(module);
1026 on prepare commit we loop over our queued la_context structures
1027 and apply each of them
1029 static int linked_attributes_prepare_commit(struct ldb_module *module)
1031 struct la_private *la_private =
1032 talloc_get_type(ldb_module_get_private(module), struct la_private);
1033 struct la_context *ac;
1036 /* prepare commit without begin_transaction - let someone else return the error, just don't segfault */
1037 return ldb_next_prepare_commit(module);
1039 /* walk the list backwards, to do the first entry first, as we
1040 * added the entries with DLIST_ADD() which puts them at the
1041 * start of the list */
1043 /* Start at the end of the list - so we can start
1044 * there, but ensure we don't create a loop by NULLing
1045 * it out in the first element */
1046 ac = DLIST_TAIL(la_private->la_list);
1048 for (; ac; ac=DLIST_PREV(ac)) {
1051 ret = la_do_mod_request(module, ac);
1052 if (ret != LDB_SUCCESS) {
1053 DEBUG(0,(__location__ ": Failed mod request ret=%d\n", ret));
1054 talloc_free(la_private);
1055 ldb_module_set_private(module, NULL);
1060 talloc_free(la_private);
1061 ldb_module_set_private(module, NULL);
1063 return ldb_next_prepare_commit(module);
1066 static int linked_attributes_del_transaction(struct ldb_module *module)
1068 struct la_private *la_private =
1069 talloc_get_type(ldb_module_get_private(module), struct la_private);
1070 talloc_free(la_private);
1071 ldb_module_set_private(module, NULL);
1072 return ldb_next_del_trans(module);
1076 _PUBLIC_ const struct ldb_module_ops ldb_linked_attributes_module_ops = {
1077 .name = "linked_attributes",
1078 .add = linked_attributes_add,
1079 .modify = linked_attributes_modify,
1080 .rename = linked_attributes_rename,
1081 .start_transaction = linked_attributes_start_transaction,
1082 .prepare_commit = linked_attributes_prepare_commit,
1083 .del_transaction = linked_attributes_del_transaction,