r17299: Improve the partition module to replicate attribute records into all
[samba.git] / source4 / dsdb / samdb / ldb_modules / partition.c
1 /* 
2    Partitions ldb module
3
4    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006
5
6    * NOTICE: this module is NOT released under the GNU LGPL license as
7    * other ldb code. This module is release under the GNU GPL v2 or
8    * later license.
9
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 2 of the License, or
13    (at your option) any later version.
14    
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19    
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 */
24
25 /*
26  *  Name: ldb
27  *
28  *  Component: ldb partitions module
29  *
30  *  Description: Implement LDAP partitions
31  *
32  *  Author: Andrew Bartlett
33  */
34
35 #include "includes.h"
36 #include "ldb/include/includes.h"
37
38 struct partition {
39         struct ldb_module *module;
40         const char *backend;
41         struct ldb_dn *dn;
42 };
43 struct partition_private_data {
44         struct partition **partitions;
45         struct ldb_dn **replicate;
46 };
47
48 struct partition_context {
49         struct ldb_module *module;
50         struct ldb_request *orig_req;
51
52         struct ldb_request **down_req;
53         int num_requests;
54         int finished_requests;
55 };
56
57 static struct ldb_handle *partition_init_handle(struct ldb_request *req, struct ldb_module *module)
58 {
59         struct partition_context *ac;
60         struct ldb_handle *h;
61
62         h = talloc_zero(req, struct ldb_handle);
63         if (h == NULL) {
64                 ldb_set_errstring(module->ldb, talloc_asprintf(module, "Out of Memory"));
65                 return NULL;
66         }
67
68         h->module = module;
69
70         ac = talloc_zero(h, struct partition_context);
71         if (ac == NULL) {
72                 ldb_set_errstring(module->ldb, talloc_asprintf(module, "Out of Memory"));
73                 talloc_free(h);
74                 return NULL;
75         }
76
77         h->private_data = (void *)ac;
78
79         ac->module = module;
80         ac->orig_req = req;
81
82         return h;
83 }
84
85 struct ldb_module *make_module_for_next_request(TALLOC_CTX *mem_ctx, 
86                                                 struct ldb_context *ldb,
87                                                 struct ldb_module *module) 
88 {
89         struct ldb_module *current;
90         static const struct ldb_module_ops ops; /* zero */
91         current = talloc_zero(mem_ctx, struct ldb_module);
92         if (current == NULL) {
93                 return module;
94         }
95         
96         current->ldb = ldb;
97         current->ops = &ops;
98         current->prev = NULL;
99         current->next = module;
100         return current;
101 }
102
103 struct ldb_module *find_backend(struct ldb_module *module, struct ldb_request *req, const struct ldb_dn *dn)
104 {
105         int i;
106         struct partition_private_data *data = talloc_get_type(module->private_data, 
107                                                               struct partition_private_data);
108         /* Look at base DN */
109         /* Figure out which partition it is under */
110         /* Skip the lot if 'data' isn't here yet (initialistion) */
111         for (i=0; data && data->partitions && data->partitions[i]; i++) {
112                 if (ldb_dn_compare_base(module->ldb, 
113                                         data->partitions[i]->dn, 
114                                         dn) == 0) {
115                         return make_module_for_next_request(req, module->ldb, data->partitions[i]->module);
116                 }
117         }
118
119         return module;
120 };
121
122
123 /*
124   fire the caller's callback for every entry, but only send 'done' once.
125 */
126 static int partition_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
127 {
128         struct partition_context *ac;
129
130         if (!context || !ares) {
131                 ldb_set_errstring(ldb, talloc_asprintf(ldb, "NULL Context or Result in callback"));
132                 goto error;
133         }
134
135         ac = talloc_get_type(context, struct partition_context);
136
137         if (ares->type == LDB_REPLY_ENTRY) {
138                 return ac->orig_req->callback(ldb, ac->orig_req->context, ares);
139         } else {
140                 ac->finished_requests++;
141                 if (ac->finished_requests == ac->num_requests) {
142                         return ac->orig_req->callback(ldb, ac->orig_req->context, ares);
143                 } else {
144                         talloc_free(ares);
145                         return LDB_SUCCESS;
146                 }
147         }
148 error:
149         talloc_free(ares);
150         return LDB_ERR_OPERATIONS_ERROR;
151 }
152
153 /*
154   only fire the 'last' callback, and only for START-TLS for now 
155 */
156 static int partition_other_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
157 {
158         struct partition_context *ac;
159
160         if (!context || !ares) {
161                 ldb_set_errstring(ldb, talloc_asprintf(ldb, "NULL Context or Result in callback"));
162                 goto error;
163         }
164
165         ac = talloc_get_type(context, struct partition_context);
166
167         if (ares->type == LDB_REPLY_EXTENDED && strcmp(ares->response->oid, LDB_EXTENDED_START_TLS_OID)) {
168                 ac->finished_requests++;
169                 if (ac->finished_requests == ac->num_requests) {
170                         return ac->orig_req->callback(ldb, ac->orig_req->context, ares);
171                 }
172                 talloc_free(ares);
173                 return LDB_SUCCESS;
174         }
175         ldb_set_errstring(ldb, talloc_asprintf(ldb, "partition_other_callback: Unknown reply type, only supports START_TLS"));
176 error:
177         talloc_free(ares);
178         return LDB_ERR_OPERATIONS_ERROR;
179 }
180
181
182 static int partition_send_request(struct partition_context *ac, struct ldb_module *partition)
183 {
184         int ret;
185         struct ldb_module *next = make_module_for_next_request(ac->module, ac->module->ldb, partition);
186         
187         ac->down_req = talloc_realloc(ac, ac->down_req, 
188                                         struct ldb_request *, ac->num_requests + 1);
189         if (!ac->down_req) {
190                 ldb_set_errstring(ac->module->ldb, talloc_asprintf(ac->module->ldb, "Out of memory!"));
191                 return LDB_ERR_OPERATIONS_ERROR;
192         }
193         ac->down_req[ac->num_requests] = talloc(ac, struct ldb_request);
194         if (ac->down_req[ac->num_requests] == NULL) {
195                 ldb_set_errstring(ac->module->ldb, talloc_asprintf(ac->module->ldb, "Out of memory!"));
196                 return LDB_ERR_OPERATIONS_ERROR;
197         }
198         
199         *ac->down_req[ac->num_requests] = *ac->orig_req; /* copy the request */
200         
201         if (ac->down_req[ac->num_requests]->operation == LDB_SEARCH) {
202                 ac->down_req[ac->num_requests]->callback = partition_search_callback;
203                 ac->down_req[ac->num_requests]->context = ac;
204         } else {
205                 ac->down_req[ac->num_requests]->callback = partition_other_callback;
206                 ac->down_req[ac->num_requests]->context = ac;
207         }
208
209         /* Spray off search requests to all backends */
210         ret = ldb_next_request(next, ac->down_req[ac->num_requests]); 
211         if (ret != LDB_SUCCESS) {
212                 return ret;
213         }
214         
215         ac->num_requests++;
216         return LDB_SUCCESS;
217 }
218
219 /* Send a request down to all the partitions */
220 static int partition_send_all(struct ldb_module *module, 
221                               struct partition_context *ac, struct ldb_request *req) 
222 {
223         int i;
224         struct partition_private_data *data = talloc_get_type(module->private_data, 
225                                                               struct partition_private_data);
226         int ret = partition_send_request(ac, module->next);
227         if (ret != LDB_SUCCESS) {
228                 return ret;
229         }
230         for (i=0; data && data->partitions && data->partitions[i]; i++) {
231                 ret = partition_send_request(ac, data->partitions[i]->module);
232                 if (ret != LDB_SUCCESS) {
233                         return ret;
234                 }
235         }
236         return LDB_SUCCESS;
237 }
238
239 /* Figure out which backend a request needs to be aimed at.  Some
240  * requests must be replicated to all backends */
241 static int partition_replicate(struct ldb_module *module, struct ldb_request *req, const struct ldb_dn *dn) 
242 {
243         int i;
244         struct ldb_module *backend;
245         struct partition_private_data *data = talloc_get_type(module->private_data, 
246                                                               struct partition_private_data);
247         
248         /* Is this a special DN, we need to replicate to every backend? */
249         for (i=0; data->replicate && data->replicate[i]; i++) {
250                 if (ldb_dn_compare(module->ldb, 
251                                    data->replicate[i], 
252                                    dn) == 0) {
253                         struct ldb_handle *h;
254                         struct partition_context *ac;
255                         
256                         h = partition_init_handle(req, module);
257                         if (!h) {
258                                 return LDB_ERR_OPERATIONS_ERROR;
259                         }
260                         /* return our own handle to deal with this call */
261                         req->handle = h;
262                         
263                         ac = talloc_get_type(h->private_data, struct partition_context);
264                         
265                         return partition_send_all(module, ac, req);
266                 }
267         }
268
269         /* Otherwise, we need to find the backend to fire it to */
270
271         /* Find backend */
272         backend = find_backend(module, req, req->op.add.message->dn);
273         
274         /* issue request */
275         return ldb_next_request(backend, req);
276         
277 }
278
279 /* search */
280 static int partition_search(struct ldb_module *module, struct ldb_request *req)
281 {
282         /* Find backend */
283         struct partition_private_data *data = talloc_get_type(module->private_data, 
284                                                               struct partition_private_data);
285         /* issue request */
286
287         /* (later) consider if we should be searching multiple
288          * partitions (for 'invisible' partition behaviour */
289         if (ldb_get_opaque(module->ldb, "global_catalog")) {
290                 int ret, i;
291                 struct ldb_handle *h;
292                 struct partition_context *ac;
293                 
294                 h = partition_init_handle(req, module);
295                 if (!h) {
296                         return LDB_ERR_OPERATIONS_ERROR;
297                 }
298                 /* return our own handle to deal with this call */
299                 req->handle = h;
300                 
301                 ac = talloc_get_type(h->private_data, struct partition_context);
302                 
303                 for (i=0; data && data->partitions && data->partitions[i]; i++) {
304                         /* Find all partitions under the search base */
305                         if (ldb_dn_compare_base(module->ldb, 
306                                                 req->op.search.base,
307                                                 data->partitions[i]->dn) == 0) {
308                                 ret = partition_send_request(ac, data->partitions[i]->module);
309                                 if (ret != LDB_SUCCESS) {
310                                         return ret;
311                                 }
312                         }
313                 }
314
315                 /* Perhaps we didn't match any partitions.  Try the main partition, then all partitions */
316                 if (ac->num_requests == 0) {
317                         return partition_send_all(module, ac, req);
318                 }
319                 
320                 return LDB_SUCCESS;
321         } else {
322                 struct ldb_module *backend = find_backend(module, req, req->op.search.base);
323         
324                 return ldb_next_request(backend, req);
325         }
326 }
327
328 /* add */
329 static int partition_add(struct ldb_module *module, struct ldb_request *req)
330 {
331         return partition_replicate(module, req, req->op.add.message->dn);
332 }
333
334 /* modify */
335 static int partition_modify(struct ldb_module *module, struct ldb_request *req)
336 {
337         return partition_replicate(module, req, req->op.mod.message->dn);
338 }
339
340 /* delete */
341 static int partition_delete(struct ldb_module *module, struct ldb_request *req)
342 {
343         return partition_replicate(module, req, req->op.del.dn);
344 }
345
346 /* rename */
347 static int partition_rename(struct ldb_module *module, struct ldb_request *req)
348 {
349         /* Find backend */
350         struct ldb_module *backend = find_backend(module, req, req->op.rename.olddn);
351         struct ldb_module *backend2 = find_backend(module, req, req->op.rename.newdn);
352
353         if (backend->next != backend2->next) {
354                 return LDB_ERR_AFFECTS_MULTIPLE_DSAS;
355         }
356
357         return partition_replicate(module, req, req->op.rename.olddn);
358 }
359
360 /* start a transaction */
361 static int partition_start_trans(struct ldb_module *module)
362 {
363         int i, ret;
364         struct partition_private_data *data = talloc_get_type(module->private_data, 
365                                                               struct partition_private_data);
366         /* Look at base DN */
367         /* Figure out which partition it is under */
368         /* Skip the lot if 'data' isn't here yet (initialistion) */
369         ret = ldb_next_start_trans(module);
370         if (ret != LDB_SUCCESS) {
371                 return ret;
372         }
373
374         for (i=0; data && data->partitions && data->partitions[i]; i++) {
375                 struct ldb_module *next = make_module_for_next_request(module, module->ldb, data->partitions[i]->module);
376
377                 ret = ldb_next_start_trans(next);
378                 talloc_free(next);
379                 if (ret != LDB_SUCCESS) {
380                         /* Back it out, if it fails on one */
381                         for (i--; i >= 0; i--) {
382                                 next = make_module_for_next_request(module, module->ldb, data->partitions[i]->module);
383                                 ldb_next_del_trans(next);
384                                 talloc_free(next);
385                         }
386                         return ret;
387                 }
388         }
389         return LDB_SUCCESS;
390 }
391
392 /* end a transaction */
393 static int partition_end_trans(struct ldb_module *module)
394 {
395         int i, ret, ret2 = LDB_SUCCESS;
396         struct partition_private_data *data = talloc_get_type(module->private_data, 
397                                                               struct partition_private_data);
398         ret = ldb_next_end_trans(module);
399         if (ret != LDB_SUCCESS) {
400                 return ret;
401         }
402
403         /* Look at base DN */
404         /* Figure out which partition it is under */
405         /* Skip the lot if 'data' isn't here yet (initialistion) */
406         for (i=0; data && data->partitions && data->partitions[i]; i++) {
407                 struct ldb_module *next = make_module_for_next_request(module, module->ldb, data->partitions[i]->module);
408                 
409                 ret = ldb_next_end_trans(next);
410                 talloc_free(next);
411                 if (ret != LDB_SUCCESS) {
412                         ret2 = ret;
413                 }
414         }
415
416         if (ret != LDB_SUCCESS) {
417                 /* Back it out, if it fails on one */
418                 for (i=0; data && data->partitions && data->partitions[i]; i++) {
419                         struct ldb_module *next = make_module_for_next_request(module, module->ldb, data->partitions[i]->module);
420                         ldb_next_del_trans(next);
421                         talloc_free(next);
422                 }
423         }
424         return ret;
425 }
426
427 /* delete a transaction */
428 static int partition_del_trans(struct ldb_module *module)
429 {
430         int i, ret, ret2 = LDB_SUCCESS;
431         struct partition_private_data *data = talloc_get_type(module->private_data, 
432                                                               struct partition_private_data);
433         ret = ldb_next_del_trans(module);
434         if (ret != LDB_SUCCESS) {
435                 ret2 = ret;
436         }
437
438         /* Look at base DN */
439         /* Figure out which partition it is under */
440         /* Skip the lot if 'data' isn't here yet (initialistion) */
441         for (i=0; data && data->partitions && data->partitions[i]; i++) {
442                 struct ldb_module *next = make_module_for_next_request(module, module->ldb, data->partitions[i]->module);
443                 
444                 ret = ldb_next_del_trans(next);
445                 talloc_free(next);
446                 if (ret != LDB_SUCCESS) {
447                         ret2 = ret;
448                 }
449         }
450         return ret2;
451 }
452
453 static int partition_sequence_number(struct ldb_module *module, struct ldb_request *req)
454 {
455         int i, ret;
456         uint64_t seq_number = 0;
457         struct partition_private_data *data = talloc_get_type(module->private_data, 
458                                                               struct partition_private_data);
459         ret = ldb_next_request(module, req);
460         if (ret != LDB_SUCCESS) {
461                 return ret;
462         }
463         seq_number = seq_number + req->op.seq_num.seq_num;
464
465         /* Look at base DN */
466         /* Figure out which partition it is under */
467         /* Skip the lot if 'data' isn't here yet (initialistion) */
468         for (i=0; data && data->partitions && data->partitions[i]; i++) {
469                 struct ldb_module *next = make_module_for_next_request(req, module->ldb, data->partitions[i]->module);
470                 
471                 ret = ldb_next_request(next, req);
472                 talloc_free(next);
473                 if (ret != LDB_SUCCESS) {
474                         return ret;
475                 }
476                 seq_number = seq_number + req->op.seq_num.seq_num;
477         }
478         req->op.seq_num.seq_num = seq_number;
479         return LDB_SUCCESS;
480 }
481
482 static int sort_compare(void *void1,
483                         void *void2, void *opaque)
484 {
485         struct ldb_context *ldb = talloc_get_type(opaque, struct ldb_context);
486         struct partition **pp1 = void1;
487         struct partition **pp2 = void2;
488         struct partition *partition1 = talloc_get_type(*pp1, struct partition);
489         struct partition *partition2 = talloc_get_type(*pp2, struct partition);
490
491         return ldb_dn_compare(ldb, partition1->dn, partition2->dn);
492 }
493
494 static int partition_init(struct ldb_module *module)
495 {
496         int ret, i;
497         TALLOC_CTX *mem_ctx = talloc_new(module);
498         static const char *attrs[] = { "partition", "replicateEntries", NULL };
499         struct ldb_result *res;
500         struct ldb_message *msg;
501         struct ldb_message_element *partition_attributes;
502         struct ldb_message_element *replicate_attributes;
503
504         struct partition_private_data *data;
505
506         if (!mem_ctx) {
507                 return LDB_ERR_OPERATIONS_ERROR;
508         }
509
510         data = talloc(mem_ctx, struct partition_private_data);
511         if (data == NULL) {
512                 return LDB_ERR_OPERATIONS_ERROR;
513         }
514
515         ret = ldb_search(module->ldb, ldb_dn_explode(mem_ctx, "@PARTITION"),
516                          LDB_SCOPE_BASE,
517                          NULL, attrs,
518                          &res);
519         if (ret != LDB_SUCCESS) {
520                 talloc_free(mem_ctx);
521                 return ret;
522         }
523         talloc_steal(mem_ctx, res);
524         if (res->count == 0) {
525                 talloc_free(mem_ctx);
526                 return ldb_next_init(module);
527         }
528
529         if (res->count > 1) {
530                 talloc_free(mem_ctx);
531                 return LDB_ERR_CONSTRAINT_VIOLATION;
532         }
533
534         msg = res->msgs[0];
535
536         partition_attributes = ldb_msg_find_element(msg, "partition");
537         if (!partition_attributes) {
538                 ldb_set_errstring(module->ldb, 
539                                   talloc_asprintf(module, "partition_init: "
540                                                   "no partitions specified"));
541                 return LDB_ERR_CONSTRAINT_VIOLATION;
542         }
543         data->partitions = talloc_array(data, struct partition *, partition_attributes->num_values + 1);
544         if (!data->partitions) {
545                 talloc_free(mem_ctx);
546                 return LDB_ERR_OPERATIONS_ERROR;
547         }
548         for (i=0; i < partition_attributes->num_values; i++) {
549                 char *base = talloc_strdup(data->partitions, (char *)partition_attributes->values[i].data);
550                 char *p = strchr(base, ':');
551                 if (!p) {
552                         ldb_set_errstring(module->ldb, 
553                                           talloc_asprintf(module, "partition_init: "
554                                                           "invalid form for partition record (missing ':'): %s", base));
555                         return LDB_ERR_CONSTRAINT_VIOLATION;
556                 }
557                 p[0] = '\0';
558                 p++;
559                 if (!p[0]) {
560                         ldb_set_errstring(module->ldb, 
561                                           talloc_asprintf(module, "partition_init: "
562                                                           "invalid form for partition record (missing backend database): %s", base));
563                         return LDB_ERR_CONSTRAINT_VIOLATION;
564                 }
565                 data->partitions[i] = talloc(data->partitions, struct partition);
566                 if (!data->partitions[i]) {
567                         talloc_free(mem_ctx);
568                         return LDB_ERR_OPERATIONS_ERROR;
569                 }
570
571                 data->partitions[i]->dn = ldb_dn_explode(data->partitions[i], base);
572                 if (!data->partitions[i]->dn) {
573                         ldb_set_errstring(module->ldb, 
574                                           talloc_asprintf(module, "partition_init: "
575                                                           "invalid DN in partition record: %s", base));
576                         return LDB_ERR_CONSTRAINT_VIOLATION;
577                 }
578
579                 data->partitions[i]->backend = private_path(data->partitions[i], p);
580                 ret = ldb_connect_backend(module->ldb, data->partitions[i]->backend, NULL, &data->partitions[i]->module);
581                 if (ret != LDB_SUCCESS) {
582                         return ret;
583                 }
584         }
585         data->partitions[i] = NULL;
586
587         /* sort these into order, most to least specific */
588         ldb_qsort(data->partitions, partition_attributes->num_values, sizeof(*data->partitions), 
589                   module->ldb, sort_compare);
590
591         for (i=0; data->partitions[i]; i++) {
592                 struct ldb_request *req;
593                 req = talloc_zero(mem_ctx, struct ldb_request);
594                 if (req == NULL) {
595                         ldb_debug(module->ldb, LDB_DEBUG_ERROR, "partition: Out of memory!\n");
596                         return LDB_ERR_OPERATIONS_ERROR;
597                 }
598                 
599                 req->operation = LDB_REQ_REGISTER_PARTITION;
600                 req->op.reg_partition.dn = data->partitions[i]->dn;
601                 
602                 ret = ldb_request(module->ldb, req);
603                 if (ret != LDB_SUCCESS) {
604                         ldb_debug(module->ldb, LDB_DEBUG_ERROR, "partition: Unable to register partition with rootdse!\n");
605                         return LDB_ERR_OTHER;
606                 }
607                 talloc_free(req);
608         }
609
610         replicate_attributes = ldb_msg_find_element(msg, "replicateEntries");
611         if (!replicate_attributes) {
612                 ldb_set_errstring(module->ldb, 
613                                   talloc_asprintf(module, "partition_init: "
614                                                   "no entries to replicate specified"));
615                 data->replicate = NULL;
616         } else {
617                 data->replicate = talloc_array(data, struct ldb_dn *, replicate_attributes->num_values + 1);
618                 if (!data->replicate) {
619                         talloc_free(mem_ctx);
620                         return LDB_ERR_OPERATIONS_ERROR;
621                 }
622                 
623                 for (i=0; i < replicate_attributes->num_values; i++) {
624                         data->replicate[i] = ldb_dn_explode(data->replicate[i], replicate_attributes->values[i].data);
625                         if (!data->replicate[i]) {
626                                 ldb_set_errstring(module->ldb, 
627                                                   talloc_asprintf(module, "partition_init: "
628                                                                   "invalid DN in partition replicate record: %s", 
629                                                                   replicate_attributes->values[i].data));
630                                 return LDB_ERR_CONSTRAINT_VIOLATION;
631                         }
632                 }
633                 data->replicate[i] = NULL;
634         }
635
636         module->private_data = data;
637         talloc_steal(module, data);
638         
639         talloc_free(mem_ctx);
640         return ldb_next_init(module);
641 }
642
643 static int partition_wait_none(struct ldb_handle *handle) {
644         struct partition_context *ac;
645         int ret;
646         int i;
647     
648         if (!handle || !handle->private_data) {
649                 return LDB_ERR_OPERATIONS_ERROR;
650         }
651
652         if (handle->state == LDB_ASYNC_DONE) {
653                 return handle->status;
654         }
655
656         handle->state = LDB_ASYNC_PENDING;
657         handle->status = LDB_SUCCESS;
658
659         ac = talloc_get_type(handle->private_data, struct partition_context);
660
661         for (i=0; i < ac->num_requests; i++) {
662                 ret = ldb_wait(ac->down_req[i]->handle, LDB_WAIT_NONE);
663                 
664                 if (ret != LDB_SUCCESS) {
665                         handle->status = ret;
666                         goto done;
667                 }
668                 if (ac->down_req[i]->handle->status != LDB_SUCCESS) {
669                         handle->status = ac->down_req[i]->handle->status;
670                         goto done;
671                 }
672                 
673                 if (ac->down_req[i]->handle->state != LDB_ASYNC_DONE) {
674                         return LDB_SUCCESS;
675                 }
676         }
677
678         ret = LDB_SUCCESS;
679
680 done:
681         handle->state = LDB_ASYNC_DONE;
682         return ret;
683 }
684
685
686 static int partition_wait_all(struct ldb_handle *handle) {
687
688         int ret;
689
690         while (handle->state != LDB_ASYNC_DONE) {
691                 ret = partition_wait_none(handle);
692                 if (ret != LDB_SUCCESS) {
693                         return ret;
694                 }
695         }
696
697         return handle->status;
698 }
699
700 static int partition_wait(struct ldb_handle *handle, enum ldb_wait_type type)
701 {
702         if (type == LDB_WAIT_ALL) {
703                 return partition_wait_all(handle);
704         } else {
705                 return partition_wait_none(handle);
706         }
707 }
708
709 static const struct ldb_module_ops partition_ops = {
710         .name              = "partition",
711         .init_context      = partition_init,
712         .search            = partition_search,
713         .add               = partition_add,
714         .modify            = partition_modify,
715         .del               = partition_delete,
716         .rename            = partition_rename,
717         .start_transaction = partition_start_trans,
718         .end_transaction   = partition_end_trans,
719         .del_transaction   = partition_del_trans,
720         .sequence_number   = partition_sequence_number,
721         .wait              = partition_wait
722 };
723
724 int ldb_partition_init(void)
725 {
726         return ldb_register_module(&partition_ops);
727 }