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 "dlinklist.h"
34 #include "dsdb/samdb/samdb.h"
35 #include "librpc/gen_ndr/ndr_misc.h"
38 struct la_context *la_list;
42 struct la_op_store *next;
43 struct la_op_store *prev;
44 enum la_op {LA_OP_ADD, LA_OP_DEL} op;
50 struct replace_context {
51 struct la_context *ac;
52 unsigned int num_elements;
53 struct ldb_message_element *el;
57 struct la_context *next, *prev;
58 const struct dsdb_schema *schema;
59 struct ldb_module *module;
60 struct ldb_request *req;
61 struct ldb_dn *partition_dn;
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;
75 const struct ldb_control *partition_ctrl;
77 ldb = ldb_module_get_ctx(module);
79 ac = talloc_zero(req, struct la_context);
85 ac->schema = dsdb_get_schema(ldb);
89 /* remember the partition DN that came in, if given */
90 partition_ctrl = ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID);
92 const struct dsdb_control_current_partition *partition;
93 partition = talloc_get_type(partition_ctrl->data,
94 struct dsdb_control_current_partition);
95 SMB_ASSERT(partition && partition->version == DSDB_CONTROL_CURRENT_PARTITION_VERSION);
97 ac->partition_dn = ldb_dn_copy(ac, partition->dn);
104 turn a DN into a GUID
106 static int la_guid_from_dn(struct la_context *ac, struct ldb_dn *dn, struct GUID *guid)
111 status = dsdb_get_extended_dn_guid(dn, guid);
112 if (NT_STATUS_IS_OK(status)) {
115 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
116 DEBUG(4,(__location__ ": Unable to parse GUID for dn %s\n",
117 ldb_dn_get_linearized(dn)));
118 return LDB_ERR_OPERATIONS_ERROR;
121 ret = dsdb_find_guid_by_dn(ldb_module_get_ctx(ac->module), dn, guid);
122 if (ret != LDB_SUCCESS) {
123 DEBUG(4,(__location__ ": Failed to find GUID for dn %s\n",
124 ldb_dn_get_linearized(dn)));
131 /* Common routine to handle reading the attributes and creating a
132 * series of modify requests */
133 static int la_store_op(struct la_context *ac,
134 enum la_op op, struct ldb_val *dn,
137 struct ldb_context *ldb;
138 struct la_op_store *os;
139 struct ldb_dn *op_dn;
142 ldb = ldb_module_get_ctx(ac->module);
144 op_dn = ldb_dn_from_ldb_val(ac, ldb, dn);
146 ldb_asprintf_errstring(ldb,
147 "could not parse attribute as a DN");
148 return LDB_ERR_INVALID_DN_SYNTAX;
151 os = talloc_zero(ac, struct la_op_store);
154 return LDB_ERR_OPERATIONS_ERROR;
159 ret = la_guid_from_dn(ac, op_dn, &os->guid);
160 if (ret == LDB_ERR_NO_SUCH_OBJECT && ac->req->operation == LDB_DELETE) {
161 /* we are deleting an object, and we've found it has a
162 * forward link to a target that no longer
163 * exists. This is not an error in the delete, and we
164 * should just not do the deferred delete of the
170 if (ret != LDB_SUCCESS) {
174 os->name = talloc_strdup(os, name);
177 return LDB_ERR_OPERATIONS_ERROR;
180 /* Do deletes before adds */
181 if (op == LA_OP_ADD) {
182 DLIST_ADD_END(ac->ops, os, struct la_op_store *);
184 /* By adding to the head of the list, we do deletes before
185 * adds when processing a replace */
186 DLIST_ADD(ac->ops, os);
192 static int la_op_search_callback(struct ldb_request *req,
193 struct ldb_reply *ares);
194 static int la_queue_mod_request(struct la_context *ac);
195 static int la_down_req(struct la_context *ac);
200 static int linked_attributes_add(struct ldb_module *module, struct ldb_request *req)
202 struct ldb_context *ldb;
203 const struct dsdb_attribute *target_attr;
204 struct la_context *ac;
205 const char *attr_name;
209 ldb = ldb_module_get_ctx(module);
211 if (ldb_dn_is_special(req->op.add.message->dn)) {
212 /* do not manipulate our control entries */
213 return ldb_next_request(module, req);
216 ac = linked_attributes_init(module, req);
218 return LDB_ERR_OPERATIONS_ERROR;
222 /* without schema, this doesn't make any sense */
224 return ldb_next_request(module, req);
227 /* Need to ensure we only have forward links being specified */
228 for (i=0; i < req->op.add.message->num_elements; i++) {
229 const struct ldb_message_element *el = &req->op.add.message->elements[i];
230 const struct dsdb_attribute *schema_attr
231 = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
233 ldb_asprintf_errstring(ldb,
234 "attribute %s is not a valid attribute in schema", el->name);
235 return LDB_ERR_OBJECT_CLASS_VIOLATION;
237 /* We have a valid attribute, now find out if it is linked */
238 if (schema_attr->linkID == 0) {
242 if ((schema_attr->linkID & 1) == 1) {
243 /* Odd is for the target. Illegal to modify */
244 ldb_asprintf_errstring(ldb,
245 "attribute %s must not be modified directly, it is a linked attribute", el->name);
246 return LDB_ERR_UNWILLING_TO_PERFORM;
249 /* Even link IDs are for the originating attribute */
250 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
253 * windows 2003 has a broken schema where
254 * the definition of msDS-IsDomainFor
255 * is missing (which is supposed to be
256 * the backlink of the msDS-HasDomainNCs
262 attr_name = target_attr->lDAPDisplayName;
264 for (j = 0; j < el->num_values; j++) {
265 ret = la_store_op(ac, LA_OP_ADD,
268 if (ret != LDB_SUCCESS) {
274 /* if no linked attributes are present continue */
275 if (ac->ops == NULL) {
276 /* nothing to do for this module, proceed */
278 return ldb_next_request(module, req);
281 /* start with the original request */
282 return la_down_req(ac);
285 /* For a delete or rename, we need to find out what linked attributes
286 * are currently on this DN, and then deal with them. This is the
287 * callback to the base search */
289 static int la_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares)
291 struct ldb_context *ldb;
292 const struct dsdb_attribute *schema_attr;
293 const struct dsdb_attribute *target_attr;
294 struct ldb_message_element *search_el;
295 struct replace_context *rc;
296 struct la_context *ac;
297 const char *attr_name;
299 int ret = LDB_SUCCESS;
301 ac = talloc_get_type(req->context, struct la_context);
302 ldb = ldb_module_get_ctx(ac->module);
306 return ldb_module_done(ac->req, NULL, NULL,
307 LDB_ERR_OPERATIONS_ERROR);
309 if (ares->error != LDB_SUCCESS) {
310 return ldb_module_done(ac->req, ares->controls,
311 ares->response, ares->error);
314 /* Only entries are interesting, and we only want the olddn */
315 switch (ares->type) {
316 case LDB_REPLY_ENTRY:
318 if (ldb_dn_compare(ares->message->dn, ac->req->op.mod.message->dn) != 0) {
319 ldb_asprintf_errstring(ldb,
320 "linked_attributes: %s is not the DN we were looking for", ldb_dn_get_linearized(ares->message->dn));
321 /* Guh? We only asked for this DN */
323 return ldb_module_done(ac->req, NULL, NULL,
324 LDB_ERR_OPERATIONS_ERROR);
327 ac->add_dn = ac->del_dn = talloc_steal(ac, ares->message->dn);
329 /* We don't populate 'rc' for ADD - it can't be deleting elements anyway */
330 for (i = 0; rc && i < rc->num_elements; i++) {
332 schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rc->el[i].name);
334 ldb_asprintf_errstring(ldb,
335 "attribute %s is not a valid attribute in schema",
338 return ldb_module_done(ac->req, NULL, NULL,
339 LDB_ERR_OBJECT_CLASS_VIOLATION);
342 search_el = ldb_msg_find_element(ares->message,
345 /* See if this element already exists */
346 /* otherwise just ignore as
347 * the add has already been scheduled */
352 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
355 * windows 2003 has a broken schema where
356 * the definition of msDS-IsDomainFor
357 * is missing (which is supposed to be
358 * the backlink of the msDS-HasDomainNCs
363 attr_name = target_attr->lDAPDisplayName;
365 /* Now we know what was there, we can remove it for the re-add */
366 for (j = 0; j < search_el->num_values; j++) {
367 ret = la_store_op(ac, LA_OP_DEL,
368 &search_el->values[j],
370 if (ret != LDB_SUCCESS) {
372 return ldb_module_done(ac->req,
380 case LDB_REPLY_REFERRAL:
388 if (ac->req->operation == LDB_ADD) {
389 /* Start the modifies to the backlinks */
390 ret = la_queue_mod_request(ac);
392 if (ret != LDB_SUCCESS) {
393 return ldb_module_done(ac->req, NULL, NULL,
397 /* Start with the original request */
398 ret = la_down_req(ac);
399 if (ret != LDB_SUCCESS) {
400 return ldb_module_done(ac->req, NULL, NULL, ret);
413 static int linked_attributes_del(struct ldb_module *module, struct ldb_request *req)
415 struct ldb_context *ldb;
416 struct ldb_request *search_req;
417 struct la_context *ac;
422 /* This gets complex: We need to:
423 - Do a search for the entry
424 - Wait for these result to appear
425 - In the callback for the result, issue a modify
426 request based on the linked attributes found
427 - Wait for each modify result
431 ldb = ldb_module_get_ctx(module);
433 ac = linked_attributes_init(module, req);
435 return LDB_ERR_OPERATIONS_ERROR;
439 /* without schema, this doesn't make any sense */
440 return ldb_next_request(module, req);
443 werr = dsdb_linked_attribute_lDAPDisplayName_list(ac->schema, ac, &attrs);
444 if (!W_ERROR_IS_OK(werr)) {
445 return LDB_ERR_OPERATIONS_ERROR;
448 ret = ldb_build_search_req(&search_req, ldb, req,
449 req->op.del.dn, LDB_SCOPE_BASE,
450 "(objectClass=*)", attrs,
452 ac, la_op_search_callback,
455 if (ret != LDB_SUCCESS) {
459 talloc_steal(search_req, attrs);
461 return ldb_next_request(module, search_req);
465 static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req)
467 struct la_context *ac;
469 /* This gets complex: We need to:
470 - Do a search for the entry
471 - Wait for these result to appear
472 - In the callback for the result, issue a modify
473 request based on the linked attributes found
474 - Wait for each modify result
478 ac = linked_attributes_init(module, req);
480 return LDB_ERR_OPERATIONS_ERROR;
484 /* without schema, this doesn't make any sense */
485 return ldb_next_request(module, req);
488 /* start with the original request */
489 return la_down_req(ac);
493 static int la_op_search_callback(struct ldb_request *req,
494 struct ldb_reply *ares)
496 struct ldb_context *ldb;
497 struct la_context *ac;
498 const struct dsdb_attribute *schema_attr;
499 const struct dsdb_attribute *target_attr;
500 const struct ldb_message_element *el;
501 const char *attr_name;
505 ac = talloc_get_type(req->context, struct la_context);
506 ldb = ldb_module_get_ctx(ac->module);
509 return ldb_module_done(ac->req, NULL, NULL,
510 LDB_ERR_OPERATIONS_ERROR);
512 if (ares->error != LDB_SUCCESS) {
513 return ldb_module_done(ac->req, ares->controls,
514 ares->response, ares->error);
517 /* Only entries are interesting, and we only want the olddn */
518 switch (ares->type) {
519 case LDB_REPLY_ENTRY:
520 ret = ldb_dn_compare(ares->message->dn, req->op.search.base);
522 /* Guh? We only asked for this DN */
524 return ldb_module_done(ac->req, NULL, NULL,
525 LDB_ERR_OPERATIONS_ERROR);
527 if (ares->message->num_elements == 0) {
528 /* only bother at all if there were some
529 * linked attributes found */
534 switch (ac->req->operation) {
536 ac->del_dn = talloc_steal(ac, ares->message->dn);
539 ac->add_dn = talloc_steal(ac, ares->message->dn);
540 ac->del_dn = talloc_steal(ac, ac->req->op.rename.olddn);
544 ldb_set_errstring(ldb,
545 "operations must be delete or rename");
546 return ldb_module_done(ac->req, NULL, NULL,
547 LDB_ERR_OPERATIONS_ERROR);
550 for (i = 0; i < ares->message->num_elements; i++) {
551 el = &ares->message->elements[i];
553 schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
555 ldb_asprintf_errstring(ldb,
556 "attribute %s is not a valid attribute"
557 " in schema", el->name);
559 return ldb_module_done(ac->req, NULL, NULL,
560 LDB_ERR_OBJECT_CLASS_VIOLATION);
563 /* Valid attribute, now find out if it is linked */
564 if (schema_attr->linkID == 0) {
565 /* Not a linked attribute, skip */
569 if ((schema_attr->linkID & 1) == 0) {
570 /* Odd is for the target. */
571 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
575 attr_name = target_attr->lDAPDisplayName;
577 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID - 1);
581 attr_name = target_attr->lDAPDisplayName;
583 for (j = 0; j < el->num_values; j++) {
584 ret = la_store_op(ac, LA_OP_DEL,
588 /* for renames, ensure we add it back */
589 if (ret == LDB_SUCCESS
590 && ac->req->operation == LDB_RENAME) {
591 ret = la_store_op(ac, LA_OP_ADD,
595 if (ret != LDB_SUCCESS) {
597 return ldb_module_done(ac->req,
605 case LDB_REPLY_REFERRAL:
614 switch (ac->req->operation) {
616 /* start the mod requests chain */
617 ret = la_down_req(ac);
618 if (ret != LDB_SUCCESS) {
619 return ldb_module_done(ac->req, NULL, NULL, ret);
624 /* start the mod requests chain */
625 ret = la_queue_mod_request(ac);
626 if (ret != LDB_SUCCESS) {
627 return ldb_module_done(ac->req, NULL, NULL,
634 ldb_set_errstring(ldb,
635 "operations must be delete or rename");
636 return ldb_module_done(ac->req, NULL, NULL,
637 LDB_ERR_OPERATIONS_ERROR);
645 /* queue a linked attributes modify request in the la_private
647 static int la_queue_mod_request(struct la_context *ac)
649 struct la_private *la_private =
650 talloc_get_type(ldb_module_get_private(ac->module), struct la_private);
652 if (la_private == NULL) {
653 ldb_debug(ldb_module_get_ctx(ac->module), LDB_DEBUG_ERROR, __location__ ": No la_private transaction setup\n");
654 return LDB_ERR_OPERATIONS_ERROR;
657 talloc_steal(la_private, ac);
658 DLIST_ADD(la_private->la_list, ac);
660 return ldb_module_done(ac->req, ac->op_controls,
661 ac->op_response, LDB_SUCCESS);
664 /* Having done the original operation, then try to fix up all the linked attributes for modify and delete */
665 static int la_mod_del_callback(struct ldb_request *req, struct ldb_reply *ares)
668 struct la_context *ac;
669 struct ldb_context *ldb;
671 ac = talloc_get_type(req->context, struct la_context);
672 ldb = ldb_module_get_ctx(ac->module);
675 return ldb_module_done(ac->req, NULL, NULL,
676 LDB_ERR_OPERATIONS_ERROR);
678 if (ares->error != LDB_SUCCESS) {
679 return ldb_module_done(ac->req, ares->controls,
680 ares->response, ares->error);
683 if (ares->type != LDB_REPLY_DONE) {
684 ldb_set_errstring(ldb,
685 "invalid ldb_reply_type in callback");
687 return ldb_module_done(ac->req, NULL, NULL,
688 LDB_ERR_OPERATIONS_ERROR);
691 ac->op_controls = talloc_steal(ac, ares->controls);
692 ac->op_response = talloc_steal(ac, ares->response);
694 /* If we have modfies to make, this is the time to do them for modify and delete */
695 ret = la_queue_mod_request(ac);
697 if (ret != LDB_SUCCESS) {
698 return ldb_module_done(ac->req, NULL, NULL, ret);
702 /* la_queue_mod_request has already sent the callbacks */
707 /* Having done the original rename try to fix up all the linked attributes */
708 static int la_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
711 struct la_context *ac;
712 struct ldb_request *search_req;
715 struct ldb_context *ldb;
717 ac = talloc_get_type(req->context, struct la_context);
718 ldb = ldb_module_get_ctx(ac->module);
721 return ldb_module_done(ac->req, NULL, NULL,
722 LDB_ERR_OPERATIONS_ERROR);
724 if (ares->error != LDB_SUCCESS) {
725 return ldb_module_done(ac->req, ares->controls,
726 ares->response, ares->error);
729 if (ares->type != LDB_REPLY_DONE) {
730 ldb_set_errstring(ldb,
731 "invalid ldb_reply_type in callback");
733 return ldb_module_done(ac->req, NULL, NULL,
734 LDB_ERR_OPERATIONS_ERROR);
737 werr = dsdb_linked_attribute_lDAPDisplayName_list(ac->schema, ac, &attrs);
738 if (!W_ERROR_IS_OK(werr)) {
739 return LDB_ERR_OPERATIONS_ERROR;
742 ret = ldb_build_search_req(&search_req, ldb, req,
743 ac->req->op.rename.newdn, LDB_SCOPE_BASE,
744 "(objectClass=*)", attrs,
746 ac, la_op_search_callback,
749 if (ret != LDB_SUCCESS) {
753 talloc_steal(search_req, attrs);
755 if (ret == LDB_SUCCESS) {
756 ret = ldb_request_add_control(search_req,
757 LDB_CONTROL_EXTENDED_DN_OID,
760 if (ret != LDB_SUCCESS) {
761 return ldb_module_done(ac->req, NULL, NULL,
765 ac->op_controls = talloc_steal(ac, ares->controls);
766 ac->op_response = talloc_steal(ac, ares->response);
768 return ldb_next_request(ac->module, search_req);
771 /* Having done the original add, then try to fix up all the linked attributes
773 This is done after the add so the links can get the extended DNs correctly.
775 static int la_add_callback(struct ldb_request *req, struct ldb_reply *ares)
778 struct la_context *ac;
779 struct ldb_context *ldb;
781 ac = talloc_get_type(req->context, struct la_context);
782 ldb = ldb_module_get_ctx(ac->module);
785 return ldb_module_done(ac->req, NULL, NULL,
786 LDB_ERR_OPERATIONS_ERROR);
788 if (ares->error != LDB_SUCCESS) {
789 return ldb_module_done(ac->req, ares->controls,
790 ares->response, ares->error);
793 if (ares->type != LDB_REPLY_DONE) {
794 ldb_set_errstring(ldb,
795 "invalid ldb_reply_type in callback");
797 return ldb_module_done(ac->req, NULL, NULL,
798 LDB_ERR_OPERATIONS_ERROR);
802 struct ldb_request *search_req;
803 static const char *attrs[] = { NULL };
805 /* The callback does all the hard work here - we need
806 * the objectGUID and SID of the added record */
807 ret = ldb_build_search_req(&search_req, ldb, ac,
808 ac->req->op.add.message->dn,
810 "(objectClass=*)", attrs,
812 ac, la_mod_search_callback,
815 if (ret == LDB_SUCCESS) {
816 ret = ldb_request_add_control(search_req,
817 LDB_CONTROL_EXTENDED_DN_OID,
820 if (ret != LDB_SUCCESS) {
821 return ldb_module_done(ac->req, NULL, NULL,
825 ac->op_controls = talloc_steal(ac, ares->controls);
826 ac->op_response = talloc_steal(ac, ares->response);
828 return ldb_next_request(ac->module, search_req);
831 return ldb_module_done(ac->req, ares->controls,
832 ares->response, ares->error);
836 /* Reconstruct the original request, but pointing at our local callback to finish things off */
837 static int la_down_req(struct la_context *ac)
839 struct ldb_request *down_req;
841 struct ldb_context *ldb;
843 ldb = ldb_module_get_ctx(ac->module);
845 switch (ac->req->operation) {
847 ret = ldb_build_add_req(&down_req, ldb, ac,
848 ac->req->op.add.message,
854 ret = ldb_build_mod_req(&down_req, ldb, ac,
855 ac->req->op.mod.message,
857 ac, la_mod_del_callback,
861 ret = ldb_build_del_req(&down_req, ldb, ac,
864 ac, la_mod_del_callback,
868 ret = ldb_build_rename_req(&down_req, ldb, ac,
869 ac->req->op.rename.olddn,
870 ac->req->op.rename.newdn,
872 ac, la_rename_callback,
876 ret = LDB_ERR_OPERATIONS_ERROR;
878 if (ret != LDB_SUCCESS) {
882 return ldb_next_request(ac->module, down_req);
886 use the GUID part of an extended DN to find the target DN, in case
889 static int la_find_dn_target(struct ldb_module *module, struct la_context *ac,
890 struct GUID *guid, struct ldb_dn **dn)
892 return dsdb_find_dn_by_guid(ldb_module_get_ctx(ac->module), ac, GUID_string(ac, guid), dn);
895 /* apply one la_context op change */
896 static int la_do_op_request(struct ldb_module *module, struct la_context *ac, struct la_op_store *op)
898 struct ldb_message_element *ret_el;
899 struct ldb_request *mod_req;
900 struct ldb_message *new_msg;
901 struct ldb_context *ldb;
904 ldb = ldb_module_get_ctx(ac->module);
906 /* Create the modify request */
907 new_msg = ldb_msg_new(ac);
910 return LDB_ERR_OPERATIONS_ERROR;
913 ret = la_find_dn_target(module, ac, &op->guid, &new_msg->dn);
914 if (ret != LDB_SUCCESS) {
918 if (op->op == LA_OP_ADD) {
919 ret = ldb_msg_add_empty(new_msg, op->name,
920 LDB_FLAG_MOD_ADD, &ret_el);
922 ret = ldb_msg_add_empty(new_msg, op->name,
923 LDB_FLAG_MOD_DELETE, &ret_el);
925 if (ret != LDB_SUCCESS) {
928 ret_el->values = talloc_array(new_msg, struct ldb_val, 1);
929 if (!ret_el->values) {
931 return LDB_ERR_OPERATIONS_ERROR;
933 ret_el->num_values = 1;
934 if (op->op == LA_OP_ADD) {
935 ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->add_dn, 1));
937 ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->del_dn, 1));
941 ldb_debug(ldb, LDB_DEBUG_WARNING,
942 "link on %s %s: %s %s\n",
943 ldb_dn_get_linearized(new_msg->dn), ret_el->name,
944 ret_el->values[0].data, ac->ops->op == LA_OP_ADD ? "added" : "deleted");
947 ret = ldb_build_mod_req(&mod_req, ldb, op,
951 ldb_op_default_callback,
953 if (ret != LDB_SUCCESS) {
956 talloc_steal(mod_req, new_msg);
959 DEBUG(4,("Applying linked attribute change:\n%s\n",
960 ldb_ldif_message_string(ldb, op, LDB_CHANGETYPE_MODIFY, new_msg)));
963 /* Run the new request */
964 ret = ldb_next_request(module, mod_req);
966 /* we need to wait for this to finish, as we are being called
967 from the synchronous end_transaction hook of this module */
968 if (ret == LDB_SUCCESS) {
969 ret = ldb_wait(mod_req->handle, LDB_WAIT_ALL);
972 if (ret != LDB_SUCCESS) {
973 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s' %s\n",
975 ldb_ldif_message_string(ldb, op, LDB_CHANGETYPE_MODIFY, new_msg));
981 /* apply one set of la_context changes */
982 static int la_do_mod_request(struct ldb_module *module, struct la_context *ac)
984 struct la_op_store *op;
986 for (op = ac->ops; op; op=op->next) {
987 int ret = la_do_op_request(module, ac, op);
988 if (ret != LDB_SUCCESS) {
989 if (ret != LDB_ERR_NO_SUCH_OBJECT) {
1000 we hook into the transaction operations to allow us to
1001 perform the linked attribute updates at the end of the whole
1002 transaction. This allows a forward linked attribute to be created
1003 before the target is created, as long as the target is created
1004 in the same transaction
1006 static int linked_attributes_start_transaction(struct ldb_module *module)
1008 /* create our private structure for this transaction */
1009 struct la_private *la_private = talloc_get_type(ldb_module_get_private(module),
1011 talloc_free(la_private);
1012 la_private = talloc(module, struct la_private);
1013 if (la_private == NULL) {
1014 return LDB_ERR_OPERATIONS_ERROR;
1016 la_private->la_list = NULL;
1017 ldb_module_set_private(module, la_private);
1018 return ldb_next_start_trans(module);
1022 on prepare commit we loop over our queued la_context structures
1023 and apply each of them
1025 static int linked_attributes_prepare_commit(struct ldb_module *module)
1027 struct la_private *la_private =
1028 talloc_get_type(ldb_module_get_private(module), struct la_private);
1029 struct la_context *ac;
1032 /* prepare commit without begin_transaction - let someone else return the error, just don't segfault */
1033 return ldb_next_prepare_commit(module);
1035 /* walk the list backwards, to do the first entry first, as we
1036 * added the entries with DLIST_ADD() which puts them at the
1037 * start of the list */
1038 for (ac = la_private->la_list; ac && ac->next; ac=ac->next) ;
1040 for (; ac; ac=ac->prev) {
1043 ret = la_do_mod_request(module, ac);
1044 if (ret != LDB_SUCCESS) {
1045 DEBUG(0,(__location__ ": Failed mod request ret=%d\n", ret));
1046 talloc_free(la_private);
1047 ldb_module_set_private(module, NULL);
1052 talloc_free(la_private);
1053 ldb_module_set_private(module, NULL);
1055 return ldb_next_prepare_commit(module);
1058 static int linked_attributes_del_transaction(struct ldb_module *module)
1060 struct la_private *la_private =
1061 talloc_get_type(ldb_module_get_private(module), struct la_private);
1062 talloc_free(la_private);
1063 ldb_module_set_private(module, NULL);
1064 return ldb_next_del_trans(module);
1068 _PUBLIC_ const struct ldb_module_ops ldb_linked_attributes_module_ops = {
1069 .name = "linked_attributes",
1070 .add = linked_attributes_add,
1071 .del = linked_attributes_del,
1072 .rename = linked_attributes_rename,
1073 .start_transaction = linked_attributes_start_transaction,
1074 .prepare_commit = linked_attributes_prepare_commit,
1075 .del_transaction = linked_attributes_del_transaction,