r25781: Handle and test linked attribute renames.
[samba.git] / source4 / dsdb / samdb / ldb_modules / linked_attributes.c
1 /* 
2    ldb database library
3
4    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007
5
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.
10    
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.
15    
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/>.
18 */
19
20 /*
21  *  Name: ldb
22  *
23  *  Component: ldb linked_attributes module
24  *
25  *  Description: Module to ensure linked attribute pairs remain in sync
26  *
27  *  Author: Andrew Bartlett
28  */
29
30 #include "includes.h"
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"
35
36 struct linked_attributes_context {
37         struct ldb_module *module;
38         struct ldb_handle *handle;
39         struct ldb_request *orig_req;
40
41         struct ldb_request **down_req;
42         int num_requests;
43         int finished_requests;
44
45         const char **linked_attrs;
46 };
47
48 static struct linked_attributes_context *linked_attributes_init_handle(struct ldb_request *req, 
49                                                                  struct ldb_module *module)
50 {
51         struct linked_attributes_context *ac;
52         struct ldb_handle *h;
53
54         h = talloc_zero(req, struct ldb_handle);
55         if (h == NULL) {
56                 ldb_set_errstring(module->ldb, "Out of Memory");
57                 return NULL;
58         }
59
60         h->module = module;
61
62         ac = talloc_zero(h, struct linked_attributes_context);
63         if (ac == NULL) {
64                 ldb_set_errstring(module->ldb, "Out of Memory");
65                 talloc_free(h);
66                 return NULL;
67         }
68
69         h->private_data = ac;
70
71         ac->module = module;
72         ac->handle = h;
73         ac->orig_req = req;
74
75         req->handle = h;
76
77         return ac;
78 }
79
80 /* add */
81 static int linked_attributes_add(struct ldb_module *module, struct ldb_request *req)
82 {
83         int i, j, ret;
84         struct linked_attributes_context *ac;
85
86         const struct dsdb_schema *schema = dsdb_get_schema(module->ldb);
87         if (!schema) {
88                 /* without schema, this doesn't make any sense */
89                 return ldb_next_request(module, req);
90         }
91
92         if (ldb_dn_is_special(req->op.mod.message->dn)) {
93                 /* do not manipulate our control entries */
94                 return ldb_next_request(module, req);
95         }
96
97
98         ac = linked_attributes_init_handle(req, module);
99         if (!ac) {
100                 return LDB_ERR_OPERATIONS_ERROR;
101         }
102         
103         /* prepare the first operation */
104         ac->down_req = talloc_realloc(ac, ac->down_req, 
105                                       struct ldb_request *, 1);
106         if (!ac->down_req) {
107                 ldb_oom(ac->module->ldb);
108                 return LDB_ERR_OPERATIONS_ERROR;
109         }
110         
111         ac->down_req[0] = talloc(ac->down_req, struct ldb_request);
112         if (!ac->down_req[0]) {
113                 ldb_oom(ac->module->ldb);
114                 return LDB_ERR_OPERATIONS_ERROR;
115         }
116         *(ac->down_req[0]) = *req; /* copy the request */
117         
118         ac->num_requests++;
119         
120         /* Run the original request */
121         ret = ldb_next_request(module, req);
122         if (ret != LDB_SUCCESS) {
123                 return ret;
124         }
125
126         for (i=0; i < req->op.add.message->num_elements; i++) {
127                 const struct dsdb_attribute *target_attr;
128                 const struct ldb_message_element *el = &req->op.add.message->elements[i];
129                 const struct dsdb_attribute *schema_attr
130                         = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
131                 if (!schema_attr) {
132                         ldb_asprintf_errstring(module->ldb, 
133                                                "attribute %s is not a valid attribute in schema", req->op.add.message->elements[i].name);
134                         return LDB_ERR_OBJECT_CLASS_VIOLATION;                  
135                 }
136                 /* We have a valid attribute, not find out if it is linked */
137                 if (schema_attr->linkID == 0) {
138                         continue;
139                 }
140                 
141                 if ((schema_attr->linkID & 1) == 1) {
142                         /* Odd is for the target.  Illigal to modify */
143                         ldb_asprintf_errstring(module->ldb, 
144                                                "attribute %s must not be modified directly, it is a linked attribute", req->op.add.message->elements[i].name);
145                         return LDB_ERR_UNWILLING_TO_PERFORM;
146                 }
147                 
148                 /* Even link IDs are for the originating attribute */
149                 
150                 /* Now find the target attribute */
151                 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID + 1);
152                 if (!target_attr) {
153                         ldb_asprintf_errstring(module->ldb, 
154                                                "attribute %s does not have valid link target", req->op.add.message->elements[i].name);
155                         return LDB_ERR_OBJECT_CLASS_VIOLATION;                  
156                 }
157
158                 /* Prepare the modify (add element) on the targets */
159
160                 /* For each value being added, we need to setup the modify */
161                 for (j=0; j < el->num_values; j++) {
162                         struct ldb_request *new_req;
163                         /* Create the modify request */
164                         struct ldb_message *new_msg = ldb_msg_new(ac->down_req);
165                         if (!new_msg) {
166                                 ldb_oom(module->ldb);
167                                 return LDB_ERR_OPERATIONS_ERROR;
168                         }
169                         new_msg->dn = ldb_dn_new(new_msg, module->ldb, (char *)el->values[j].data);
170                         if (!new_msg->dn) {
171                                 ldb_asprintf_errstring(module->ldb, 
172                                                "attribute %s value %s was not a valid DN", req->op.add.message->elements[i].name,
173                                                        el->values[j].data);
174                                 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
175                         }
176
177                         ret = ldb_msg_add_empty(new_msg, target_attr->lDAPDisplayName, 
178                                                 LDB_FLAG_MOD_ADD, NULL);
179                         if (ret != LDB_SUCCESS) {
180                                 return ret;
181                         }
182                         
183                         ret = ldb_msg_add_string(new_msg, target_attr->lDAPDisplayName, 
184                                                  ldb_dn_get_linearized(ac->orig_req->op.add.message->dn));
185                         if (ret != LDB_SUCCESS) {
186                                 return ret;
187                         }
188
189                         ret = ldb_build_mod_req(&new_req, module->ldb, ac->down_req,
190                                                 new_msg,
191                                                 NULL,
192                                                 NULL,
193                                                 NULL);
194                         if (ret != LDB_SUCCESS) {
195                                 return ret;
196                         }
197                         
198                         talloc_steal(new_req, new_msg);
199                         
200                         ldb_set_timeout_from_prev_req(module->ldb, req, new_req);
201                         
202                         /* Now add it to the list */
203                         ac->down_req = talloc_realloc(ac, ac->down_req, 
204                                                       struct ldb_request *, ac->num_requests + 1);
205                         if (!ac->down_req) {
206                                 ldb_oom(ac->module->ldb);
207                                 return LDB_ERR_OPERATIONS_ERROR;
208                         }
209                         ac->down_req[ac->num_requests] = new_req;
210                         ac->num_requests++;
211
212                         /* Run the new request */
213                         ret = ldb_next_request(module, new_req);
214                         if (ret != LDB_SUCCESS) {
215                                 return ret;
216                         }
217                 }
218         }
219         return ret;
220 }
221
222 /* modify */
223 static int linked_attributes_modify(struct ldb_module *module, struct ldb_request *req)
224 {
225         /* Look over list of modifications */
226         /* Find if any are for linked attributes */
227         /* Determine the effect of the modification */
228         /* Apply the modify to the linked entry */
229
230         int i, j, ret;
231         struct linked_attributes_context *ac;
232
233         const struct dsdb_schema *schema = dsdb_get_schema(module->ldb);
234         if (!schema) {
235                 /* without schema, this doesn't make any sense */
236                 return ldb_next_request(module, req);
237         }
238
239         if (ldb_dn_is_special(req->op.mod.message->dn)) {
240                 /* do not manipulate our control entries */
241                 return ldb_next_request(module, req);
242         }
243
244
245         ac = linked_attributes_init_handle(req, module);
246         if (!ac) {
247                 return LDB_ERR_OPERATIONS_ERROR;
248         }
249         
250         /* prepare the first operation */
251         ac->down_req = talloc_realloc(ac, ac->down_req, 
252                                       struct ldb_request *, 1);
253         if (!ac->down_req) {
254                 ldb_oom(ac->module->ldb);
255                 return LDB_ERR_OPERATIONS_ERROR;
256         }
257         
258         ac->down_req[0] = talloc(ac->down_req, struct ldb_request);
259         if (!ac->down_req[0]) {
260                 ldb_oom(ac->module->ldb);
261                 return LDB_ERR_OPERATIONS_ERROR;
262         }
263         *(ac->down_req[0]) = *req; /* copy the request */
264         
265         ac->num_requests++;
266         
267         /* Run the original request */
268         ret = ldb_next_request(module, req);
269         if (ret != LDB_SUCCESS) {
270                 return ret;
271         }
272
273         for (i=0; i < req->op.mod.message->num_elements; i++) {
274                 const struct dsdb_attribute *target_attr;
275                 const struct ldb_message_element *el = &req->op.mod.message->elements[i];
276                 const struct dsdb_attribute *schema_attr
277                         = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
278                 if (!schema_attr) {
279                         ldb_asprintf_errstring(module->ldb, 
280                                                "attribute %s is not a valid attribute in schema", req->op.mod.message->elements[i].name);
281                         return LDB_ERR_OBJECT_CLASS_VIOLATION;                  
282                 }
283                 /* We have a valid attribute, not find out if it is linked */
284                 if (schema_attr->linkID == 0) {
285                         continue;
286                 }
287                 
288                 if ((schema_attr->linkID & 1) == 1) {
289                         /* Odd is for the target.  Illigal to modify */
290                         ldb_asprintf_errstring(module->ldb, 
291                                                "attribute %s must not be modified directly, it is a linked attribute", req->op.mod.message->elements[i].name);
292                         return LDB_ERR_UNWILLING_TO_PERFORM;
293                 }
294                 
295                 /* Even link IDs are for the originating attribute */
296                 
297                 /* Now find the target attribute */
298                 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID + 1);
299                 if (!target_attr) {
300                         ldb_asprintf_errstring(module->ldb, 
301                                                "attribute %s does not have valid link target", req->op.mod.message->elements[i].name);
302                         return LDB_ERR_OBJECT_CLASS_VIOLATION;                  
303                 }
304
305                 if ((el->flags & LDB_FLAG_MOD_MASK) == LDB_FLAG_MOD_REPLACE) {
306                         ldb_asprintf_errstring(module->ldb, 
307                                                "attribute %s may not be replaced, only added or deleted", req->op.mod.message->elements[i].name);
308                         return LDB_ERR_UNWILLING_TO_PERFORM;
309                 }
310                 /* Prepare the modify (mod element) on the targets */
311
312                 /* For each value being moded, we need to setup the modify */
313                 for (j=0; j < el->num_values; j++) {
314                         struct ldb_request *new_req;
315                         /* Create the modify request */
316                         struct ldb_message *new_msg = ldb_msg_new(ac->down_req);
317                         if (!new_msg) {
318                                 ldb_oom(module->ldb);
319                                 return LDB_ERR_OPERATIONS_ERROR;
320                         }
321                         new_msg->dn = ldb_dn_new(new_msg, module->ldb, (char *)el->values[j].data);
322                         if (!new_msg->dn) {
323                                 ldb_asprintf_errstring(module->ldb, 
324                                                "attribute %s value %s was not a valid DN", req->op.mod.message->elements[i].name,
325                                                        el->values[j].data);
326                                 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
327                         }
328
329                         ret = ldb_msg_add_empty(new_msg, target_attr->lDAPDisplayName, 
330                                                 el->flags & LDB_FLAG_MOD_MASK, NULL);
331                         if (ret != LDB_SUCCESS) {
332                                 return ret;
333                         }
334                         
335                         ret = ldb_msg_add_string(new_msg, target_attr->lDAPDisplayName, 
336                                                  ldb_dn_get_linearized(ac->orig_req->op.add.message->dn));
337                         if (ret != LDB_SUCCESS) {
338                                 return ret;
339                         }
340
341                         ret = ldb_build_mod_req(&new_req, module->ldb, ac->down_req,
342                                                 new_msg,
343                                                 NULL,
344                                                 NULL,
345                                                 NULL);
346                         if (ret != LDB_SUCCESS) {
347                                 return ret;
348                         }
349                         
350                         talloc_steal(new_req, new_msg);
351                         
352                         ldb_set_timeout_from_prev_req(module->ldb, req, new_req);
353                         
354                         /* Now add it to the list */
355                         ac->down_req = talloc_realloc(ac, ac->down_req, 
356                                                       struct ldb_request *, ac->num_requests + 1);
357                         if (!ac->down_req) {
358                                 ldb_oom(ac->module->ldb);
359                                 return LDB_ERR_OPERATIONS_ERROR;
360                         }
361                         ac->down_req[ac->num_requests] = new_req;
362                         ac->num_requests++;
363
364                         /* Run the new request */
365                         ret = ldb_next_request(module, new_req);
366                         if (ret != LDB_SUCCESS) {
367                                 return ret;
368                         }
369                 }
370         }
371         return ret;
372 }
373
374 static int setup_modifies(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, 
375                           struct linked_attributes_context *ac,
376                           struct ldb_message *msg, 
377                           struct ldb_dn *olddn, struct ldb_dn *newdn) 
378 {
379         int i, j, ret = LDB_SUCCESS;
380         const struct dsdb_schema *schema = dsdb_get_schema(ldb);
381         /* Look up each of the returned attributes */
382         /* Find their schema */
383         /* And it is an actual entry: now create a series of modify requests */
384         for (i=0; i < msg->num_elements; i++) {
385                 int otherid;
386                 const struct dsdb_attribute *target_attr;
387                 const struct ldb_message_element *el = &msg->elements[i];
388                 const struct dsdb_attribute *schema_attr
389                         = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
390                 if (!schema_attr) {
391                         ldb_asprintf_errstring(ldb, 
392                                                "attribute %s is not a valid attribute in schema", el->name);
393                         return LDB_ERR_OBJECT_CLASS_VIOLATION;                  
394                 }
395                 /* We have a valid attribute, but if it's not linked they maybe we just got an extra return on our search... */
396                 if (schema_attr->linkID == 0) {
397                         continue;
398                 }
399                 
400                 /* Depending on which direction this link is in, we need to find it's partner */
401                 if ((schema_attr->linkID & 1) == 1) {
402                         otherid = schema_attr->linkID - 1;
403                 } else {
404                         otherid = schema_attr->linkID + 1;
405                 }
406                 
407                 /* Now find the target attribute */
408                 target_attr = dsdb_attribute_by_linkID(schema, otherid);
409                 if (!target_attr) {
410                         ldb_asprintf_errstring(ldb, 
411                                                "attribute %s does not have valid link target", el->name);
412                         return LDB_ERR_OBJECT_CLASS_VIOLATION;                  
413                 }
414                 
415                 /* For each value being moded, we need to setup the modify */
416                 for (j=0; j < el->num_values; j++) {
417                         struct ldb_message_element *ret_el;
418                         struct ldb_request *new_req;
419                         /* Create the modify request */
420                         struct ldb_message *new_msg = ldb_msg_new(ac->down_req);
421                         if (!new_msg) {
422                                 ldb_oom(ldb);
423                                 return LDB_ERR_OPERATIONS_ERROR;
424                         }
425                         new_msg->dn = ldb_dn_new(new_msg, ldb, (char *)el->values[j].data);
426                         if (!new_msg->dn) {
427                                 ldb_asprintf_errstring(ldb, 
428                                                        "attribute %s value %s was not a valid DN", msg->elements[i].name,
429                                                        el->values[j].data);
430                                 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
431                         }
432                         
433                         if (olddn) {
434                                 ret = ldb_msg_add_empty(new_msg, target_attr->lDAPDisplayName, 
435                                                         LDB_FLAG_MOD_DELETE, &ret_el);
436                                 if (ret != LDB_SUCCESS) {
437                                         return ret;
438                                 }       
439                                 ret_el->values = talloc_array(new_msg, struct ldb_val, 1);
440                                 if (!ret_el->values) {
441                                         ldb_oom(ldb);
442                                         return LDB_ERR_OPERATIONS_ERROR;
443                                 }
444                                 ret_el->values[0] = data_blob_string_const(ldb_dn_get_linearized(olddn));
445                                 ret_el->num_values = 1;
446                         }
447                         
448                         if (newdn) {
449                                 ret = ldb_msg_add_empty(new_msg, target_attr->lDAPDisplayName, 
450                                                         LDB_FLAG_MOD_ADD, &ret_el);
451                                 if (ret != LDB_SUCCESS) {
452                                         return ret;
453                                 }       
454                                 ret_el->values = talloc_array(new_msg, struct ldb_val, 1);
455                                 if (!ret_el->values) {
456                                         ldb_oom(ldb);
457                                         return LDB_ERR_OPERATIONS_ERROR;
458                                 }
459                                 ret_el->values[0] = data_blob_string_const(ldb_dn_get_linearized(newdn));
460                                 ret_el->num_values = 1;
461                         }
462
463                         ret = ldb_build_mod_req(&new_req, ldb, ac->down_req,
464                                                 new_msg,
465                                                 NULL,
466                                                 NULL,
467                                                 NULL);
468                         if (ret != LDB_SUCCESS) {
469                                 return ret;
470                         }
471                         
472                         talloc_steal(new_req, new_msg);
473                         
474                         ldb_set_timeout_from_prev_req(ldb, ac->orig_req, new_req);
475                         
476                         /* Now add it to the list */
477                         ac->down_req = talloc_realloc(ac, ac->down_req, 
478                                                       struct ldb_request *, ac->num_requests + 1);
479                         if (!ac->down_req) {
480                                 ldb_oom(ldb);
481                                 return LDB_ERR_OPERATIONS_ERROR;
482                         }
483                         ac->down_req[ac->num_requests] = new_req;
484                         ac->num_requests++;
485                         
486                         /* Run the new request */
487                         ret = ldb_next_request(ac->module, new_req);
488                         if (ret != LDB_SUCCESS) {
489                                 return ret;
490                         }
491                 }
492         }
493         return ret;
494 }
495
496 static int linked_attributes_rename_del_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares) 
497 {
498         struct ldb_request *req;
499         struct linked_attributes_context *ac = talloc_get_type(context, struct linked_attributes_context);
500         struct ldb_dn *olddn, *newdn;
501         TALLOC_CTX *mem_ctx = talloc_new(ac);
502     
503         if (!mem_ctx) {
504                 ldb_oom(ldb);
505                 return LDB_ERR_OPERATIONS_ERROR;
506         }
507         
508         switch (ac->orig_req->operation) {
509         case LDB_DELETE:
510         {
511                 olddn = ac->orig_req->op.del.dn;
512                 newdn = NULL;
513                 break;
514         } 
515         case LDB_RENAME:
516         {
517                 olddn = ac->orig_req->op.rename.olddn;
518                 newdn = ac->orig_req->op.rename.newdn;
519                 break;
520         }       
521         default:
522                 return LDB_ERR_OPERATIONS_ERROR;
523         }
524         
525
526         /* OK, we have one search result here: */
527
528         /* Only entries are interesting, and we only want the olddn */
529         if (ares->type == LDB_REPLY_ENTRY
530             && ldb_dn_compare(ares->message->dn, olddn) == 0) {
531                 /* only bother at all if there were some linked attributes found */
532                 if (ares->message->num_elements > 0) {
533                         return setup_modifies(ldb, mem_ctx, ac,
534                                               ares->message, olddn, newdn);
535                 }
536                 talloc_free(ares);
537                 return LDB_SUCCESS;
538         } else if (ares->type == LDB_REPLY_ENTRY) {
539                 /* Guh?  We only asked for this DN */
540                 return LDB_ERR_OPERATIONS_ERROR;
541         } else if (ares->type == LDB_REPLY_DONE) {
542                 req = talloc(mem_ctx, struct ldb_request);
543                 *req = *ac->orig_req;
544                 talloc_free(ares);
545
546                 ac->down_req = talloc_realloc(ac, ac->down_req, 
547                                               struct ldb_request *, ac->num_requests + 1);
548                 if (!ac->down_req) {
549                         ldb_oom(ldb);
550                         return LDB_ERR_OPERATIONS_ERROR;
551                 }
552                 ac->down_req[ac->num_requests] = req;
553                 ac->num_requests++;
554                 
555                 return ldb_next_request(ac->module, req);
556
557         } else {
558                 talloc_free(ares);
559                 return LDB_SUCCESS;
560         }
561         
562         
563 }
564 /* rename */
565 static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req)
566 {
567         /* Look up list of linked attributes */
568         const char **attrs;
569         WERROR werr;
570         int ret;
571         struct linked_attributes_context *ac;
572         struct ldb_request *new_req;
573         const struct dsdb_schema *schema = dsdb_get_schema(module->ldb);
574         if (!schema) {
575                 /* without schema, this doesn't make any sense */
576                 return ldb_next_request(module, req);
577         }
578
579         /* This gets complex:  We need to:
580            - Do a search for the entry 
581            - Wait for these result to appear
582            - In the callback for the result, issue a modify request based on the linked attributes found
583            - Wait for each modify result
584            - Regain our sainity 
585         */
586
587         ac = linked_attributes_init_handle(req, module);
588         if (!ac) {
589                 return LDB_ERR_OPERATIONS_ERROR;
590         }
591         
592         werr = dsdb_linked_attribute_lDAPDisplayName_list(schema, ac, &attrs);
593         if (!W_ERROR_IS_OK(werr)) {
594                 return LDB_ERR_OPERATIONS_ERROR;
595         }
596         
597         ret = ldb_build_search_req(&new_req, module->ldb, req,
598                                    req->op.rename.olddn, 
599                                    LDB_SCOPE_BASE,
600                                    "(objectClass=*)",
601                                    attrs,
602                                    NULL, 
603                                    ac, 
604                                    linked_attributes_rename_del_search_callback);
605
606         if (ret != LDB_SUCCESS) {
607                 return ret;
608         }
609
610         talloc_steal(new_req, attrs);
611
612         ac->down_req = talloc_realloc(ac, ac->down_req, 
613                                         struct ldb_request *, ac->num_requests + 1);
614         if (!ac->down_req) {
615                 ldb_oom(ac->module->ldb);
616                 return LDB_ERR_OPERATIONS_ERROR;
617         }
618         ac->down_req[ac->num_requests] = new_req;
619         if (req == NULL) {
620                 ldb_oom(ac->module->ldb);
621                 return LDB_ERR_OPERATIONS_ERROR;
622         }
623         ac->num_requests++;
624         return ldb_next_request(module, new_req);
625 }
626
627 /* delete */
628 static int linked_attributes_delete(struct ldb_module *module, struct ldb_request *req)
629 {
630         /* Look up list of linked attributes */
631         const char **attrs;
632         WERROR werr;
633         int ret;
634         struct ldb_request *new_req;
635         struct linked_attributes_context *ac;
636         const struct dsdb_schema *schema = dsdb_get_schema(module->ldb);
637         if (!schema) {
638                 /* without schema, this doesn't make any sense */
639                 return ldb_next_request(module, req);
640         }
641
642         /* This gets complex:  We need to:
643            - Do a search for the entry 
644            - Wait for these result to appear
645            - In the callback for the result, issue a modify request based on the linked attributes found
646            - Wait for each modify result
647            - Regain our sainity 
648         */
649
650         ac = linked_attributes_init_handle(req, module);
651         if (!ac) {
652                 return LDB_ERR_OPERATIONS_ERROR;
653         }
654         
655         werr = dsdb_linked_attribute_lDAPDisplayName_list(schema, ac, &attrs);
656         if (!W_ERROR_IS_OK(werr)) {
657                 return LDB_ERR_OPERATIONS_ERROR;
658         };
659         
660         ret = ldb_build_search_req(&new_req, module->ldb, req,
661                                    req->op.del.dn, 
662                                    LDB_SCOPE_BASE,
663                                    "(objectClass=*)",
664                                    attrs,
665                                    NULL, 
666                                    ac, 
667                                    linked_attributes_rename_del_search_callback);
668
669         if (ret != LDB_SUCCESS) {
670                 return ret;
671         }
672
673         talloc_steal(new_req, attrs);
674
675         ac->down_req = talloc_realloc(ac, ac->down_req, 
676                                         struct ldb_request *, ac->num_requests + 1);
677         if (!ac->down_req) {
678                 ldb_oom(ac->module->ldb);
679                 return LDB_ERR_OPERATIONS_ERROR;
680         }
681         ac->down_req[ac->num_requests] = new_req;
682         if (req == NULL) {
683                 ldb_oom(ac->module->ldb);
684                 return LDB_ERR_OPERATIONS_ERROR;
685         }
686         ac->num_requests++;
687         return ldb_next_request(module, new_req);
688 }
689
690
691 static int linked_attributes_wait_none(struct ldb_handle *handle) {
692         struct linked_attributes_context *ac;
693         int i, ret = LDB_ERR_OPERATIONS_ERROR;
694         if (!handle || !handle->private_data) {
695                 return LDB_ERR_OPERATIONS_ERROR;
696         }
697
698         if (handle->state == LDB_ASYNC_DONE) {
699                 return handle->status;
700         }
701
702         handle->state = LDB_ASYNC_PENDING;
703         handle->status = LDB_SUCCESS;
704
705         ac = talloc_get_type(handle->private_data, struct linked_attributes_context);
706
707         for (i=0; i < ac->num_requests; i++) {
708                 ret = ldb_wait(ac->down_req[i]->handle, LDB_WAIT_NONE);
709                 
710                 if (ret != LDB_SUCCESS) {
711                         handle->status = ret;
712                         goto done;
713                 }
714                 if (ac->down_req[i]->handle->status != LDB_SUCCESS) {
715                         handle->status = ac->down_req[i]->handle->status;
716                         goto done;
717                 }
718                 
719                 if (ac->down_req[i]->handle->state != LDB_ASYNC_DONE) {
720                         return LDB_SUCCESS;
721                 }
722         }
723
724 done:
725         handle->state = LDB_ASYNC_DONE;
726         return ret;
727
728 }
729
730 static int linked_attributes_wait_all(struct ldb_handle *handle) {
731
732         int ret;
733
734         while (handle->state != LDB_ASYNC_DONE) {
735                 ret = linked_attributes_wait_none(handle);
736                 if (ret != LDB_SUCCESS) {
737                         return ret;
738                 }
739         }
740
741         return handle->status;
742 }
743
744 static int linked_attributes_wait(struct ldb_handle *handle, enum ldb_wait_type type)
745 {
746         if (type == LDB_WAIT_ALL) {
747                 return linked_attributes_wait_all(handle);
748         } else {
749                 return linked_attributes_wait_none(handle);
750         }
751 }
752
753 static const struct ldb_module_ops linked_attributes_ops = {
754         .name              = "linked_attributes",
755         .add               = linked_attributes_add,
756         .modify            = linked_attributes_modify,
757         .del               = linked_attributes_delete,
758         .rename            = linked_attributes_rename,
759         .wait              = linked_attributes_wait,
760 };
761
762 int ldb_linked_attributes_init(void)
763 {
764         return ldb_register_module(&linked_attributes_ops);
765 }