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
)
71 if ((module
&& module
->ldb
->flags
& LDB_FLG_ENABLE_TRACING
)) { \
72 const struct dsdb_control_current_partition
*partition
= NULL
;
73 struct ldb_control
*partition_ctrl
= ldb_request_get_control(request
, DSDB_CONTROL_CURRENT_PARTITION_OID
);
75 partition
= talloc_get_type(partition_ctrl
->data
,
76 struct dsdb_control_current_partition
);
79 if (partition
!= NULL
) {
80 ldb_debug(module
->ldb
, LDB_DEBUG_TRACE
, "partition_request() -> %s",
81 ldb_dn_get_linearized(partition
->dn
));
83 ldb_debug(module
->ldb
, LDB_DEBUG_TRACE
, "partition_request() -> (metadata partition)");
87 return ldb_next_request(module
, request
);
90 static struct dsdb_partition
*find_partition(struct partition_private_data
*data
,
92 struct ldb_request
*req
)
95 struct ldb_control
*partition_ctrl
;
97 /* see if the request has the partition DN specified in a
98 * control. The repl_meta_data module can specify this to
99 * ensure that replication happens to the right partition
101 partition_ctrl
= ldb_request_get_control(req
, DSDB_CONTROL_CURRENT_PARTITION_OID
);
102 if (partition_ctrl
) {
103 const struct dsdb_control_current_partition
*partition
;
104 partition
= talloc_get_type(partition_ctrl
->data
,
105 struct dsdb_control_current_partition
);
106 if (partition
!= NULL
) {
115 /* Look at base DN */
116 /* Figure out which partition it is under */
117 /* Skip the lot if 'data' isn't here yet (initialisation) */
118 for (i
=0; data
&& data
->partitions
&& data
->partitions
[i
]; i
++) {
119 if (ldb_dn_compare_base(data
->partitions
[i
]->ctrl
->dn
, dn
) == 0) {
120 return data
->partitions
[i
];
128 * fire the caller's callback for every entry, but only send 'done' once.
130 static int partition_req_callback(struct ldb_request
*req
,
131 struct ldb_reply
*ares
)
133 struct partition_context
*ac
;
134 struct ldb_module
*module
;
135 struct ldb_request
*nreq
;
137 struct partition_private_data
*data
;
138 struct ldb_control
*partition_ctrl
;
140 ac
= talloc_get_type(req
->context
, struct partition_context
);
141 data
= talloc_get_type(ac
->module
->private_data
, struct partition_private_data
);
144 return ldb_module_done(ac
->req
, NULL
, NULL
,
145 LDB_ERR_OPERATIONS_ERROR
);
148 partition_ctrl
= ldb_request_get_control(req
, DSDB_CONTROL_CURRENT_PARTITION_OID
);
149 if (partition_ctrl
&& (ac
->num_requests
== 1 || ares
->type
== LDB_REPLY_ENTRY
)) {
150 /* If we didn't fan this request out to mulitple partitions,
151 * or this is an individual search result, we can
152 * deterministily tell the caller what partition this was
153 * written to (repl_meta_data likes to know) */
154 ret
= ldb_reply_add_control(ares
,
155 DSDB_CONTROL_CURRENT_PARTITION_OID
,
156 false, partition_ctrl
->data
);
157 if (ret
!= LDB_SUCCESS
) {
158 return ldb_module_done(ac
->req
, NULL
, NULL
,
163 if (ares
->error
!= LDB_SUCCESS
&& !ac
->got_success
) {
164 return ldb_module_done(ac
->req
, ares
->controls
,
165 ares
->response
, ares
->error
);
168 switch (ares
->type
) {
169 case LDB_REPLY_REFERRAL
:
170 /* ignore referrals for now */
173 case LDB_REPLY_ENTRY
:
174 if (ac
->req
->operation
!= LDB_SEARCH
) {
175 ldb_set_errstring(ldb_module_get_ctx(ac
->module
),
176 "partition_req_callback:"
177 " Unsupported reply type for this request");
178 return ldb_module_done(ac
->req
, NULL
, NULL
,
179 LDB_ERR_OPERATIONS_ERROR
);
182 return ldb_module_send_entry(ac
->req
, ares
->message
, ares
->controls
);
185 if (ares
->error
== LDB_SUCCESS
) {
186 ac
->got_success
= true;
188 if (ac
->req
->operation
== LDB_EXTENDED
) {
189 /* FIXME: check for ares->response, replmd does not fill it ! */
190 if (ares
->response
) {
191 if (strcmp(ares
->response
->oid
, LDB_EXTENDED_START_TLS_OID
) != 0) {
192 ldb_set_errstring(ldb_module_get_ctx(ac
->module
),
193 "partition_req_callback:"
194 " Unknown extended reply, "
195 "only supports START_TLS");
197 return ldb_module_done(ac
->req
, NULL
, NULL
,
198 LDB_ERR_OPERATIONS_ERROR
);
203 ac
->finished_requests
++;
204 if (ac
->finished_requests
== ac
->num_requests
) {
205 /* this was the last one, call callback */
206 return ldb_module_done(ac
->req
, ares
->controls
,
208 ac
->got_success
?LDB_SUCCESS
:ares
->error
);
211 /* not the last, now call the next one */
212 module
= ac
->part_req
[ac
->finished_requests
].module
;
213 nreq
= ac
->part_req
[ac
->finished_requests
].req
;
215 ret
= partition_request(module
, nreq
);
216 if (ret
!= LDB_SUCCESS
) {
218 return ldb_module_done(ac
->req
, NULL
, NULL
, ret
);
228 static int partition_prep_request(struct partition_context
*ac
,
229 struct dsdb_partition
*partition
)
232 struct ldb_request
*req
;
234 ac
->part_req
= talloc_realloc(ac
, ac
->part_req
,
236 ac
->num_requests
+ 1);
237 if (ac
->part_req
== NULL
) {
238 ldb_oom(ldb_module_get_ctx(ac
->module
));
239 return LDB_ERR_OPERATIONS_ERROR
;
242 switch (ac
->req
->operation
) {
244 ret
= ldb_build_search_req_ex(&req
, ldb_module_get_ctx(ac
->module
),
246 ac
->req
->op
.search
.base
,
247 ac
->req
->op
.search
.scope
,
248 ac
->req
->op
.search
.tree
,
249 ac
->req
->op
.search
.attrs
,
251 ac
, partition_req_callback
,
255 ret
= ldb_build_add_req(&req
, ldb_module_get_ctx(ac
->module
), ac
->part_req
,
256 ac
->req
->op
.add
.message
,
258 ac
, partition_req_callback
,
262 ret
= ldb_build_mod_req(&req
, ldb_module_get_ctx(ac
->module
), ac
->part_req
,
263 ac
->req
->op
.mod
.message
,
265 ac
, partition_req_callback
,
269 ret
= ldb_build_del_req(&req
, ldb_module_get_ctx(ac
->module
), ac
->part_req
,
272 ac
, partition_req_callback
,
276 ret
= ldb_build_rename_req(&req
, ldb_module_get_ctx(ac
->module
), ac
->part_req
,
277 ac
->req
->op
.rename
.olddn
,
278 ac
->req
->op
.rename
.newdn
,
280 ac
, partition_req_callback
,
284 ret
= ldb_build_extended_req(&req
, ldb_module_get_ctx(ac
->module
),
286 ac
->req
->op
.extended
.oid
,
287 ac
->req
->op
.extended
.data
,
289 ac
, partition_req_callback
,
293 ldb_set_errstring(ldb_module_get_ctx(ac
->module
),
294 "Unsupported request type!");
295 ret
= LDB_ERR_UNWILLING_TO_PERFORM
;
298 if (ret
!= LDB_SUCCESS
) {
302 ac
->part_req
[ac
->num_requests
].req
= req
;
304 if (ac
->req
->controls
) {
305 req
->controls
= talloc_memdup(req
, ac
->req
->controls
,
306 talloc_get_size(ac
->req
->controls
));
307 if (req
->controls
== NULL
) {
308 ldb_oom(ldb_module_get_ctx(ac
->module
));
309 return LDB_ERR_OPERATIONS_ERROR
;
314 ac
->part_req
[ac
->num_requests
].module
= partition
->module
;
316 if (!ldb_request_get_control(req
, DSDB_CONTROL_CURRENT_PARTITION_OID
)) {
317 ret
= ldb_request_add_control(req
,
318 DSDB_CONTROL_CURRENT_PARTITION_OID
,
319 false, partition
->ctrl
);
320 if (ret
!= LDB_SUCCESS
) {
325 if (req
->operation
== LDB_SEARCH
) {
326 /* If the search is for 'more' than this partition,
327 * then change the basedn, so a remote LDAP server
329 if (ldb_dn_compare_base(partition
->ctrl
->dn
,
330 req
->op
.search
.base
) != 0) {
331 req
->op
.search
.base
= partition
->ctrl
->dn
;
336 /* make sure you put the module here, or
337 * or ldb_next_request() will skip a module */
338 ac
->part_req
[ac
->num_requests
].module
= ac
->module
;
346 static int partition_call_first(struct partition_context
*ac
)
348 return partition_request(ac
->part_req
[0].module
, ac
->part_req
[0].req
);
352 * Send a request down to all the partitions
354 static int partition_send_all(struct ldb_module
*module
,
355 struct partition_context
*ac
,
356 struct ldb_request
*req
)
359 struct partition_private_data
*data
= talloc_get_type(module
->private_data
,
360 struct partition_private_data
);
361 int ret
= partition_prep_request(ac
, NULL
);
362 if (ret
!= LDB_SUCCESS
) {
365 for (i
=0; data
&& data
->partitions
&& data
->partitions
[i
]; i
++) {
366 ret
= partition_prep_request(ac
, data
->partitions
[i
]);
367 if (ret
!= LDB_SUCCESS
) {
372 /* fire the first one */
373 return partition_call_first(ac
);
377 * Figure out which backend a request needs to be aimed at. Some
378 * requests must be replicated to all backends
380 static int partition_replicate(struct ldb_module
*module
, struct ldb_request
*req
, struct ldb_dn
*dn
)
382 struct partition_context
*ac
;
385 struct dsdb_partition
*partition
;
386 struct partition_private_data
*data
= talloc_get_type(module
->private_data
,
387 struct partition_private_data
);
388 if (!data
|| !data
->partitions
) {
389 return ldb_next_request(module
, req
);
392 if (req
->operation
!= LDB_SEARCH
&& ldb_dn_is_special(dn
)) {
393 /* Is this a special DN, we need to replicate to every backend? */
394 for (i
=0; data
->replicate
&& data
->replicate
[i
]; i
++) {
395 if (ldb_dn_compare(data
->replicate
[i
],
398 ac
= partition_init_ctx(module
, req
);
400 return LDB_ERR_OPERATIONS_ERROR
;
403 return partition_send_all(module
, ac
, req
);
408 /* Otherwise, we need to find the partition to fire it to */
411 partition
= find_partition(data
, dn
, req
);
414 * if we haven't found a matching partition
415 * pass the request to the main ldb
417 * TODO: we should maybe return an error here
418 * if it's not a special dn
421 return ldb_next_request(module
, req
);
424 ac
= partition_init_ctx(module
, req
);
426 return LDB_ERR_OPERATIONS_ERROR
;
429 /* we need to add a control but we never touch the original request */
430 ret
= partition_prep_request(ac
, partition
);
431 if (ret
!= LDB_SUCCESS
) {
435 /* fire the first one */
436 return partition_call_first(ac
);
440 static int partition_search(struct ldb_module
*module
, struct ldb_request
*req
)
443 struct ldb_control
**saved_controls
;
445 struct partition_private_data
*data
= talloc_get_type(module
->private_data
,
446 struct partition_private_data
);
450 /* (later) consider if we should be searching multiple
451 * partitions (for 'invisible' partition behaviour */
453 struct ldb_control
*search_control
= ldb_request_get_control(req
, LDB_CONTROL_SEARCH_OPTIONS_OID
);
454 struct ldb_control
*domain_scope_control
= ldb_request_get_control(req
, LDB_CONTROL_DOMAIN_SCOPE_OID
);
456 struct ldb_search_options_control
*search_options
= NULL
;
457 struct dsdb_partition
*p
;
459 ret
= partition_reload_if_required(module
, data
);
460 if (ret
!= LDB_SUCCESS
) {
464 p
= find_partition(data
, NULL
, req
);
466 /* the caller specified what partition they want the
467 * search - just pass it on
469 return ldb_next_request(p
->module
, req
);
473 if (search_control
) {
474 search_options
= talloc_get_type(search_control
->data
, struct ldb_search_options_control
);
477 /* Remove the domain_scope control, so we don't confuse a backend server */
478 if (domain_scope_control
&& !save_controls(domain_scope_control
, req
, &saved_controls
)) {
479 ldb_oom(ldb_module_get_ctx(module
));
480 return LDB_ERR_OPERATIONS_ERROR
;
484 * for now pass down the LDB_CONTROL_SEARCH_OPTIONS_OID control
485 * down as uncritical to make windows 2008 dcpromo happy.
487 if (search_control
) {
488 search_control
->critical
= 0;
492 Generate referrals (look for a partition under this DN) if we don't have the above control specified
495 if (search_options
&& (search_options
->search_options
& LDB_SEARCH_OPTION_PHANTOM_ROOT
)) {
497 struct partition_context
*ac
;
498 if ((search_options
->search_options
& ~LDB_SEARCH_OPTION_PHANTOM_ROOT
) == 0) {
499 /* We have processed this flag, so we are done with this control now */
501 /* Remove search control, so we don't confuse a backend server */
502 if (search_control
&& !save_controls(search_control
, req
, &saved_controls
)) {
503 ldb_oom(ldb_module_get_ctx(module
));
504 return LDB_ERR_OPERATIONS_ERROR
;
507 ac
= partition_init_ctx(module
, req
);
509 return LDB_ERR_OPERATIONS_ERROR
;
512 /* Search from the base DN */
513 if (!req
->op
.search
.base
|| ldb_dn_is_null(req
->op
.search
.base
)) {
514 return partition_send_all(module
, ac
, req
);
516 for (i
=0; data
&& data
->partitions
&& data
->partitions
[i
]; i
++) {
517 bool match
= false, stop
= false;
518 /* Find all partitions under the search base
522 1) the DN we are looking for exactly matches the partition
524 2) the DN we are looking for is a parent of the partition and it isn't
527 3) the DN we are looking for is a child of the partition
529 if (ldb_dn_compare(data
->partitions
[i
]->ctrl
->dn
, req
->op
.search
.base
) == 0) {
531 if (req
->op
.search
.scope
== LDB_SCOPE_BASE
) {
536 (ldb_dn_compare_base(req
->op
.search
.base
, data
->partitions
[i
]->ctrl
->dn
) == 0 &&
537 req
->op
.search
.scope
!= LDB_SCOPE_BASE
)) {
541 ldb_dn_compare_base(data
->partitions
[i
]->ctrl
->dn
, req
->op
.search
.base
) == 0) {
543 stop
= true; /* note that this relies on partition ordering */
546 ret
= partition_prep_request(ac
, data
->partitions
[i
]);
547 if (ret
!= LDB_SUCCESS
) {
554 /* Perhaps we didn't match any partitions. Try the main partition, only */
555 if (ac
->num_requests
== 0) {
557 return ldb_next_request(module
, req
);
560 /* fire the first one */
561 return partition_call_first(ac
);
564 /* Handle this like all other requests */
565 if (search_control
&& (search_options
->search_options
& ~LDB_SEARCH_OPTION_PHANTOM_ROOT
) == 0) {
566 /* We have processed this flag, so we are done with this control now */
568 /* Remove search control, so we don't confuse a backend server */
569 if (search_control
&& !save_controls(search_control
, req
, &saved_controls
)) {
570 ldb_oom(ldb_module_get_ctx(module
));
571 return LDB_ERR_OPERATIONS_ERROR
;
575 return partition_replicate(module
, req
, req
->op
.search
.base
);
580 static int partition_add(struct ldb_module
*module
, struct ldb_request
*req
)
582 return partition_replicate(module
, req
, req
->op
.add
.message
->dn
);
586 static int partition_modify(struct ldb_module
*module
, struct ldb_request
*req
)
588 return partition_replicate(module
, req
, req
->op
.mod
.message
->dn
);
592 static int partition_delete(struct ldb_module
*module
, struct ldb_request
*req
)
594 return partition_replicate(module
, req
, req
->op
.del
.dn
);
598 static int partition_rename(struct ldb_module
*module
, struct ldb_request
*req
)
601 struct dsdb_partition
*backend
, *backend2
;
603 struct partition_private_data
*data
= talloc_get_type(module
->private_data
,
604 struct partition_private_data
);
606 /* Skip the lot if 'data' isn't here yet (initialisation) */
608 return LDB_ERR_OPERATIONS_ERROR
;
611 backend
= find_partition(data
, req
->op
.rename
.olddn
, req
);
612 backend2
= find_partition(data
, req
->op
.rename
.newdn
, req
);
614 if ((backend
&& !backend2
) || (!backend
&& backend2
)) {
615 return LDB_ERR_AFFECTS_MULTIPLE_DSAS
;
618 if (backend
!= backend2
) {
619 ldb_asprintf_errstring(ldb_module_get_ctx(module
),
620 "Cannot rename from %s in %s to %s in %s: %s",
621 ldb_dn_get_linearized(req
->op
.rename
.olddn
),
622 ldb_dn_get_linearized(backend
->ctrl
->dn
),
623 ldb_dn_get_linearized(req
->op
.rename
.newdn
),
624 ldb_dn_get_linearized(backend2
->ctrl
->dn
),
625 ldb_strerror(LDB_ERR_AFFECTS_MULTIPLE_DSAS
));
626 return LDB_ERR_AFFECTS_MULTIPLE_DSAS
;
629 return partition_replicate(module
, req
, req
->op
.rename
.olddn
);
632 /* start a transaction */
633 static int partition_start_trans(struct ldb_module
*module
)
636 struct partition_private_data
*data
= talloc_get_type(module
->private_data
,
637 struct partition_private_data
);
638 /* Look at base DN */
639 /* Figure out which partition it is under */
640 /* Skip the lot if 'data' isn't here yet (initialization) */
641 if ((module
&& module
->ldb
->flags
& LDB_FLG_ENABLE_TRACING
)) {
642 ldb_debug(module
->ldb
, LDB_DEBUG_TRACE
, "partition_start_trans() -> (metadata partition)");
644 ret
= ldb_next_start_trans(module
);
645 if (ret
!= LDB_SUCCESS
) {
649 ret
= partition_reload_if_required(module
, data
);
650 if (ret
!= LDB_SUCCESS
) {
654 for (i
=0; data
&& data
->partitions
&& data
->partitions
[i
]; i
++) {
655 if ((module
&& module
->ldb
->flags
& LDB_FLG_ENABLE_TRACING
)) {
656 ldb_debug(module
->ldb
, LDB_DEBUG_TRACE
, "partition_start_trans() -> %s",
657 ldb_dn_get_linearized(data
->partitions
[i
]->ctrl
->dn
));
659 ret
= ldb_next_start_trans(data
->partitions
[i
]->module
);
660 if (ret
!= LDB_SUCCESS
) {
661 /* Back it out, if it fails on one */
662 for (i
--; i
>= 0; i
--) {
663 ldb_next_del_trans(data
->partitions
[i
]->module
);
665 ldb_next_del_trans(module
);
670 data
->in_transaction
++;
675 /* prepare for a commit */
676 static int partition_prepare_commit(struct ldb_module
*module
)
679 struct partition_private_data
*data
= talloc_get_type(module
->private_data
,
680 struct partition_private_data
);
682 for (i
=0; data
&& data
->partitions
&& data
->partitions
[i
]; i
++) {
685 if ((module
&& module
->ldb
->flags
& LDB_FLG_ENABLE_TRACING
)) {
686 ldb_debug(module
->ldb
, LDB_DEBUG_TRACE
, "partition_prepare_commit() -> %s",
687 ldb_dn_get_linearized(data
->partitions
[i
]->ctrl
->dn
));
689 ret
= ldb_next_prepare_commit(data
->partitions
[i
]->module
);
690 if (ret
!= LDB_SUCCESS
) {
691 ldb_asprintf_errstring(module
->ldb
, "prepare_commit error on %s: %s", ldb_dn_get_linearized(data
->partitions
[i
]->ctrl
->dn
), ldb_errstring(module
->ldb
));
696 if ((module
&& module
->ldb
->flags
& LDB_FLG_ENABLE_TRACING
)) {
697 ldb_debug(module
->ldb
, LDB_DEBUG_TRACE
, "partition_prepare_commit() -> (metadata partition)");
699 return ldb_next_prepare_commit(module
);
703 /* end a transaction */
704 static int partition_end_trans(struct ldb_module
*module
)
707 struct partition_private_data
*data
= talloc_get_type(module
->private_data
,
708 struct partition_private_data
);
712 if (data
->in_transaction
== 0) {
713 DEBUG(0,("partition end transaction mismatch\n"));
714 ret
= LDB_ERR_OPERATIONS_ERROR
;
716 data
->in_transaction
--;
719 for (i
=0; data
&& data
->partitions
&& data
->partitions
[i
]; i
++) {
720 if ((module
&& module
->ldb
->flags
& LDB_FLG_ENABLE_TRACING
)) {
721 ldb_debug(module
->ldb
, LDB_DEBUG_TRACE
, "partition_end_trans() -> %s",
722 ldb_dn_get_linearized(data
->partitions
[i
]->ctrl
->dn
));
724 ret2
= ldb_next_end_trans(data
->partitions
[i
]->module
);
725 if (ret2
!= LDB_SUCCESS
) {
726 ldb_asprintf_errstring(module
->ldb
, "end_trans error on %s: %s", ldb_dn_get_linearized(data
->partitions
[i
]->ctrl
->dn
), ldb_errstring(module
->ldb
));
731 if ((module
&& module
->ldb
->flags
& LDB_FLG_ENABLE_TRACING
)) {
732 ldb_debug(module
->ldb
, LDB_DEBUG_TRACE
, "partition_end_trans() -> (metadata partition)");
734 ret2
= ldb_next_end_trans(module
);
735 if (ret2
!= LDB_SUCCESS
) {
741 /* delete a transaction */
742 static int partition_del_trans(struct ldb_module
*module
)
744 int i
, ret
, final_ret
= LDB_SUCCESS
;
745 struct partition_private_data
*data
= talloc_get_type(module
->private_data
,
746 struct partition_private_data
);
747 for (i
=0; data
&& data
->partitions
&& data
->partitions
[i
]; i
++) {
748 if ((module
&& module
->ldb
->flags
& LDB_FLG_ENABLE_TRACING
)) {
749 ldb_debug(module
->ldb
, LDB_DEBUG_TRACE
, "partition_del_trans() -> %s",
750 ldb_dn_get_linearized(data
->partitions
[i
]->ctrl
->dn
));
752 ret
= ldb_next_del_trans(data
->partitions
[i
]->module
);
753 if (ret
!= LDB_SUCCESS
) {
754 ldb_asprintf_errstring(module
->ldb
, "del_trans error on %s: %s", ldb_dn_get_linearized(data
->partitions
[i
]->ctrl
->dn
), ldb_errstring(module
->ldb
));
759 if (data
->in_transaction
== 0) {
760 DEBUG(0,("partition del transaction mismatch\n"));
761 return LDB_ERR_OPERATIONS_ERROR
;
763 data
->in_transaction
--;
765 if ((module
&& module
->ldb
->flags
& LDB_FLG_ENABLE_TRACING
)) {
766 ldb_debug(module
->ldb
, LDB_DEBUG_TRACE
, "partition_del_trans() -> (metadata partition)");
768 ret
= ldb_next_del_trans(module
);
769 if (ret
!= LDB_SUCCESS
) {
775 int partition_primary_sequence_number(struct ldb_module
*module
, TALLOC_CTX
*mem_ctx
,
776 enum ldb_sequence_type type
, uint64_t *seq_number
)
779 struct ldb_result
*res
;
780 struct ldb_seqnum_request
*tseq
;
781 struct ldb_request
*treq
;
782 struct ldb_seqnum_result
*seqr
;
783 res
= talloc_zero(mem_ctx
, struct ldb_result
);
785 return LDB_ERR_OPERATIONS_ERROR
;
787 tseq
= talloc_zero(res
, struct ldb_seqnum_request
);
790 return LDB_ERR_OPERATIONS_ERROR
;
794 ret
= ldb_build_extended_req(&treq
, ldb_module_get_ctx(module
), res
,
795 LDB_EXTENDED_SEQUENCE_NUMBER
,
799 ldb_extended_default_callback
,
801 if (ret
!= LDB_SUCCESS
) {
806 ret
= ldb_next_request(module
, treq
);
807 if (ret
!= LDB_SUCCESS
) {
811 ret
= ldb_wait(treq
->handle
, LDB_WAIT_ALL
);
812 if (ret
!= LDB_SUCCESS
) {
817 seqr
= talloc_get_type(res
->extended
->data
,
818 struct ldb_seqnum_result
);
819 if (seqr
->flags
& LDB_SEQ_TIMESTAMP_SEQUENCE
) {
820 ret
= LDB_ERR_OPERATIONS_ERROR
;
821 ldb_set_errstring(ldb_module_get_ctx(module
), "Primary backend in partitions module returned a timestamp based seq number (must return a normal number)");
825 *seq_number
= seqr
->seq_num
;
831 /* FIXME: This function is still semi-async */
832 static int partition_sequence_number(struct ldb_module
*module
, struct ldb_request
*req
)
835 uint64_t seq_number
= 0;
836 uint64_t timestamp_sequence
= 0;
837 uint64_t timestamp
= 0;
838 struct partition_private_data
*data
= talloc_get_type(module
->private_data
,
839 struct partition_private_data
);
840 struct ldb_seqnum_request
*seq
;
841 struct ldb_seqnum_result
*seqr
;
842 struct ldb_request
*treq
;
843 struct ldb_seqnum_request
*tseq
;
844 struct ldb_seqnum_result
*tseqr
;
845 struct ldb_extended
*ext
;
846 struct ldb_result
*res
;
847 struct dsdb_partition
*p
;
849 p
= find_partition(data
, NULL
, req
);
851 /* the caller specified what partition they want the
852 * sequence number operation on - just pass it on
854 return ldb_next_request(p
->module
, req
);
857 seq
= talloc_get_type(req
->op
.extended
.data
, struct ldb_seqnum_request
);
861 case LDB_SEQ_HIGHEST_SEQ
:
863 ret
= partition_primary_sequence_number(module
, req
, seq
->type
, &seq_number
);
864 if (ret
!= LDB_SUCCESS
) {
868 /* Skip the lot if 'data' isn't here yet (initialisation) */
869 for (i
=0; data
&& data
->partitions
&& data
->partitions
[i
]; i
++) {
871 res
= talloc_zero(req
, struct ldb_result
);
873 return LDB_ERR_OPERATIONS_ERROR
;
875 tseq
= talloc_zero(res
, struct ldb_seqnum_request
);
878 return LDB_ERR_OPERATIONS_ERROR
;
880 tseq
->type
= seq
->type
;
882 ret
= ldb_build_extended_req(&treq
, ldb_module_get_ctx(module
), res
,
883 LDB_EXTENDED_SEQUENCE_NUMBER
,
887 ldb_extended_default_callback
,
889 if (ret
!= LDB_SUCCESS
) {
894 if (!ldb_request_get_control(treq
, DSDB_CONTROL_CURRENT_PARTITION_OID
)) {
895 ret
= ldb_request_add_control(treq
,
896 DSDB_CONTROL_CURRENT_PARTITION_OID
,
897 false, data
->partitions
[i
]->ctrl
);
898 if (ret
!= LDB_SUCCESS
) {
904 ret
= partition_request(data
->partitions
[i
]->module
, treq
);
905 if (ret
!= LDB_SUCCESS
) {
909 ret
= ldb_wait(treq
->handle
, LDB_WAIT_ALL
);
910 if (ret
!= LDB_SUCCESS
) {
914 tseqr
= talloc_get_type(res
->extended
->data
,
915 struct ldb_seqnum_result
);
916 if (tseqr
->flags
& LDB_SEQ_TIMESTAMP_SEQUENCE
) {
917 timestamp_sequence
= MAX(timestamp_sequence
,
920 seq_number
+= tseqr
->seq_num
;
925 case LDB_SEQ_HIGHEST_TIMESTAMP
:
927 res
= talloc_zero(req
, struct ldb_result
);
929 return LDB_ERR_OPERATIONS_ERROR
;
932 tseq
= talloc_zero(res
, struct ldb_seqnum_request
);
935 return LDB_ERR_OPERATIONS_ERROR
;
937 tseq
->type
= LDB_SEQ_HIGHEST_TIMESTAMP
;
939 ret
= ldb_build_extended_req(&treq
, ldb_module_get_ctx(module
), res
,
940 LDB_EXTENDED_SEQUENCE_NUMBER
,
944 ldb_extended_default_callback
,
946 if (ret
!= LDB_SUCCESS
) {
951 ret
= ldb_next_request(module
, treq
);
952 if (ret
!= LDB_SUCCESS
) {
956 ret
= ldb_wait(treq
->handle
, LDB_WAIT_ALL
);
957 if (ret
!= LDB_SUCCESS
) {
962 tseqr
= talloc_get_type(res
->extended
->data
,
963 struct ldb_seqnum_result
);
964 timestamp
= tseqr
->seq_num
;
968 /* Skip the lot if 'data' isn't here yet (initialisation) */
969 for (i
=0; data
&& data
->partitions
&& data
->partitions
[i
]; i
++) {
971 res
= talloc_zero(req
, struct ldb_result
);
973 return LDB_ERR_OPERATIONS_ERROR
;
976 tseq
= talloc_zero(res
, struct ldb_seqnum_request
);
979 return LDB_ERR_OPERATIONS_ERROR
;
981 tseq
->type
= LDB_SEQ_HIGHEST_TIMESTAMP
;
983 ret
= ldb_build_extended_req(&treq
, ldb_module_get_ctx(module
), res
,
984 LDB_EXTENDED_SEQUENCE_NUMBER
,
988 ldb_extended_default_callback
,
990 if (ret
!= LDB_SUCCESS
) {
995 if (!ldb_request_get_control(treq
, DSDB_CONTROL_CURRENT_PARTITION_OID
)) {
996 ret
= ldb_request_add_control(treq
,
997 DSDB_CONTROL_CURRENT_PARTITION_OID
,
998 false, data
->partitions
[i
]->ctrl
);
999 if (ret
!= LDB_SUCCESS
) {
1005 ret
= partition_request(data
->partitions
[i
]->module
, treq
);
1006 if (ret
!= LDB_SUCCESS
) {
1010 ret
= ldb_wait(treq
->handle
, LDB_WAIT_ALL
);
1011 if (ret
!= LDB_SUCCESS
) {
1016 tseqr
= talloc_get_type(res
->extended
->data
,
1017 struct ldb_seqnum_result
);
1018 timestamp
= MAX(timestamp
, tseqr
->seq_num
);
1026 ext
= talloc_zero(req
, struct ldb_extended
);
1028 return LDB_ERR_OPERATIONS_ERROR
;
1030 seqr
= talloc_zero(ext
, struct ldb_seqnum_result
);
1033 return LDB_ERR_OPERATIONS_ERROR
;
1035 ext
->oid
= LDB_EXTENDED_SEQUENCE_NUMBER
;
1038 switch (seq
->type
) {
1040 case LDB_SEQ_HIGHEST_SEQ
:
1042 /* Has someone above set a timebase sequence? */
1043 if (timestamp_sequence
) {
1044 seqr
->seq_num
= (((unsigned long long)timestamp
<< 24) | (seq_number
& 0xFFFFFF));
1046 seqr
->seq_num
= seq_number
;
1049 if (timestamp_sequence
> seqr
->seq_num
) {
1050 seqr
->seq_num
= timestamp_sequence
;
1051 seqr
->flags
|= LDB_SEQ_TIMESTAMP_SEQUENCE
;
1054 seqr
->flags
|= LDB_SEQ_GLOBAL_SEQUENCE
;
1056 case LDB_SEQ_HIGHEST_TIMESTAMP
:
1057 seqr
->seq_num
= timestamp
;
1061 if (seq
->type
== LDB_SEQ_NEXT
) {
1065 /* send request done */
1066 return ldb_module_done(req
, NULL
, ext
, LDB_SUCCESS
);
1070 static int partition_extended(struct ldb_module
*module
, struct ldb_request
*req
)
1072 struct partition_private_data
*data
;
1073 struct partition_context
*ac
;
1076 data
= talloc_get_type(module
->private_data
, struct partition_private_data
);
1078 return ldb_next_request(module
, req
);
1081 ret
= partition_reload_if_required(module
, data
);
1082 if (ret
!= LDB_SUCCESS
) {
1086 if (strcmp(req
->op
.extended
.oid
, LDB_EXTENDED_SEQUENCE_NUMBER
) == 0) {
1087 return partition_sequence_number(module
, req
);
1090 if (strcmp(req
->op
.extended
.oid
, DSDB_EXTENDED_CREATE_PARTITION_OID
) == 0) {
1091 return partition_create(module
, req
);
1095 * as the extended operation has no dn
1096 * we need to send it to all partitions
1099 ac
= partition_init_ctx(module
, req
);
1101 return LDB_ERR_OPERATIONS_ERROR
;
1104 return partition_send_all(module
, ac
, req
);
1107 _PUBLIC_
const struct ldb_module_ops ldb_partition_module_ops
= {
1108 .name
= "partition",
1109 .init_context
= partition_init
,
1110 .search
= partition_search
,
1111 .add
= partition_add
,
1112 .modify
= partition_modify
,
1113 .del
= partition_delete
,
1114 .rename
= partition_rename
,
1115 .extended
= partition_extended
,
1116 .start_transaction
= partition_start_trans
,
1117 .prepare_commit
= partition_prepare_commit
,
1118 .end_transaction
= partition_end_trans
,
1119 .del_transaction
= partition_del_trans
,