4 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 * Component: ldb linked_attributes module
25 * Description: Module to ensure linked attribute pairs remain in sync
27 * Author: Andrew Bartlett
31 #include "ldb/include/ldb.h"
32 #include "ldb/include/ldb_errors.h"
33 #include "ldb/include/ldb_private.h"
34 #include "dsdb/samdb/samdb.h"
36 struct linked_attributes_context {
37 enum la_step {LA_SEARCH, LA_DO_OPS, LA_DO_ORIG} step;
38 struct ldb_module *module;
39 struct ldb_handle *handle;
40 struct ldb_request *orig_req;
42 struct ldb_request *search_req;
43 struct ldb_request **down_req;
44 struct ldb_request *orig_down_req;
47 int finished_requests;
49 const char **linked_attrs;
52 struct replace_context {
53 struct linked_attributes_context *ac;
54 struct ldb_message_element *el;
57 static int linked_attributes_rename_del_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares);
59 static struct linked_attributes_context *linked_attributes_init_handle(struct ldb_request *req,
60 struct ldb_module *module)
62 struct linked_attributes_context *ac;
65 h = talloc_zero(req, struct ldb_handle);
67 ldb_set_errstring(module->ldb, "Out of Memory");
73 ac = talloc_zero(h, struct linked_attributes_context);
75 ldb_set_errstring(module->ldb, "Out of Memory");
86 ac->orig_down_req = talloc(ac, struct ldb_request);
87 if (!ac->orig_down_req) {
88 ldb_oom(ac->module->ldb);
92 *ac->orig_down_req = *req;
99 /* Common routine to handle reading the attributes and creating a
100 * series of modify requests */
102 static int setup_modifies(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
103 struct linked_attributes_context *ac,
104 const struct ldb_message *msg,
105 struct ldb_dn *olddn, struct ldb_dn *newdn)
107 int i, j, ret = LDB_SUCCESS;
108 const struct dsdb_schema *schema = dsdb_get_schema(ldb);
109 /* Look up each of the returned attributes */
110 /* Find their schema */
111 /* And it is an actual entry: now create a series of modify requests */
112 for (i=0; i < msg->num_elements; i++) {
114 const struct dsdb_attribute *target_attr;
115 const struct ldb_message_element *el = &msg->elements[i];
116 const struct dsdb_attribute *schema_attr
117 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
119 ldb_asprintf_errstring(ldb,
120 "attribute %s is not a valid attribute in schema", el->name);
121 return LDB_ERR_OBJECT_CLASS_VIOLATION;
123 /* We have a valid attribute, but if it's not linked they maybe we just got an extra return on our search... */
124 if (schema_attr->linkID == 0) {
128 /* Depending on which direction this link is in, we need to find it's partner */
129 if ((schema_attr->linkID & 1) == 1) {
130 otherid = schema_attr->linkID - 1;
132 otherid = schema_attr->linkID + 1;
135 /* Now find the target attribute */
136 target_attr = dsdb_attribute_by_linkID(schema, otherid);
138 ldb_asprintf_errstring(ldb,
139 "attribute %s does not have valid link target", el->name);
140 return LDB_ERR_OBJECT_CLASS_VIOLATION;
143 /* For each value being moded, we need to setup the modify */
144 for (j=0; j < el->num_values; j++) {
145 struct ldb_message_element *ret_el;
146 struct ldb_request *new_req;
147 struct ldb_message *new_msg;
149 /* Create a spot in the list for the requests */
150 ac->down_req = talloc_realloc(ac, ac->down_req,
151 struct ldb_request *, ac->num_requests + 1);
154 return LDB_ERR_OPERATIONS_ERROR;
157 /* Create the modify request */
158 new_msg = ldb_msg_new(ac->down_req);
161 return LDB_ERR_OPERATIONS_ERROR;
163 new_msg->dn = ldb_dn_from_ldb_val(new_msg, ldb, &el->values[j]);
165 ldb_asprintf_errstring(ldb,
166 "attribute %s value %s was not a valid DN", msg->elements[i].name,
168 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
172 ret = ldb_msg_add_empty(new_msg, target_attr->lDAPDisplayName,
173 LDB_FLAG_MOD_DELETE, &ret_el);
174 if (ret != LDB_SUCCESS) {
177 ret_el->values = talloc_array(new_msg, struct ldb_val, 1);
178 if (!ret_el->values) {
180 return LDB_ERR_OPERATIONS_ERROR;
182 ret_el->values[0] = data_blob_string_const(ldb_dn_get_linearized(olddn));
183 ret_el->num_values = 1;
187 ret = ldb_msg_add_empty(new_msg, target_attr->lDAPDisplayName,
188 LDB_FLAG_MOD_ADD, &ret_el);
189 if (ret != LDB_SUCCESS) {
192 ret_el->values = talloc_array(new_msg, struct ldb_val, 1);
193 if (!ret_el->values) {
195 return LDB_ERR_OPERATIONS_ERROR;
197 ret_el->values[0] = data_blob_string_const(ldb_dn_get_linearized(newdn));
198 ret_el->num_values = 1;
201 ret = ldb_build_mod_req(&new_req, ldb, ac->down_req,
206 if (ret != LDB_SUCCESS) {
210 talloc_steal(new_req, new_msg);
212 ldb_set_timeout_from_prev_req(ldb, ac->orig_req, new_req);
214 ac->down_req[ac->num_requests] = new_req;
218 /* Run the new request */
219 ret = ldb_next_request(ac->module, new_req);
220 if (ret != LDB_SUCCESS) {
229 static int linked_attributes_add(struct ldb_module *module, struct ldb_request *req)
232 struct linked_attributes_context *ac;
234 const struct dsdb_schema *schema = dsdb_get_schema(module->ldb);
236 /* without schema, this doesn't make any sense */
237 return ldb_next_request(module, req);
240 if (ldb_dn_is_special(req->op.mod.message->dn)) {
241 /* do not manipulate our control entries */
242 return ldb_next_request(module, req);
246 ac = linked_attributes_init_handle(req, module);
248 return LDB_ERR_OPERATIONS_ERROR;
251 ac->step = LA_DO_OPS;
253 /* Need to ensure we only have forward links being specified */
254 for (i=0; i < req->op.add.message->num_elements; i++) {
255 const struct ldb_message_element *el = &req->op.add.message->elements[i];
256 const struct dsdb_attribute *schema_attr
257 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
259 ldb_asprintf_errstring(module->ldb,
260 "attribute %s is not a valid attribute in schema", req->op.add.message->elements[i].name);
261 return LDB_ERR_OBJECT_CLASS_VIOLATION;
263 /* We have a valid attribute, not find out if it is linked */
264 if (schema_attr->linkID == 0) {
268 if ((schema_attr->linkID & 1) == 1) {
269 /* Odd is for the target. Illigal to modify */
270 ldb_asprintf_errstring(module->ldb,
271 "attribute %s must not be modified directly, it is a linked attribute", req->op.add.message->elements[i].name);
272 return LDB_ERR_UNWILLING_TO_PERFORM;
275 /* Even link IDs are for the originating attribute */
278 /* Now call the common routine to setup the modifies across all the attributes */
279 return setup_modifies(module->ldb, ac, ac, req->op.add.message, NULL, req->op.add.message->dn);
288 static int merge_cmp(struct merge *merge1, struct merge *merge2) {
290 ret = ldb_dn_compare(merge1->dn, merge2->dn);
292 if (merge1->add == merge2->add) {
295 if (merge1->add == true) {
303 static int linked_attributes_mod_replace_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
305 struct replace_context *ac2 = talloc_get_type(context, struct replace_context);
306 struct linked_attributes_context *ac = ac2->ac;
308 /* OK, we have one search result here: */
310 /* Only entries are interesting, and we only want the olddn */
311 if (ares->type == LDB_REPLY_ENTRY
312 && ldb_dn_compare(ares->message->dn, ac->orig_req->op.mod.message->dn) == 0) {
313 /* only bother at all if there were some linked attributes found */
314 struct ldb_message_element *search_el
315 = ldb_msg_find_element(ares->message,
318 /* See if this element already exists */
321 struct merge *merged_list = NULL;
323 int ret, size = 0, i;
324 struct ldb_message *msg = ldb_msg_new(ac);
326 ldb_oom(ac->module->ldb);
327 return LDB_ERR_OPERATIONS_ERROR;
330 /* Add all the existing elements, marking as 'proposed for delete' by setting .add = false */
331 for (i=0; i < search_el->num_values; i++) {
332 merged_list = talloc_realloc(ares, merged_list, struct merge, size + 1);
333 merged_list[size].dn = ldb_dn_from_ldb_val(merged_list, ldb, &search_el->values[i]);
334 merged_list[size].add = false;
335 merged_list[size].ignore = false;
339 /* Add all the new replacement elements, marking as 'proposed for add' by setting .add = true */
340 for (i=0; i < ac2->el->num_values; i++) {
341 merged_list = talloc_realloc(ares, merged_list, struct merge, size + 1);
342 merged_list[size].dn = ldb_dn_from_ldb_val(merged_list, ldb, &ac2->el->values[i]);
343 merged_list[size].add = true;
344 merged_list[size].ignore = false;
348 /* Sort the list, so we can pick out an add and delete for the same DN, and eliminate them */
349 qsort(merged_list, size,
350 sizeof(*merged_list),
351 (comparison_fn_t)merge_cmp);
353 /* Now things are sorted, it is trivial to mark pairs of DNs as 'ignore' */
354 for (i=0; i + 1 < size; i++) {
355 if (ldb_dn_compare(merged_list[i].dn,
356 merged_list[i+1].dn) == 0
357 /* Fortunetly the sort also sorts 'add == false' first */
358 && merged_list[i].add == false
359 && merged_list[i+1].add == true) {
361 /* Mark as ignore, so we include neither in the actual operations */
362 merged_list[i].ignore = true;
363 merged_list[i+1].ignore = true;
367 /* Arrange to delete anything the search found that we don't re-add */
368 for (i=0; i < size; i++) {
369 if (merged_list[i].ignore == false
370 && merged_list[i].add == false) {
371 ldb_msg_add_steal_string(msg, search_el->name,
372 ldb_dn_get_linearized(merged_list[i].dn));
376 /* The DN to set on the linked attributes is the original DN of the modify message */
377 msg->dn = ac->orig_req->op.mod.message->dn;
379 ret = setup_modifies(ac->module->ldb, ac2, ac, msg, ares->message->dn, NULL);
380 if (ret != LDB_SUCCESS) {
384 /* Now add links for all the actually new elements */
385 for (i=0; i < size; i++) {
386 if (merged_list[i].ignore == false && merged_list[i].add == true) {
387 ldb_msg_add_steal_string(msg, search_el->name,
388 ldb_dn_get_linearized(merged_list[i].dn));
392 ret = setup_modifies(ac->module->ldb, ac2, ac, msg, NULL, ares->message->dn);
393 if (ret != LDB_SUCCESS) {
397 talloc_free(merged_list);
400 /* Looks like it doesn't exist, process like an 'add' */
401 struct ldb_message *msg = ldb_msg_new(ac);
403 ldb_oom(ac->module->ldb);
404 return LDB_ERR_OPERATIONS_ERROR;
406 msg->num_elements = 1;
407 msg->elements = ac2->el;
408 msg->dn = ac->orig_req->op.mod.message->dn;
410 return setup_modifies(ac->module->ldb, ac2, ac, msg, NULL, ac->orig_req->op.mod.message->dn);
414 } else if (ares->type == LDB_REPLY_ENTRY) {
415 /* Guh? We only asked for this DN */
416 return LDB_ERR_OPERATIONS_ERROR;
426 static int linked_attributes_modify(struct ldb_module *module, struct ldb_request *req)
428 /* Look over list of modifications */
429 /* Find if any are for linked attributes */
430 /* Determine the effect of the modification */
431 /* Apply the modify to the linked entry */
434 struct linked_attributes_context *ac;
436 const struct dsdb_schema *schema = dsdb_get_schema(module->ldb);
438 /* without schema, this doesn't make any sense */
439 return ldb_next_request(module, req);
442 if (ldb_dn_is_special(req->op.mod.message->dn)) {
443 /* do not manipulate our control entries */
444 return ldb_next_request(module, req);
448 ac = linked_attributes_init_handle(req, module);
450 return LDB_ERR_OPERATIONS_ERROR;
453 /* prepare the first operation */
454 ac->step = LA_DO_OPS;
456 for (i=0; i < req->op.mod.message->num_elements; i++) {
458 struct ldb_request *new_req;
459 const struct dsdb_attribute *target_attr;
460 const struct ldb_message_element *el = &req->op.mod.message->elements[i];
461 const struct dsdb_attribute *schema_attr
462 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
464 ldb_asprintf_errstring(module->ldb,
465 "attribute %s is not a valid attribute in schema", req->op.mod.message->elements[i].name);
466 return LDB_ERR_OBJECT_CLASS_VIOLATION;
468 /* We have a valid attribute, not find out if it is linked */
469 if (schema_attr->linkID == 0) {
473 if ((schema_attr->linkID & 1) == 1) {
474 /* Odd is for the target. Illigal to modify */
475 ldb_asprintf_errstring(module->ldb,
476 "attribute %s must not be modified directly, it is a linked attribute", req->op.mod.message->elements[i].name);
477 return LDB_ERR_UNWILLING_TO_PERFORM;
480 /* Even link IDs are for the originating attribute */
482 /* Now find the target attribute */
483 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID + 1);
485 ldb_asprintf_errstring(module->ldb,
486 "attribute %s does not have valid link target", req->op.mod.message->elements[i].name);
487 return LDB_ERR_OBJECT_CLASS_VIOLATION;
490 /* Replace with new set of values */
491 if (((el->flags & LDB_FLAG_MOD_MASK) == LDB_FLAG_MOD_REPLACE)
492 && el->num_values > 0) {
493 struct replace_context *ac2 = talloc(ac, struct replace_context);
494 const char **attrs = talloc_array(ac, const char *, 2);
495 if (!attrs || !ac2) {
496 ldb_oom(ac->module->ldb);
497 return LDB_ERR_OPERATIONS_ERROR;
505 /* We need to setup a search, compare with the list, and then setup add/del as required */
507 /* The callback does all the hard work here */
508 ret = ldb_build_search_req(&new_req, module->ldb, req,
509 req->op.mod.message->dn,
515 linked_attributes_mod_replace_search_callback);
517 if (ret != LDB_SUCCESS) {
521 talloc_steal(new_req, attrs);
523 ret = ldb_set_timeout_from_prev_req(module->ldb, req, new_req);
525 if (ret != LDB_SUCCESS) {
529 /* Create a spot in the list for the requests */
530 ac->down_req = talloc_realloc(ac, ac->down_req,
531 struct ldb_request *, ac->num_requests + 1);
533 ldb_oom(ac->module->ldb);
534 return LDB_ERR_OPERATIONS_ERROR;
537 ac->down_req[ac->num_requests] = talloc_steal(ac->down_req, new_req);
540 ret = ldb_next_request(module, new_req);
542 if (ret != LDB_SUCCESS) {
548 /* Delete all values case */
549 } else if (((el->flags & LDB_FLAG_MOD_MASK) & (LDB_FLAG_MOD_DELETE|LDB_FLAG_MOD_REPLACE))
550 && el->num_values == 0) {
551 const char **attrs = talloc_array(ac, const char *, 2);
553 ldb_oom(ac->module->ldb);
554 return LDB_ERR_OPERATIONS_ERROR;
559 /* We need to setup a search, and then setup del as required */
561 /* The callback does all the hard work here, acting identically to if we had delted the whole entry */
562 ret = ldb_build_search_req(&new_req, module->ldb, req,
563 req->op.mod.message->dn,
569 linked_attributes_rename_del_search_callback);
571 if (ret != LDB_SUCCESS) {
575 talloc_steal(new_req, attrs);
577 ret = ldb_set_timeout_from_prev_req(module->ldb, req, new_req);
579 if (ret != LDB_SUCCESS) {
583 /* Create a spot in the list for the requests */
584 ac->down_req = talloc_realloc(ac, ac->down_req,
585 struct ldb_request *, ac->num_requests + 1);
587 ldb_oom(ac->module->ldb);
588 return LDB_ERR_OPERATIONS_ERROR;
591 ac->down_req[ac->num_requests] = talloc_steal(ac->down_req, new_req);
594 ret = ldb_next_request(module, new_req);
596 if (ret != LDB_SUCCESS) {
603 /* Prepare the modify (mod element) on the targets, for a normal modify request */
605 /* For each value being moded, we need to setup the modify */
606 for (j=0; j < el->num_values; j++) {
607 /* Create the modify request */
608 struct ldb_message *new_msg = ldb_msg_new(ac);
610 ldb_oom(module->ldb);
611 return LDB_ERR_OPERATIONS_ERROR;
613 new_msg->dn = ldb_dn_from_ldb_val(new_msg, module->ldb, &el->values[j]);
615 ldb_asprintf_errstring(module->ldb,
616 "attribute %s value %s was not a valid DN", req->op.mod.message->elements[i].name,
618 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
621 ret = ldb_msg_add_empty(new_msg, target_attr->lDAPDisplayName,
622 el->flags & LDB_FLAG_MOD_MASK, NULL);
623 if (ret != LDB_SUCCESS) {
627 ret = ldb_msg_add_string(new_msg, target_attr->lDAPDisplayName,
628 ldb_dn_get_linearized(ac->orig_req->op.add.message->dn));
629 if (ret != LDB_SUCCESS) {
633 ret = ldb_build_mod_req(&new_req, module->ldb, ac,
638 if (ret != LDB_SUCCESS) {
642 talloc_steal(new_req, new_msg);
644 ret = ldb_set_timeout_from_prev_req(module->ldb, req, new_req);
646 if (ret != LDB_SUCCESS) {
650 /* Now add it to the list */
651 ac->down_req = talloc_realloc(ac, ac->down_req,
652 struct ldb_request *, ac->num_requests + 1);
654 ldb_oom(ac->module->ldb);
655 return LDB_ERR_OPERATIONS_ERROR;
657 ac->down_req[ac->num_requests] = talloc_steal(ac->down_req, new_req);
660 /* Run the new request */
661 ret = ldb_next_request(module, new_req);
662 if (ret != LDB_SUCCESS) {
670 static int linked_attributes_rename_del_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
672 struct linked_attributes_context *ac = talloc_get_type(context, struct linked_attributes_context);
673 struct ldb_dn *olddn, *newdn;
675 switch (ac->orig_req->operation) {
678 olddn = ac->orig_req->op.del.dn;
682 /* This isn't the general modify case, just the modify when we are asked to delete all values */
685 olddn = ac->orig_req->op.mod.message->dn;
691 olddn = ac->orig_req->op.rename.olddn;
692 newdn = ac->orig_req->op.rename.newdn;
696 return LDB_ERR_OPERATIONS_ERROR;
700 /* OK, we have one search result here: */
702 /* Only entries are interesting, and we only want the olddn */
703 if (ares->type == LDB_REPLY_ENTRY
704 && ldb_dn_compare(ares->message->dn, olddn) == 0) {
705 /* only bother at all if there were some linked attributes found */
706 if (ares->message->num_elements > 0) {
707 return setup_modifies(ldb, ac, ac,
708 ares->message, olddn, newdn);
712 } else if (ares->type == LDB_REPLY_ENTRY) {
713 /* Guh? We only asked for this DN */
714 return LDB_ERR_OPERATIONS_ERROR;
724 static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req)
726 /* Look up list of linked attributes */
730 struct linked_attributes_context *ac;
731 struct ldb_request *new_req;
732 const struct dsdb_schema *schema = dsdb_get_schema(module->ldb);
734 /* without schema, this doesn't make any sense */
735 return ldb_next_request(module, req);
738 /* This gets complex: We need to:
739 - Do a search for the entry
740 - Wait for these result to appear
741 - In the callback for the result, issue a modify request based on the linked attributes found
742 - Wait for each modify result
746 ac = linked_attributes_init_handle(req, module);
748 return LDB_ERR_OPERATIONS_ERROR;
751 werr = dsdb_linked_attribute_lDAPDisplayName_list(schema, ac, &attrs);
752 if (!W_ERROR_IS_OK(werr)) {
753 return LDB_ERR_OPERATIONS_ERROR;
756 ret = ldb_build_search_req(&new_req, module->ldb, req,
757 req->op.rename.olddn,
763 linked_attributes_rename_del_search_callback);
765 if (ret != LDB_SUCCESS) {
769 talloc_steal(new_req, attrs);
771 ret = ldb_set_timeout_from_prev_req(module->ldb, req, new_req);
773 if (ret != LDB_SUCCESS) {
777 ac->search_req = new_req;
778 ac->step = LA_SEARCH;
779 return ldb_next_request(module, new_req);
783 static int linked_attributes_delete(struct ldb_module *module, struct ldb_request *req)
785 /* Look up list of linked attributes */
789 struct ldb_request *new_req;
790 struct linked_attributes_context *ac;
791 const struct dsdb_schema *schema = dsdb_get_schema(module->ldb);
793 /* without schema, this doesn't make any sense */
794 return ldb_next_request(module, req);
797 /* This gets complex: We need to:
798 - Do a search for the entry
799 - Wait for these result to appear
800 - In the callback for the result, issue a modify request based on the linked attributes found
801 - Wait for each modify result
805 ac = linked_attributes_init_handle(req, module);
807 return LDB_ERR_OPERATIONS_ERROR;
810 werr = dsdb_linked_attribute_lDAPDisplayName_list(schema, ac, &attrs);
811 if (!W_ERROR_IS_OK(werr)) {
812 return LDB_ERR_OPERATIONS_ERROR;
815 ret = ldb_build_search_req(&new_req, module->ldb, req,
822 linked_attributes_rename_del_search_callback);
824 if (ret != LDB_SUCCESS) {
828 talloc_steal(new_req, attrs);
830 ret = ldb_set_timeout_from_prev_req(module->ldb, req, new_req);
832 if (ret != LDB_SUCCESS) {
836 ac->search_req = new_req;
837 ac->step = LA_SEARCH;
838 return ldb_next_request(module, new_req);
842 static int linked_attributes_wait_none(struct ldb_handle *handle) {
843 struct linked_attributes_context *ac;
844 int i, ret = LDB_ERR_OPERATIONS_ERROR;
845 if (!handle || !handle->private_data) {
846 return LDB_ERR_OPERATIONS_ERROR;
849 if (handle->state == LDB_ASYNC_DONE) {
850 return handle->status;
853 handle->state = LDB_ASYNC_PENDING;
854 handle->status = LDB_SUCCESS;
856 ac = talloc_get_type(handle->private_data, struct linked_attributes_context);
860 ret = ldb_wait(ac->search_req->handle, LDB_WAIT_NONE);
862 if (ret != LDB_SUCCESS) {
863 handle->status = ret;
866 if (ac->search_req->handle->status != LDB_SUCCESS) {
867 handle->status = ac->search_req->handle->status;
871 if (ac->search_req->handle->state != LDB_ASYNC_DONE) {
874 ac->step = LA_DO_OPS;
878 for (i=0; i < ac->num_requests; i++) {
879 ret = ldb_wait(ac->down_req[i]->handle, LDB_WAIT_NONE);
881 if (ret != LDB_SUCCESS) {
882 handle->status = ret;
885 if (ac->down_req[i]->handle->status != LDB_SUCCESS) {
886 handle->status = ac->down_req[i]->handle->status;
890 if (ac->down_req[i]->handle->state != LDB_ASYNC_DONE) {
895 /* Now run the original request */
896 ac->step = LA_DO_ORIG;
897 return ldb_next_request(ac->module, ac->orig_down_req);
900 ret = ldb_wait(ac->orig_down_req->handle, LDB_WAIT_NONE);
902 if (ret != LDB_SUCCESS) {
903 handle->status = ret;
906 if (ac->orig_down_req->handle->status != LDB_SUCCESS) {
907 handle->status = ac->orig_down_req->handle->status;
911 if (ac->orig_down_req->handle->state != LDB_ASYNC_DONE) {
918 handle->state = LDB_ASYNC_DONE;
923 static int linked_attributes_wait_all(struct ldb_handle *handle) {
927 while (handle->state != LDB_ASYNC_DONE) {
928 ret = linked_attributes_wait_none(handle);
929 if (ret != LDB_SUCCESS) {
934 return handle->status;
937 static int linked_attributes_wait(struct ldb_handle *handle, enum ldb_wait_type type)
939 if (type == LDB_WAIT_ALL) {
940 return linked_attributes_wait_all(handle);
942 return linked_attributes_wait_none(handle);
946 _PUBLIC_ const struct ldb_module_ops ldb_linked_attributes_module_ops = {
947 .name = "linked_attributes",
948 .add = linked_attributes_add,
949 .modify = linked_attributes_modify,
950 .del = linked_attributes_delete,
951 .rename = linked_attributes_rename,
952 .wait = linked_attributes_wait,