4 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006
5 Copyright (C) Stefan Metzmacher <metze@samba.org> 2007
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 partitions module
26 * Description: Implement LDAP partitions
28 * Author: Andrew Bartlett
29 * Author: Stefan Metzmacher
32 #include "dsdb/samdb/ldb_modules/partition.h"
35 struct ldb_module
*module
;
36 struct ldb_request
*req
;
39 struct partition_context
{
40 struct ldb_module
*module
;
41 struct ldb_request
*req
;
44 struct part_request
*part_req
;
46 int finished_requests
;
49 static struct partition_context
*partition_init_ctx(struct ldb_module
*module
, struct ldb_request
*req
)
51 struct partition_context
*ac
;
53 ac
= talloc_zero(req
, struct partition_context
);
55 ldb_set_errstring(ldb_module_get_ctx(module
), "Out of Memory");
66 * helper functions to call the next module in chain
69 int partition_request(struct ldb_module
*module
, struct ldb_request
*request
)
72 switch (request
->operation
) {
74 PARTITION_FIND_OP(module
, search
);
75 ret
= module
->ops
->search(module
, request
);
78 PARTITION_FIND_OP(module
, add
);
79 ret
= module
->ops
->add(module
, request
);
82 PARTITION_FIND_OP(module
, modify
);
83 ret
= module
->ops
->modify(module
, request
);
86 PARTITION_FIND_OP(module
, del
);
87 ret
= module
->ops
->del(module
, request
);
90 PARTITION_FIND_OP(module
, rename
);
91 ret
= module
->ops
->rename(module
, request
);
94 PARTITION_FIND_OP(module
, extended
);
95 ret
= module
->ops
->extended(module
, request
);
98 PARTITION_FIND_OP(module
, request
);
99 ret
= module
->ops
->request(module
, request
);
102 if (ret
== LDB_SUCCESS
) {
105 if (!ldb_errstring(ldb_module_get_ctx(module
))) {
106 /* Set a default error string, to place the blame somewhere */
107 ldb_asprintf_errstring(ldb_module_get_ctx(module
),
108 "error in module %s: %s (%d)",
110 ldb_strerror(ret
), ret
);
115 static struct dsdb_partition
*find_partition(struct partition_private_data
*data
,
117 struct ldb_request
*req
)
120 struct ldb_control
*partition_ctrl
;
122 /* see if the request has the partition DN specified in a
123 * control. The repl_meta_data module can specify this to
124 * ensure that replication happens to the right partition
126 partition_ctrl
= ldb_request_get_control(req
, DSDB_CONTROL_CURRENT_PARTITION_OID
);
127 if (partition_ctrl
) {
128 const struct dsdb_control_current_partition
*partition
;
129 partition
= talloc_get_type(partition_ctrl
->data
,
130 struct dsdb_control_current_partition
);
131 if (partition
!= NULL
) {
140 /* Look at base DN */
141 /* Figure out which partition it is under */
142 /* Skip the lot if 'data' isn't here yet (initialisation) */
143 for (i
=0; data
&& data
->partitions
&& data
->partitions
[i
]; i
++) {
144 if (ldb_dn_compare_base(data
->partitions
[i
]->ctrl
->dn
, dn
) == 0) {
145 return data
->partitions
[i
];
153 * fire the caller's callback for every entry, but only send 'done' once.
155 static int partition_req_callback(struct ldb_request
*req
,
156 struct ldb_reply
*ares
)
158 struct partition_context
*ac
;
159 struct ldb_module
*module
;
160 struct ldb_request
*nreq
;
162 struct partition_private_data
*data
;
163 struct ldb_control
*partition_ctrl
;
165 ac
= talloc_get_type(req
->context
, struct partition_context
);
166 data
= talloc_get_type(ac
->module
->private_data
, struct partition_private_data
);
169 return ldb_module_done(ac
->req
, NULL
, NULL
,
170 LDB_ERR_OPERATIONS_ERROR
);
173 partition_ctrl
= ldb_request_get_control(req
, DSDB_CONTROL_CURRENT_PARTITION_OID
);
174 if (partition_ctrl
&& (ac
->num_requests
== 1 || ares
->type
== LDB_REPLY_ENTRY
)) {
175 /* If we didn't fan this request out to mulitple partitions,
176 * or this is an individual search result, we can
177 * deterministily tell the caller what partition this was
178 * written to (repl_meta_data likes to know) */
179 ret
= ldb_reply_add_control(ares
,
180 DSDB_CONTROL_CURRENT_PARTITION_OID
,
181 false, partition_ctrl
->data
);
182 if (ret
!= LDB_SUCCESS
) {
183 return ldb_module_done(ac
->req
, NULL
, NULL
,
188 if (ares
->error
!= LDB_SUCCESS
&& !ac
->got_success
) {
189 return ldb_module_done(ac
->req
, ares
->controls
,
190 ares
->response
, ares
->error
);
193 switch (ares
->type
) {
194 case LDB_REPLY_REFERRAL
:
195 /* ignore referrals for now */
198 case LDB_REPLY_ENTRY
:
199 if (ac
->req
->operation
!= LDB_SEARCH
) {
200 ldb_set_errstring(ldb_module_get_ctx(ac
->module
),
201 "partition_req_callback:"
202 " Unsupported reply type for this request");
203 return ldb_module_done(ac
->req
, NULL
, NULL
,
204 LDB_ERR_OPERATIONS_ERROR
);
207 return ldb_module_send_entry(ac
->req
, ares
->message
, ares
->controls
);
210 if (ares
->error
== LDB_SUCCESS
) {
211 ac
->got_success
= true;
213 if (ac
->req
->operation
== LDB_EXTENDED
) {
214 /* FIXME: check for ares->response, replmd does not fill it ! */
215 if (ares
->response
) {
216 if (strcmp(ares
->response
->oid
, LDB_EXTENDED_START_TLS_OID
) != 0) {
217 ldb_set_errstring(ldb_module_get_ctx(ac
->module
),
218 "partition_req_callback:"
219 " Unknown extended reply, "
220 "only supports START_TLS");
222 return ldb_module_done(ac
->req
, NULL
, NULL
,
223 LDB_ERR_OPERATIONS_ERROR
);
228 ac
->finished_requests
++;
229 if (ac
->finished_requests
== ac
->num_requests
) {
230 /* this was the last one, call callback */
231 return ldb_module_done(ac
->req
, ares
->controls
,
233 ac
->got_success
?LDB_SUCCESS
:ares
->error
);
236 /* not the last, now call the next one */
237 module
= ac
->part_req
[ac
->finished_requests
].module
;
238 nreq
= ac
->part_req
[ac
->finished_requests
].req
;
240 ret
= partition_request(module
, nreq
);
241 if (ret
!= LDB_SUCCESS
) {
243 return ldb_module_done(ac
->req
, NULL
, NULL
, ret
);
253 static int partition_prep_request(struct partition_context
*ac
,
254 struct dsdb_partition
*partition
)
257 struct ldb_request
*req
;
259 ac
->part_req
= talloc_realloc(ac
, ac
->part_req
,
261 ac
->num_requests
+ 1);
262 if (ac
->part_req
== NULL
) {
263 ldb_oom(ldb_module_get_ctx(ac
->module
));
264 return LDB_ERR_OPERATIONS_ERROR
;
267 switch (ac
->req
->operation
) {
269 ret
= ldb_build_search_req_ex(&req
, ldb_module_get_ctx(ac
->module
),
271 ac
->req
->op
.search
.base
,
272 ac
->req
->op
.search
.scope
,
273 ac
->req
->op
.search
.tree
,
274 ac
->req
->op
.search
.attrs
,
276 ac
, partition_req_callback
,
280 ret
= ldb_build_add_req(&req
, ldb_module_get_ctx(ac
->module
), ac
->part_req
,
281 ac
->req
->op
.add
.message
,
283 ac
, partition_req_callback
,
287 ret
= ldb_build_mod_req(&req
, ldb_module_get_ctx(ac
->module
), ac
->part_req
,
288 ac
->req
->op
.mod
.message
,
290 ac
, partition_req_callback
,
294 ret
= ldb_build_del_req(&req
, ldb_module_get_ctx(ac
->module
), ac
->part_req
,
297 ac
, partition_req_callback
,
301 ret
= ldb_build_rename_req(&req
, ldb_module_get_ctx(ac
->module
), ac
->part_req
,
302 ac
->req
->op
.rename
.olddn
,
303 ac
->req
->op
.rename
.newdn
,
305 ac
, partition_req_callback
,
309 ret
= ldb_build_extended_req(&req
, ldb_module_get_ctx(ac
->module
),
311 ac
->req
->op
.extended
.oid
,
312 ac
->req
->op
.extended
.data
,
314 ac
, partition_req_callback
,
318 ldb_set_errstring(ldb_module_get_ctx(ac
->module
),
319 "Unsupported request type!");
320 ret
= LDB_ERR_UNWILLING_TO_PERFORM
;
323 if (ret
!= LDB_SUCCESS
) {
327 ac
->part_req
[ac
->num_requests
].req
= req
;
329 if (ac
->req
->controls
) {
330 req
->controls
= talloc_memdup(req
, ac
->req
->controls
,
331 talloc_get_size(ac
->req
->controls
));
332 if (req
->controls
== NULL
) {
333 ldb_oom(ldb_module_get_ctx(ac
->module
));
334 return LDB_ERR_OPERATIONS_ERROR
;
339 ac
->part_req
[ac
->num_requests
].module
= partition
->module
;
341 if (!ldb_request_get_control(req
, DSDB_CONTROL_CURRENT_PARTITION_OID
)) {
342 ret
= ldb_request_add_control(req
,
343 DSDB_CONTROL_CURRENT_PARTITION_OID
,
344 false, partition
->ctrl
);
345 if (ret
!= LDB_SUCCESS
) {
350 if (req
->operation
== LDB_SEARCH
) {
351 /* If the search is for 'more' than this partition,
352 * then change the basedn, so a remote LDAP server
354 if (ldb_dn_compare_base(partition
->ctrl
->dn
,
355 req
->op
.search
.base
) != 0) {
356 req
->op
.search
.base
= partition
->ctrl
->dn
;
361 /* make sure you put the NEXT module here, or
362 * partition_request() will simply loop forever on itself */
363 ac
->part_req
[ac
->num_requests
].module
= ac
->module
->next
;
371 static int partition_call_first(struct partition_context
*ac
)
373 return partition_request(ac
->part_req
[0].module
, ac
->part_req
[0].req
);
377 * Send a request down to all the partitions
379 static int partition_send_all(struct ldb_module
*module
,
380 struct partition_context
*ac
,
381 struct ldb_request
*req
)
384 struct partition_private_data
*data
= talloc_get_type(module
->private_data
,
385 struct partition_private_data
);
386 int ret
= partition_prep_request(ac
, NULL
);
387 if (ret
!= LDB_SUCCESS
) {
390 for (i
=0; data
&& data
->partitions
&& data
->partitions
[i
]; i
++) {
391 ret
= partition_prep_request(ac
, data
->partitions
[i
]);
392 if (ret
!= LDB_SUCCESS
) {
397 /* fire the first one */
398 return partition_call_first(ac
);
402 * Figure out which backend a request needs to be aimed at. Some
403 * requests must be replicated to all backends
405 static int partition_replicate(struct ldb_module
*module
, struct ldb_request
*req
, struct ldb_dn
*dn
)
407 struct partition_context
*ac
;
410 struct dsdb_partition
*partition
;
411 struct partition_private_data
*data
= talloc_get_type(module
->private_data
,
412 struct partition_private_data
);
413 if (!data
|| !data
->partitions
) {
414 return ldb_next_request(module
, req
);
417 if (req
->operation
!= LDB_SEARCH
) {
418 /* Is this a special DN, we need to replicate to every backend? */
419 for (i
=0; data
->replicate
&& data
->replicate
[i
]; i
++) {
420 if (ldb_dn_compare(data
->replicate
[i
],
423 ac
= partition_init_ctx(module
, req
);
425 return LDB_ERR_OPERATIONS_ERROR
;
428 return partition_send_all(module
, ac
, req
);
433 /* Otherwise, we need to find the partition to fire it to */
436 partition
= find_partition(data
, dn
, req
);
439 * if we haven't found a matching partition
440 * pass the request to the main ldb
442 * TODO: we should maybe return an error here
443 * if it's not a special dn
446 return ldb_next_request(module
, req
);
449 ac
= partition_init_ctx(module
, req
);
451 return LDB_ERR_OPERATIONS_ERROR
;
454 /* we need to add a control but we never touch the original request */
455 ret
= partition_prep_request(ac
, partition
);
456 if (ret
!= LDB_SUCCESS
) {
460 /* fire the first one */
461 return partition_call_first(ac
);
465 static int partition_search(struct ldb_module
*module
, struct ldb_request
*req
)
468 struct ldb_control
**saved_controls
;
470 struct partition_private_data
*data
= talloc_get_type(module
->private_data
,
471 struct partition_private_data
);
475 /* (later) consider if we should be searching multiple
476 * partitions (for 'invisible' partition behaviour */
478 struct ldb_control
*search_control
= ldb_request_get_control(req
, LDB_CONTROL_SEARCH_OPTIONS_OID
);
479 struct ldb_control
*domain_scope_control
= ldb_request_get_control(req
, LDB_CONTROL_DOMAIN_SCOPE_OID
);
481 struct ldb_search_options_control
*search_options
= NULL
;
482 struct dsdb_partition
*p
;
484 ret
= partition_reload_if_required(module
, data
);
485 if (ret
!= LDB_SUCCESS
) {
489 p
= find_partition(data
, NULL
, req
);
491 /* the caller specified what partition they want the
492 * search - just pass it on
494 return ldb_next_request(p
->module
, req
);
498 if (search_control
) {
499 search_options
= talloc_get_type(search_control
->data
, struct ldb_search_options_control
);
502 /* Remove the domain_scope control, so we don't confuse a backend server */
503 if (domain_scope_control
&& !save_controls(domain_scope_control
, req
, &saved_controls
)) {
504 ldb_oom(ldb_module_get_ctx(module
));
505 return LDB_ERR_OPERATIONS_ERROR
;
509 * for now pass down the LDB_CONTROL_SEARCH_OPTIONS_OID control
510 * down as uncritical to make windows 2008 dcpromo happy.
512 if (search_control
) {
513 search_control
->critical
= 0;
517 Generate referrals (look for a partition under this DN) if we don't have the above control specified
520 if (search_options
&& (search_options
->search_options
& LDB_SEARCH_OPTION_PHANTOM_ROOT
)) {
522 struct partition_context
*ac
;
523 if ((search_options
->search_options
& ~LDB_SEARCH_OPTION_PHANTOM_ROOT
) == 0) {
524 /* We have processed this flag, so we are done with this control now */
526 /* Remove search control, so we don't confuse a backend server */
527 if (search_control
&& !save_controls(search_control
, req
, &saved_controls
)) {
528 ldb_oom(ldb_module_get_ctx(module
));
529 return LDB_ERR_OPERATIONS_ERROR
;
532 ac
= partition_init_ctx(module
, req
);
534 return LDB_ERR_OPERATIONS_ERROR
;
537 /* Search from the base DN */
538 if (!req
->op
.search
.base
|| ldb_dn_is_null(req
->op
.search
.base
)) {
539 return partition_send_all(module
, ac
, req
);
541 for (i
=0; data
&& data
->partitions
&& data
->partitions
[i
]; i
++) {
542 bool match
= false, stop
= false;
543 /* Find all partitions under the search base
547 1) the DN we are looking for exactly matches the partition
549 2) the DN we are looking for is a parent of the partition and it isn't
552 3) the DN we are looking for is a child of the partition
554 if (ldb_dn_compare(data
->partitions
[i
]->ctrl
->dn
, req
->op
.search
.base
) == 0) {
556 if (req
->op
.search
.scope
== LDB_SCOPE_BASE
) {
561 (ldb_dn_compare_base(req
->op
.search
.base
, data
->partitions
[i
]->ctrl
->dn
) == 0 &&
562 req
->op
.search
.scope
!= LDB_SCOPE_BASE
)) {
566 ldb_dn_compare_base(data
->partitions
[i
]->ctrl
->dn
, req
->op
.search
.base
) == 0) {
568 stop
= true; /* note that this relies on partition ordering */
571 ret
= partition_prep_request(ac
, data
->partitions
[i
]);
572 if (ret
!= LDB_SUCCESS
) {
579 /* Perhaps we didn't match any partitions. Try the main partition, only */
580 if (ac
->num_requests
== 0) {
582 return ldb_next_request(module
, req
);
585 /* fire the first one */
586 return partition_call_first(ac
);
589 /* Handle this like all other requests */
590 if (search_control
&& (search_options
->search_options
& ~LDB_SEARCH_OPTION_PHANTOM_ROOT
) == 0) {
591 /* We have processed this flag, so we are done with this control now */
593 /* Remove search control, so we don't confuse a backend server */
594 if (search_control
&& !save_controls(search_control
, req
, &saved_controls
)) {
595 ldb_oom(ldb_module_get_ctx(module
));
596 return LDB_ERR_OPERATIONS_ERROR
;
600 return partition_replicate(module
, req
, req
->op
.search
.base
);
605 static int partition_add(struct ldb_module
*module
, struct ldb_request
*req
)
607 return partition_replicate(module
, req
, req
->op
.add
.message
->dn
);
611 static int partition_modify(struct ldb_module
*module
, struct ldb_request
*req
)
613 return partition_replicate(module
, req
, req
->op
.mod
.message
->dn
);
617 static int partition_delete(struct ldb_module
*module
, struct ldb_request
*req
)
619 return partition_replicate(module
, req
, req
->op
.del
.dn
);
623 static int partition_rename(struct ldb_module
*module
, struct ldb_request
*req
)
626 struct dsdb_partition
*backend
, *backend2
;
628 struct partition_private_data
*data
= talloc_get_type(module
->private_data
,
629 struct partition_private_data
);
631 /* Skip the lot if 'data' isn't here yet (initialisation) */
633 return LDB_ERR_OPERATIONS_ERROR
;
636 backend
= find_partition(data
, req
->op
.rename
.olddn
, req
);
637 backend2
= find_partition(data
, req
->op
.rename
.newdn
, req
);
639 if ((backend
&& !backend2
) || (!backend
&& backend2
)) {
640 return LDB_ERR_AFFECTS_MULTIPLE_DSAS
;
643 if (backend
!= backend2
) {
644 ldb_asprintf_errstring(ldb_module_get_ctx(module
),
645 "Cannot rename from %s in %s to %s in %s: %s",
646 ldb_dn_get_linearized(req
->op
.rename
.olddn
),
647 ldb_dn_get_linearized(backend
->ctrl
->dn
),
648 ldb_dn_get_linearized(req
->op
.rename
.newdn
),
649 ldb_dn_get_linearized(backend2
->ctrl
->dn
),
650 ldb_strerror(LDB_ERR_AFFECTS_MULTIPLE_DSAS
));
651 return LDB_ERR_AFFECTS_MULTIPLE_DSAS
;
654 return partition_replicate(module
, req
, req
->op
.rename
.olddn
);
657 /* start a transaction */
658 static int partition_start_trans(struct ldb_module
*module
)
661 struct partition_private_data
*data
= talloc_get_type(module
->private_data
,
662 struct partition_private_data
);
663 /* Look at base DN */
664 /* Figure out which partition it is under */
665 /* Skip the lot if 'data' isn't here yet (initialization) */
666 ret
= ldb_next_start_trans(module
);
667 if (ret
!= LDB_SUCCESS
) {
671 ret
= partition_reload_if_required(module
, data
);
672 if (ret
!= LDB_SUCCESS
) {
676 for (i
=0; data
&& data
->partitions
&& data
->partitions
[i
]; i
++) {
677 struct ldb_module
*next
= data
->partitions
[i
]->module
;
678 PARTITION_FIND_OP(next
, start_transaction
);
680 ret
= next
->ops
->start_transaction(next
);
681 if (ret
!= LDB_SUCCESS
) {
682 /* Back it out, if it fails on one */
683 for (i
--; i
>= 0; i
--) {
684 next
= data
->partitions
[i
]->module
;
685 PARTITION_FIND_OP(next
, del_transaction
);
687 next
->ops
->del_transaction(next
);
689 ldb_next_del_trans(module
);
694 data
->in_transaction
++;
699 /* prepare for a commit */
700 static int partition_prepare_commit(struct ldb_module
*module
)
703 struct partition_private_data
*data
= talloc_get_type(module
->private_data
,
704 struct partition_private_data
);
706 for (i
=0; data
&& data
->partitions
&& data
->partitions
[i
]; i
++) {
707 struct ldb_module
*next_prepare
= data
->partitions
[i
]->module
;
710 PARTITION_FIND_OP_NOERROR(next_prepare
, prepare_commit
);
711 if (next_prepare
== NULL
) {
715 ret
= next_prepare
->ops
->prepare_commit(next_prepare
);
716 if (ret
!= LDB_SUCCESS
) {
721 return ldb_next_prepare_commit(module
);
725 /* end a transaction */
726 static int partition_end_trans(struct ldb_module
*module
)
729 struct partition_private_data
*data
= talloc_get_type(module
->private_data
,
730 struct partition_private_data
);
731 for (i
=0; data
&& data
->partitions
&& data
->partitions
[i
]; i
++) {
732 struct ldb_module
*next_end
= data
->partitions
[i
]->module
;
735 PARTITION_FIND_OP(next_end
, end_transaction
);
737 ret
= next_end
->ops
->end_transaction(next_end
);
738 if (ret
!= LDB_SUCCESS
) {
743 if (data
->in_transaction
== 0) {
744 DEBUG(0,("partition end transaction mismatch\n"));
745 return LDB_ERR_OPERATIONS_ERROR
;
747 data
->in_transaction
--;
749 return ldb_next_end_trans(module
);
752 /* delete a transaction */
753 static int partition_del_trans(struct ldb_module
*module
)
755 int i
, ret
, final_ret
= LDB_SUCCESS
;
756 struct partition_private_data
*data
= talloc_get_type(module
->private_data
,
757 struct partition_private_data
);
758 for (i
=0; data
&& data
->partitions
&& data
->partitions
[i
]; i
++) {
759 struct ldb_module
*next
= data
->partitions
[i
]->module
;
760 PARTITION_FIND_OP(next
, del_transaction
);
762 ret
= next
->ops
->del_transaction(next
);
763 if (ret
!= LDB_SUCCESS
) {
768 if (data
->in_transaction
== 0) {
769 DEBUG(0,("partition del transaction mismatch\n"));
770 return LDB_ERR_OPERATIONS_ERROR
;
772 data
->in_transaction
--;
774 ret
= ldb_next_del_trans(module
);
775 if (ret
!= LDB_SUCCESS
) {
781 int partition_primary_sequence_number(struct ldb_module
*module
, TALLOC_CTX
*mem_ctx
,
782 enum ldb_sequence_type type
, uint64_t *seq_number
)
785 struct ldb_result
*res
;
786 struct ldb_seqnum_request
*tseq
;
787 struct ldb_request
*treq
;
788 struct ldb_seqnum_result
*seqr
;
789 res
= talloc_zero(mem_ctx
, struct ldb_result
);
791 return LDB_ERR_OPERATIONS_ERROR
;
793 tseq
= talloc_zero(res
, struct ldb_seqnum_request
);
796 return LDB_ERR_OPERATIONS_ERROR
;
800 ret
= ldb_build_extended_req(&treq
, ldb_module_get_ctx(module
), res
,
801 LDB_EXTENDED_SEQUENCE_NUMBER
,
805 ldb_extended_default_callback
,
807 if (ret
!= LDB_SUCCESS
) {
812 ret
= ldb_next_request(module
, treq
);
813 if (ret
!= LDB_SUCCESS
) {
817 ret
= ldb_wait(treq
->handle
, LDB_WAIT_ALL
);
818 if (ret
!= LDB_SUCCESS
) {
823 seqr
= talloc_get_type(res
->extended
->data
,
824 struct ldb_seqnum_result
);
825 if (seqr
->flags
& LDB_SEQ_TIMESTAMP_SEQUENCE
) {
826 ret
= LDB_ERR_OPERATIONS_ERROR
;
827 ldb_set_errstring(ldb_module_get_ctx(module
), "Primary backend in partitions module returned a timestamp based seq number (must return a normal number)");
831 *seq_number
= seqr
->seq_num
;
837 /* FIXME: This function is still semi-async */
838 static int partition_sequence_number(struct ldb_module
*module
, struct ldb_request
*req
)
841 uint64_t seq_number
= 0;
842 uint64_t timestamp_sequence
= 0;
843 uint64_t timestamp
= 0;
844 struct partition_private_data
*data
= talloc_get_type(module
->private_data
,
845 struct partition_private_data
);
846 struct ldb_seqnum_request
*seq
;
847 struct ldb_seqnum_result
*seqr
;
848 struct ldb_request
*treq
;
849 struct ldb_seqnum_request
*tseq
;
850 struct ldb_seqnum_result
*tseqr
;
851 struct ldb_extended
*ext
;
852 struct ldb_result
*res
;
853 struct dsdb_partition
*p
;
855 p
= find_partition(data
, NULL
, req
);
857 /* the caller specified what partition they want the
858 * sequence number operation on - just pass it on
860 return ldb_next_request(p
->module
, req
);
863 seq
= talloc_get_type(req
->op
.extended
.data
, struct ldb_seqnum_request
);
867 case LDB_SEQ_HIGHEST_SEQ
:
869 ret
= partition_primary_sequence_number(module
, req
, seq
->type
, &seq_number
);
870 if (ret
!= LDB_SUCCESS
) {
874 /* Skip the lot if 'data' isn't here yet (initialisation) */
875 for (i
=0; data
&& data
->partitions
&& data
->partitions
[i
]; i
++) {
877 res
= talloc_zero(req
, struct ldb_result
);
879 return LDB_ERR_OPERATIONS_ERROR
;
881 tseq
= talloc_zero(res
, struct ldb_seqnum_request
);
884 return LDB_ERR_OPERATIONS_ERROR
;
886 tseq
->type
= seq
->type
;
888 ret
= ldb_build_extended_req(&treq
, ldb_module_get_ctx(module
), res
,
889 LDB_EXTENDED_SEQUENCE_NUMBER
,
893 ldb_extended_default_callback
,
895 if (ret
!= LDB_SUCCESS
) {
900 if (!ldb_request_get_control(treq
, DSDB_CONTROL_CURRENT_PARTITION_OID
)) {
901 ret
= ldb_request_add_control(treq
,
902 DSDB_CONTROL_CURRENT_PARTITION_OID
,
903 false, data
->partitions
[i
]->ctrl
);
904 if (ret
!= LDB_SUCCESS
) {
910 ret
= partition_request(data
->partitions
[i
]->module
, treq
);
911 if (ret
!= LDB_SUCCESS
) {
915 ret
= ldb_wait(treq
->handle
, LDB_WAIT_ALL
);
916 if (ret
!= LDB_SUCCESS
) {
920 tseqr
= talloc_get_type(res
->extended
->data
,
921 struct ldb_seqnum_result
);
922 if (tseqr
->flags
& LDB_SEQ_TIMESTAMP_SEQUENCE
) {
923 timestamp_sequence
= MAX(timestamp_sequence
,
926 seq_number
+= tseqr
->seq_num
;
931 case LDB_SEQ_HIGHEST_TIMESTAMP
:
933 res
= talloc_zero(req
, struct ldb_result
);
935 return LDB_ERR_OPERATIONS_ERROR
;
938 tseq
= talloc_zero(res
, struct ldb_seqnum_request
);
941 return LDB_ERR_OPERATIONS_ERROR
;
943 tseq
->type
= LDB_SEQ_HIGHEST_TIMESTAMP
;
945 ret
= ldb_build_extended_req(&treq
, ldb_module_get_ctx(module
), res
,
946 LDB_EXTENDED_SEQUENCE_NUMBER
,
950 ldb_extended_default_callback
,
952 if (ret
!= LDB_SUCCESS
) {
957 ret
= ldb_next_request(module
, treq
);
958 if (ret
!= LDB_SUCCESS
) {
962 ret
= ldb_wait(treq
->handle
, LDB_WAIT_ALL
);
963 if (ret
!= LDB_SUCCESS
) {
968 tseqr
= talloc_get_type(res
->extended
->data
,
969 struct ldb_seqnum_result
);
970 timestamp
= tseqr
->seq_num
;
974 /* Skip the lot if 'data' isn't here yet (initialisation) */
975 for (i
=0; data
&& data
->partitions
&& data
->partitions
[i
]; i
++) {
977 res
= talloc_zero(req
, struct ldb_result
);
979 return LDB_ERR_OPERATIONS_ERROR
;
982 tseq
= talloc_zero(res
, struct ldb_seqnum_request
);
985 return LDB_ERR_OPERATIONS_ERROR
;
987 tseq
->type
= LDB_SEQ_HIGHEST_TIMESTAMP
;
989 ret
= ldb_build_extended_req(&treq
, ldb_module_get_ctx(module
), res
,
990 LDB_EXTENDED_SEQUENCE_NUMBER
,
994 ldb_extended_default_callback
,
996 if (ret
!= LDB_SUCCESS
) {
1001 if (!ldb_request_get_control(treq
, DSDB_CONTROL_CURRENT_PARTITION_OID
)) {
1002 ret
= ldb_request_add_control(treq
,
1003 DSDB_CONTROL_CURRENT_PARTITION_OID
,
1004 false, data
->partitions
[i
]->ctrl
);
1005 if (ret
!= LDB_SUCCESS
) {
1011 ret
= partition_request(data
->partitions
[i
]->module
, treq
);
1012 if (ret
!= LDB_SUCCESS
) {
1016 ret
= ldb_wait(treq
->handle
, LDB_WAIT_ALL
);
1017 if (ret
!= LDB_SUCCESS
) {
1022 tseqr
= talloc_get_type(res
->extended
->data
,
1023 struct ldb_seqnum_result
);
1024 timestamp
= MAX(timestamp
, tseqr
->seq_num
);
1032 ext
= talloc_zero(req
, struct ldb_extended
);
1034 return LDB_ERR_OPERATIONS_ERROR
;
1036 seqr
= talloc_zero(ext
, struct ldb_seqnum_result
);
1039 return LDB_ERR_OPERATIONS_ERROR
;
1041 ext
->oid
= LDB_EXTENDED_SEQUENCE_NUMBER
;
1044 switch (seq
->type
) {
1046 case LDB_SEQ_HIGHEST_SEQ
:
1048 /* Has someone above set a timebase sequence? */
1049 if (timestamp_sequence
) {
1050 seqr
->seq_num
= (((unsigned long long)timestamp
<< 24) | (seq_number
& 0xFFFFFF));
1052 seqr
->seq_num
= seq_number
;
1055 if (timestamp_sequence
> seqr
->seq_num
) {
1056 seqr
->seq_num
= timestamp_sequence
;
1057 seqr
->flags
|= LDB_SEQ_TIMESTAMP_SEQUENCE
;
1060 seqr
->flags
|= LDB_SEQ_GLOBAL_SEQUENCE
;
1062 case LDB_SEQ_HIGHEST_TIMESTAMP
:
1063 seqr
->seq_num
= timestamp
;
1067 if (seq
->type
== LDB_SEQ_NEXT
) {
1071 /* send request done */
1072 return ldb_module_done(req
, NULL
, ext
, LDB_SUCCESS
);
1076 static int partition_extended(struct ldb_module
*module
, struct ldb_request
*req
)
1078 struct partition_private_data
*data
;
1079 struct partition_context
*ac
;
1082 data
= talloc_get_type(module
->private_data
, struct partition_private_data
);
1084 return ldb_next_request(module
, req
);
1087 ret
= partition_reload_if_required(module
, data
);
1088 if (ret
!= LDB_SUCCESS
) {
1092 if (strcmp(req
->op
.extended
.oid
, LDB_EXTENDED_SEQUENCE_NUMBER
) == 0) {
1093 return partition_sequence_number(module
, req
);
1096 if (strcmp(req
->op
.extended
.oid
, DSDB_EXTENDED_CREATE_PARTITION_OID
) == 0) {
1097 return partition_create(module
, req
);
1101 * as the extended operation has no dn
1102 * we need to send it to all partitions
1105 ac
= partition_init_ctx(module
, req
);
1107 return LDB_ERR_OPERATIONS_ERROR
;
1110 return partition_send_all(module
, ac
, req
);
1113 _PUBLIC_
const struct ldb_module_ops ldb_partition_module_ops
= {
1114 .name
= "partition",
1115 .init_context
= partition_init
,
1116 .search
= partition_search
,
1117 .add
= partition_add
,
1118 .modify
= partition_modify
,
1119 .del
= partition_delete
,
1120 .rename
= partition_rename
,
1121 .extended
= partition_extended
,
1122 .start_transaction
= partition_start_trans
,
1123 .prepare_commit
= partition_prepare_commit
,
1124 .end_transaction
= partition_end_trans
,
1125 .del_transaction
= partition_del_trans
,