4 Copyright (C) Simo Sorce 2004-2006
5 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
6 Copyright (C) Andrew Tridgell 2005
7 Copyright (C) Stefan Metzmacher 2007
9 ** NOTE! The following LGPL license applies to the ldb
10 ** library. This does NOT imply that all of Samba is released
13 This library is free software; you can redistribute it and/or
14 modify it under the terms of the GNU Lesser General Public
15 License as published by the Free Software Foundation; either
16 version 2 of the License, or (at your option) any later version.
18 This library is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 Lesser General Public License for more details.
23 You should have received a copy of the GNU Lesser General Public
24 License along with this library; if not, write to the Free Software
25 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
31 * Component: ldb repl_meta_data module
33 * Description: - add a unique objectGUID onto every new record,
34 * - handle whenCreated, whenChanged timestamps
35 * - handle uSNCreated, uSNChanged numbers
36 * - handle replPropertyMetaData attribute
39 * Author: Stefan Metzmacher
43 #include "lib/ldb/include/ldb.h"
44 #include "lib/ldb/include/ldb_errors.h"
45 #include "lib/ldb/include/ldb_private.h"
46 #include "dsdb/samdb/samdb.h"
47 #include "dsdb/common/flags.h"
48 #include "librpc/gen_ndr/ndr_misc.h"
49 #include "librpc/gen_ndr/ndr_drsuapi.h"
50 #include "librpc/gen_ndr/ndr_drsblobs.h"
52 struct replmd_replicated_request
{
53 struct ldb_module
*module
;
54 struct ldb_handle
*handle
;
55 struct ldb_request
*orig_req
;
57 const struct dsdb_schema
*schema
;
59 struct dsdb_extended_replicated_objects
*objs
;
61 uint32_t index_current
;
65 struct ldb_request
*search_req
;
66 struct ldb_message
*search_msg
;
68 struct ldb_request
*change_req
;
73 static struct replmd_replicated_request
*replmd_replicated_init_handle(struct ldb_module
*module
,
74 struct ldb_request
*req
,
75 struct dsdb_extended_replicated_objects
*objs
)
77 struct replmd_replicated_request
*ar
;
79 const struct dsdb_schema
*schema
;
81 schema
= dsdb_get_schema(module
->ldb
);
83 ldb_debug_set(module
->ldb
, LDB_DEBUG_FATAL
,
84 "replmd_replicated_init_handle: no loaded schema found\n");
88 h
= talloc_zero(req
, struct ldb_handle
);
90 ldb_set_errstring(module
->ldb
, "Out of Memory");
95 h
->state
= LDB_ASYNC_PENDING
;
96 h
->status
= LDB_SUCCESS
;
98 ar
= talloc_zero(h
, struct replmd_replicated_request
);
100 ldb_set_errstring(module
->ldb
, "Out of Memory");
105 h
->private_data
= ar
;
119 add a time element to a record
121 static int add_time_element(struct ldb_message
*msg
, const char *attr
, time_t t
)
123 struct ldb_message_element
*el
;
126 if (ldb_msg_find_element(msg
, attr
) != NULL
) {
130 s
= ldb_timestring(msg
, t
);
135 if (ldb_msg_add_string(msg
, attr
, s
) != 0) {
139 el
= ldb_msg_find_element(msg
, attr
);
140 /* always set as replace. This works because on add ops, the flag
142 el
->flags
= LDB_FLAG_MOD_REPLACE
;
148 add a uint64_t element to a record
150 static int add_uint64_element(struct ldb_message
*msg
, const char *attr
, uint64_t v
)
152 struct ldb_message_element
*el
;
154 if (ldb_msg_find_element(msg
, attr
) != NULL
) {
158 if (ldb_msg_add_fmt(msg
, attr
, "%llu", (unsigned long long)v
) != 0) {
162 el
= ldb_msg_find_element(msg
, attr
);
163 /* always set as replace. This works because on add ops, the flag
165 el
->flags
= LDB_FLAG_MOD_REPLACE
;
170 static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1
*m1
,
171 const struct replPropertyMetaData1
*m2
,
172 const uint32_t *rdn_attid
)
174 if (m1
->attid
== m2
->attid
) {
179 * the rdn attribute should be at the end!
180 * so we need to return a value greater than zero
181 * which means m1 is greater than m2
183 if (m1
->attid
== *rdn_attid
) {
188 * the rdn attribute should be at the end!
189 * so we need to return a value less than zero
190 * which means m2 is greater than m1
192 if (m2
->attid
== *rdn_attid
) {
196 return m1
->attid
- m2
->attid
;
199 static void replmd_replPropertyMetaDataCtr1_sort(struct replPropertyMetaDataCtr1
*ctr1
,
200 const uint32_t *rdn_attid
)
202 ldb_qsort(ctr1
->array
, ctr1
->count
, sizeof(struct replPropertyMetaData1
),
203 discard_const_p(void, rdn_attid
), (ldb_qsort_cmp_fn_t
)replmd_replPropertyMetaData1_attid_sort
);
206 static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element
*e1
,
207 const struct ldb_message_element
*e2
,
208 const struct dsdb_schema
*schema
)
210 const struct dsdb_attribute
*a1
;
211 const struct dsdb_attribute
*a2
;
214 * TODO: make this faster by caching the dsdb_attribute pointer
215 * on the ldb_messag_element
218 a1
= dsdb_attribute_by_lDAPDisplayName(schema
, e1
->name
);
219 a2
= dsdb_attribute_by_lDAPDisplayName(schema
, e2
->name
);
222 * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
226 return strcasecmp(e1
->name
, e2
->name
);
229 return a1
->attributeID_id
- a2
->attributeID_id
;
232 static void replmd_ldb_message_sort(struct ldb_message
*msg
,
233 const struct dsdb_schema
*schema
)
235 ldb_qsort(msg
->elements
, msg
->num_elements
, sizeof(struct ldb_message_element
),
236 discard_const_p(void, schema
), (ldb_qsort_cmp_fn_t
)replmd_ldb_message_element_attid_sort
);
239 static int replmd_prepare_originating(struct ldb_module
*module
, struct ldb_request
*req
,
240 struct ldb_dn
*dn
, const char *fn_name
,
241 int (*fn
)(struct ldb_module
*,
242 struct ldb_request
*,
243 const struct dsdb_schema
*,
244 const struct dsdb_control_current_partition
*))
246 const struct dsdb_schema
*schema
;
247 const struct ldb_control
*partition_ctrl
;
248 const struct dsdb_control_current_partition
*partition
;
250 /* do not manipulate our control entries */
251 if (ldb_dn_is_special(dn
)) {
252 return ldb_next_request(module
, req
);
255 schema
= dsdb_get_schema(module
->ldb
);
257 ldb_debug_set(module
->ldb
, LDB_DEBUG_FATAL
,
258 "%s: no dsdb_schema loaded",
260 return LDB_ERR_CONSTRAINT_VIOLATION
;
263 partition_ctrl
= ldb_request_get_control(req
, DSDB_CONTROL_CURRENT_PARTITION_OID
);
264 if (!partition_ctrl
) {
265 ldb_debug_set(module
->ldb
, LDB_DEBUG_FATAL
,
266 "%s: no current partition control found",
268 return LDB_ERR_CONSTRAINT_VIOLATION
;
271 partition
= talloc_get_type(partition_ctrl
->data
,
272 struct dsdb_control_current_partition
);
274 ldb_debug_set(module
->ldb
, LDB_DEBUG_FATAL
,
275 "%s: current partition control contains invalid data",
277 return LDB_ERR_CONSTRAINT_VIOLATION
;
280 if (partition
->version
!= DSDB_CONTROL_CURRENT_PARTITION_VERSION
) {
281 ldb_debug_set(module
->ldb
, LDB_DEBUG_FATAL
,
282 "%s: current partition control contains invalid version [%u != %u]\n",
283 fn_name
, partition
->version
, DSDB_CONTROL_CURRENT_PARTITION_VERSION
);
284 return LDB_ERR_CONSTRAINT_VIOLATION
;
287 return fn(module
, req
, schema
, partition
);
290 static int replmd_add_originating(struct ldb_module
*module
,
291 struct ldb_request
*req
,
292 const struct dsdb_schema
*schema
,
293 const struct dsdb_control_current_partition
*partition
)
296 struct ldb_request
*down_req
;
297 struct ldb_message
*msg
;
298 uint32_t instance_type
;
299 struct ldb_dn
*new_dn
;
300 const char *rdn_name
;
301 const char *rdn_name_upper
;
302 const struct ldb_val
*rdn_value
= NULL
;
303 const struct dsdb_attribute
*rdn_attr
= NULL
;
305 struct ldb_val guid_value
;
306 struct replPropertyMetaDataBlob nmd
;
307 struct ldb_val nmd_value
;
309 const struct GUID
*our_invocation_id
;
310 time_t t
= time(NULL
);
316 ldb_debug(module
->ldb
, LDB_DEBUG_TRACE
, "replmd_add_originating\n");
318 if (ldb_msg_find_element(req
->op
.add
.message
, "objectGUID")) {
319 ldb_debug_set(module
->ldb
, LDB_DEBUG_ERROR
,
320 "replmd_add_originating: it's not allowed to add an object with objectGUID\n");
321 return LDB_ERR_UNWILLING_TO_PERFORM
;
324 if (ldb_msg_find_element(req
->op
.add
.message
, "instanceType")) {
325 ldb_debug_set(module
->ldb
, LDB_DEBUG_ERROR
,
326 "replmd_add_originating: it's not allowed to add an object with instanceType\n");
327 return LDB_ERR_UNWILLING_TO_PERFORM
;
330 /* Get a sequence number from the backend */
331 ret
= ldb_sequence_number(module
->ldb
, LDB_SEQ_NEXT
, &seq_num
);
332 if (ret
!= LDB_SUCCESS
) {
337 guid
= GUID_random();
339 /* get our invicationId */
340 our_invocation_id
= samdb_ntds_invocation_id(module
->ldb
);
341 if (!our_invocation_id
) {
342 ldb_debug_set(module
->ldb
, LDB_DEBUG_ERROR
,
343 "replmd_add_originating: unable to find invocationId\n");
344 return LDB_ERR_OPERATIONS_ERROR
;
347 /* create a copy of the request */
348 down_req
= talloc(req
, struct ldb_request
);
349 if (down_req
== NULL
) {
350 ldb_oom(module
->ldb
);
351 return LDB_ERR_OPERATIONS_ERROR
;
355 /* we have to copy the message as the caller might have it as a const */
356 down_req
->op
.add
.message
= msg
= ldb_msg_copy_shallow(down_req
, req
->op
.add
.message
);
358 talloc_free(down_req
);
359 ldb_oom(module
->ldb
);
360 return LDB_ERR_OPERATIONS_ERROR
;
363 /* generated times */
364 unix_to_nt_time(&now
, t
);
365 time_str
= ldb_timestring(msg
, t
);
367 talloc_free(down_req
);
368 return LDB_ERR_OPERATIONS_ERROR
;
372 * get details of the rdn name
374 rdn_name
= ldb_dn_get_rdn_name(msg
->dn
);
376 talloc_free(down_req
);
377 ldb_oom(module
->ldb
);
378 return LDB_ERR_OPERATIONS_ERROR
;
380 rdn_attr
= dsdb_attribute_by_lDAPDisplayName(schema
, rdn_name
);
382 talloc_free(down_req
);
383 return LDB_ERR_OPERATIONS_ERROR
;
385 rdn_value
= ldb_dn_get_rdn_val(msg
->dn
);
387 talloc_free(down_req
);
388 ldb_oom(module
->ldb
);
389 return LDB_ERR_OPERATIONS_ERROR
;
393 * remove autogenerated attributes
395 ldb_msg_remove_attr(msg
, rdn_name
);
396 ldb_msg_remove_attr(msg
, "name");
397 ldb_msg_remove_attr(msg
, "whenCreated");
398 ldb_msg_remove_attr(msg
, "whenChanged");
399 ldb_msg_remove_attr(msg
, "uSNCreated");
400 ldb_msg_remove_attr(msg
, "uSNChanged");
401 ldb_msg_remove_attr(msg
, "replPropertyMetaData");
404 * TODO: construct a new DN out of:
406 * - the upper case of rdn_attr->LDAPDisplayName
409 new_dn
= ldb_dn_copy(msg
, msg
->dn
);
411 talloc_free(down_req
);
412 ldb_oom(module
->ldb
);
413 return LDB_ERR_OPERATIONS_ERROR
;
415 rdn_name_upper
= strupper_talloc(msg
, rdn_attr
->lDAPDisplayName
);
416 if (!rdn_name_upper
) {
417 talloc_free(down_req
);
418 ldb_oom(module
->ldb
);
419 return LDB_ERR_OPERATIONS_ERROR
;
421 ret
= ldb_dn_set_component(new_dn
, 0, rdn_name_upper
, *rdn_value
);
422 if (ret
!= LDB_SUCCESS
) {
423 talloc_free(down_req
);
424 ldb_oom(module
->ldb
);
425 return LDB_ERR_OPERATIONS_ERROR
;
430 * TODO: calculate correct instance type
432 instance_type
= INSTANCE_TYPE_WRITE
;
433 if (ldb_dn_compare(partition
->dn
, msg
->dn
) == 0) {
434 instance_type
|= INSTANCE_TYPE_IS_NC_HEAD
;
435 if (ldb_dn_compare(msg
->dn
, samdb_base_dn(module
->ldb
)) != 0) {
436 instance_type
|= INSTANCE_TYPE_NC_ABOVE
;
441 * readd replicated attributes
443 ret
= ldb_msg_add_value(msg
, rdn_attr
->lDAPDisplayName
, rdn_value
, NULL
);
444 if (ret
!= LDB_SUCCESS
) {
445 talloc_free(down_req
);
446 ldb_oom(module
->ldb
);
447 return LDB_ERR_OPERATIONS_ERROR
;
449 ret
= ldb_msg_add_value(msg
, "name", rdn_value
, NULL
);
450 if (ret
!= LDB_SUCCESS
) {
451 talloc_free(down_req
);
452 ldb_oom(module
->ldb
);
453 return LDB_ERR_OPERATIONS_ERROR
;
455 ret
= ldb_msg_add_string(msg
, "whenCreated", time_str
);
456 if (ret
!= LDB_SUCCESS
) {
457 talloc_free(down_req
);
458 ldb_oom(module
->ldb
);
459 return LDB_ERR_OPERATIONS_ERROR
;
461 ret
= ldb_msg_add_fmt(msg
, "instanceType", "%u", instance_type
);
462 if (ret
!= LDB_SUCCESS
) {
463 talloc_free(down_req
);
464 ldb_oom(module
->ldb
);
465 return LDB_ERR_OPERATIONS_ERROR
;
468 /* build the replication meta_data */
471 nmd
.ctr
.ctr1
.count
= msg
->num_elements
;
472 nmd
.ctr
.ctr1
.array
= talloc_array(msg
,
473 struct replPropertyMetaData1
,
475 if (!nmd
.ctr
.ctr1
.array
) {
476 talloc_free(down_req
);
477 ldb_oom(module
->ldb
);
478 return LDB_ERR_OPERATIONS_ERROR
;
481 for (i
=0; i
< msg
->num_elements
; i
++) {
482 struct ldb_message_element
*e
= &msg
->elements
[i
];
483 struct replPropertyMetaData1
*m
= &nmd
.ctr
.ctr1
.array
[ni
];
484 const struct dsdb_attribute
*sa
;
486 sa
= dsdb_attribute_by_lDAPDisplayName(schema
, e
->name
);
488 ldb_debug_set(module
->ldb
, LDB_DEBUG_ERROR
,
489 "replmd_add_originating: attribute '%s' not defined in schema\n",
491 talloc_free(down_req
);
492 return LDB_ERR_NO_SUCH_ATTRIBUTE
;
495 if (sa
->systemFlags
& 0x00000001) {
496 /* attribute is not replicated so it has no meta data */
500 m
->attid
= sa
->attributeID_id
;
502 m
->orginating_time
= now
;
503 m
->orginating_invocation_id
= *our_invocation_id
;
504 m
->orginating_usn
= seq_num
;
505 m
->local_usn
= seq_num
;
509 /* fix meta data count */
510 nmd
.ctr
.ctr1
.count
= ni
;
513 * sort meta data array, and move the rdn attribute entry to the end
515 replmd_replPropertyMetaDataCtr1_sort(&nmd
.ctr
.ctr1
, &rdn_attr
->attributeID_id
);
517 /* generated NDR encoded values */
518 nt_status
= ndr_push_struct_blob(&guid_value
, msg
, &guid
,
519 (ndr_push_flags_fn_t
)ndr_push_GUID
);
520 if (!NT_STATUS_IS_OK(nt_status
)) {
521 talloc_free(down_req
);
522 ldb_oom(module
->ldb
);
523 return LDB_ERR_OPERATIONS_ERROR
;
525 nt_status
= ndr_push_struct_blob(&nmd_value
, msg
, &nmd
,
526 (ndr_push_flags_fn_t
)ndr_push_replPropertyMetaDataBlob
);
527 if (!NT_STATUS_IS_OK(nt_status
)) {
528 talloc_free(down_req
);
529 ldb_oom(module
->ldb
);
530 return LDB_ERR_OPERATIONS_ERROR
;
534 * add the autogenerated values
536 ret
= ldb_msg_add_value(msg
, "objectGUID", &guid_value
, NULL
);
537 if (ret
!= LDB_SUCCESS
) {
538 talloc_free(down_req
);
539 ldb_oom(module
->ldb
);
540 return LDB_ERR_OPERATIONS_ERROR
;
542 ret
= ldb_msg_add_string(msg
, "whenChanged", time_str
);
543 if (ret
!= LDB_SUCCESS
) {
544 talloc_free(down_req
);
545 ldb_oom(module
->ldb
);
546 return LDB_ERR_OPERATIONS_ERROR
;
548 ret
= samdb_msg_add_uint64(module
->ldb
, msg
, msg
, "uSNCreated", seq_num
);
549 if (ret
!= LDB_SUCCESS
) {
550 talloc_free(down_req
);
551 ldb_oom(module
->ldb
);
552 return LDB_ERR_OPERATIONS_ERROR
;
554 ret
= samdb_msg_add_uint64(module
->ldb
, msg
, msg
, "uSNChanged", seq_num
);
555 if (ret
!= LDB_SUCCESS
) {
556 talloc_free(down_req
);
557 ldb_oom(module
->ldb
);
558 return LDB_ERR_OPERATIONS_ERROR
;
560 ret
= ldb_msg_add_value(msg
, "replPropertyMetaData", &nmd_value
, NULL
);
561 if (ret
!= LDB_SUCCESS
) {
562 talloc_free(down_req
);
563 ldb_oom(module
->ldb
);
564 return LDB_ERR_OPERATIONS_ERROR
;
568 * sort the attributes by attid before storing the object
570 replmd_ldb_message_sort(msg
, schema
);
572 ldb_set_timeout_from_prev_req(module
->ldb
, req
, down_req
);
574 /* go on with the call chain */
575 ret
= ldb_next_request(module
, down_req
);
577 /* do not free down_req as the call results may be linked to it,
578 * it will be freed when the upper level request get freed */
579 if (ret
== LDB_SUCCESS
) {
580 req
->handle
= down_req
->handle
;
586 static int replmd_add(struct ldb_module
*module
, struct ldb_request
*req
)
588 return replmd_prepare_originating(module
, req
, req
->op
.add
.message
->dn
,
589 "replmd_add", replmd_add_originating
);
592 static int replmd_modify_originating(struct ldb_module
*module
,
593 struct ldb_request
*req
,
594 const struct dsdb_schema
*schema
,
595 const struct dsdb_control_current_partition
*partition
)
597 struct ldb_request
*down_req
;
598 struct ldb_message
*msg
;
600 time_t t
= time(NULL
);
603 ldb_debug(module
->ldb
, LDB_DEBUG_TRACE
, "replmd_modify_originating\n");
605 down_req
= talloc(req
, struct ldb_request
);
606 if (down_req
== NULL
) {
607 return LDB_ERR_OPERATIONS_ERROR
;
612 /* we have to copy the message as the caller might have it as a const */
613 down_req
->op
.mod
.message
= msg
= ldb_msg_copy_shallow(down_req
, req
->op
.mod
.message
);
615 talloc_free(down_req
);
616 return LDB_ERR_OPERATIONS_ERROR
;
619 if (add_time_element(msg
, "whenChanged", t
) != 0) {
620 talloc_free(down_req
);
621 return LDB_ERR_OPERATIONS_ERROR
;
624 /* Get a sequence number from the backend */
625 ret
= ldb_sequence_number(module
->ldb
, LDB_SEQ_NEXT
, &seq_num
);
626 if (ret
== LDB_SUCCESS
) {
627 if (add_uint64_element(msg
, "uSNChanged", seq_num
) != 0) {
628 talloc_free(down_req
);
629 return LDB_ERR_OPERATIONS_ERROR
;
633 ldb_set_timeout_from_prev_req(module
->ldb
, req
, down_req
);
635 /* go on with the call chain */
636 ret
= ldb_next_request(module
, down_req
);
638 /* do not free down_req as the call results may be linked to it,
639 * it will be freed when the upper level request get freed */
640 if (ret
== LDB_SUCCESS
) {
641 req
->handle
= down_req
->handle
;
647 static int replmd_modify(struct ldb_module
*module
, struct ldb_request
*req
)
649 return replmd_prepare_originating(module
, req
, req
->op
.mod
.message
->dn
,
650 "replmd_modify", replmd_modify_originating
);
653 static int replmd_replicated_request_reply_helper(struct replmd_replicated_request
*ar
, int ret
)
655 struct ldb_reply
*ares
= NULL
;
657 ar
->handle
->status
= ret
;
658 ar
->handle
->state
= LDB_ASYNC_DONE
;
660 if (!ar
->orig_req
->callback
) {
664 /* we're done and need to report the success to the caller */
665 ares
= talloc_zero(ar
, struct ldb_reply
);
667 ar
->handle
->status
= LDB_ERR_OPERATIONS_ERROR
;
668 ar
->handle
->state
= LDB_ASYNC_DONE
;
669 return LDB_ERR_OPERATIONS_ERROR
;
672 ares
->type
= LDB_REPLY_EXTENDED
;
673 ares
->response
= NULL
;
675 return ar
->orig_req
->callback(ar
->module
->ldb
, ar
->orig_req
->context
, ares
);
678 static int replmd_replicated_request_done(struct replmd_replicated_request
*ar
)
680 return replmd_replicated_request_reply_helper(ar
, LDB_SUCCESS
);
683 static int replmd_replicated_request_error(struct replmd_replicated_request
*ar
, int ret
)
685 return replmd_replicated_request_reply_helper(ar
, ret
);
688 static int replmd_replicated_request_werror(struct replmd_replicated_request
*ar
, WERROR status
)
690 int ret
= LDB_ERR_OTHER
;
691 /* TODO: do some error mapping */
692 return replmd_replicated_request_reply_helper(ar
, ret
);
695 static int replmd_replicated_apply_next(struct replmd_replicated_request
*ar
);
697 static int replmd_replicated_apply_add_callback(struct ldb_context
*ldb
,
699 struct ldb_reply
*ares
)
701 #ifdef REPLMD_FULL_ASYNC /* TODO: active this code when ldb support full async code */
702 struct replmd_replicated_request
*ar
= talloc_get_type(private_data
,
703 struct replmd_replicated_request
);
705 ar
->sub
.change_ret
= ldb_wait(ar
->sub
.search_req
->handle
, LDB_WAIT_ALL
);
706 if (ar
->sub
.change_ret
!= LDB_SUCCESS
) {
707 return replmd_replicated_request_error(ar
, ar
->sub
.change_ret
);
710 talloc_free(ar
->sub
.mem_ctx
);
711 ZERO_STRUCT(ar
->sub
);
715 return replmd_replicated_apply_next(ar
);
721 static int replmd_replicated_apply_add(struct replmd_replicated_request
*ar
)
724 struct ldb_message
*msg
;
725 struct replPropertyMetaDataBlob
*md
;
726 struct ldb_val md_value
;
732 * TODO: check if the parent object exist
736 * TODO: handle the conflict case where an object with the
740 msg
= ar
->objs
->objects
[ar
->index_current
].msg
;
741 md
= ar
->objs
->objects
[ar
->index_current
].meta_data
;
743 ret
= ldb_sequence_number(ar
->module
->ldb
, LDB_SEQ_NEXT
, &seq_num
);
744 if (ret
!= LDB_SUCCESS
) {
745 return replmd_replicated_request_error(ar
, ret
);
748 ret
= ldb_msg_add_value(msg
, "objectGUID", &ar
->objs
->objects
[ar
->index_current
].guid_value
, NULL
);
749 if (ret
!= LDB_SUCCESS
) {
750 return replmd_replicated_request_error(ar
, ret
);
753 ret
= ldb_msg_add_string(msg
, "whenChanged", ar
->objs
->objects
[ar
->index_current
].when_changed
);
754 if (ret
!= LDB_SUCCESS
) {
755 return replmd_replicated_request_error(ar
, ret
);
758 ret
= samdb_msg_add_uint64(ar
->module
->ldb
, msg
, msg
, "uSNCreated", seq_num
);
759 if (ret
!= LDB_SUCCESS
) {
760 return replmd_replicated_request_error(ar
, ret
);
763 ret
= samdb_msg_add_uint64(ar
->module
->ldb
, msg
, msg
, "uSNChanged", seq_num
);
764 if (ret
!= LDB_SUCCESS
) {
765 return replmd_replicated_request_error(ar
, ret
);
769 * the meta data array is already sorted by the caller
771 for (i
=0; i
< md
->ctr
.ctr1
.count
; i
++) {
772 md
->ctr
.ctr1
.array
[i
].local_usn
= seq_num
;
774 nt_status
= ndr_push_struct_blob(&md_value
, msg
, md
,
775 (ndr_push_flags_fn_t
)ndr_push_replPropertyMetaDataBlob
);
776 if (!NT_STATUS_IS_OK(nt_status
)) {
777 return replmd_replicated_request_werror(ar
, ntstatus_to_werror(nt_status
));
779 ret
= ldb_msg_add_value(msg
, "replPropertyMetaData", &md_value
, NULL
);
780 if (ret
!= LDB_SUCCESS
) {
781 return replmd_replicated_request_error(ar
, ret
);
784 replmd_ldb_message_sort(msg
, ar
->schema
);
786 ret
= ldb_build_add_req(&ar
->sub
.change_req
,
792 replmd_replicated_apply_add_callback
);
793 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
795 #ifdef REPLMD_FULL_ASYNC /* TODO: active this code when ldb support full async code */
796 return ldb_next_request(ar
->module
, ar
->sub
.change_req
);
798 ret
= ldb_next_request(ar
->module
, ar
->sub
.change_req
);
799 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
801 ar
->sub
.change_ret
= ldb_wait(ar
->sub
.search_req
->handle
, LDB_WAIT_ALL
);
802 if (ar
->sub
.change_ret
!= LDB_SUCCESS
) {
803 return replmd_replicated_request_error(ar
, ar
->sub
.change_ret
);
806 talloc_free(ar
->sub
.mem_ctx
);
807 ZERO_STRUCT(ar
->sub
);
815 static int replmd_replPropertyMetaData1_conflict_compare(struct replPropertyMetaData1
*m1
,
816 struct replPropertyMetaData1
*m2
)
820 if (m1
->version
!= m2
->version
) {
821 return m1
->version
- m2
->version
;
824 if (m1
->orginating_time
!= m2
->orginating_time
) {
825 return m1
->orginating_time
- m2
->orginating_time
;
828 ret
= GUID_compare(&m1
->orginating_invocation_id
, &m2
->orginating_invocation_id
);
833 return m1
->orginating_usn
- m2
->orginating_usn
;
836 static int replmd_replicated_apply_merge_callback(struct ldb_context
*ldb
,
838 struct ldb_reply
*ares
)
840 #ifdef REPLMD_FULL_ASYNC /* TODO: active this code when ldb support full async code */
841 struct replmd_replicated_request
*ar
= talloc_get_type(private_data
,
842 struct replmd_replicated_request
);
844 ret
= ldb_next_request(ar
->module
, ar
->sub
.change_req
);
845 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
847 ar
->sub
.change_ret
= ldb_wait(ar
->sub
.search_req
->handle
, LDB_WAIT_ALL
);
848 if (ar
->sub
.change_ret
!= LDB_SUCCESS
) {
849 return replmd_replicated_request_error(ar
, ar
->sub
.change_ret
);
852 talloc_free(ar
->sub
.mem_ctx
);
853 ZERO_STRUCT(ar
->sub
);
863 static int replmd_replicated_apply_merge(struct replmd_replicated_request
*ar
)
866 struct ldb_message
*msg
;
867 struct replPropertyMetaDataBlob
*rmd
;
868 struct replPropertyMetaDataBlob omd
;
869 const struct ldb_val
*omd_value
;
870 struct replPropertyMetaDataBlob nmd
;
871 struct ldb_val nmd_value
;
873 uint32_t removed_attrs
= 0;
877 msg
= ar
->objs
->objects
[ar
->index_current
].msg
;
878 rmd
= ar
->objs
->objects
[ar
->index_current
].meta_data
;
883 * TODO: add rename conflict handling
885 if (ldb_dn_compare(msg
->dn
, ar
->sub
.search_msg
->dn
) != 0) {
886 ldb_debug_set(ar
->module
->ldb
, LDB_DEBUG_FATAL
, "replmd_replicated_apply_merge[%u]: rename not supported",
888 ldb_debug(ar
->module
->ldb
, LDB_DEBUG_FATAL
, "%s => %s\n",
889 ldb_dn_get_linearized(ar
->sub
.search_msg
->dn
),
890 ldb_dn_get_linearized(msg
->dn
));
891 return replmd_replicated_request_werror(ar
, WERR_NOT_SUPPORTED
);
894 ret
= ldb_sequence_number(ar
->module
->ldb
, LDB_SEQ_NEXT
, &seq_num
);
895 if (ret
!= LDB_SUCCESS
) {
896 return replmd_replicated_request_error(ar
, ret
);
899 /* find existing meta data */
900 omd_value
= ldb_msg_find_ldb_val(ar
->sub
.search_msg
, "replPropertyMetaData");
902 nt_status
= ndr_pull_struct_blob(omd_value
, ar
->sub
.mem_ctx
, &omd
,
903 (ndr_pull_flags_fn_t
)ndr_pull_replPropertyMetaDataBlob
);
904 if (!NT_STATUS_IS_OK(nt_status
)) {
905 return replmd_replicated_request_werror(ar
, ntstatus_to_werror(nt_status
));
908 if (omd
.version
!= 1) {
909 return replmd_replicated_request_werror(ar
, WERR_DS_DRA_INTERNAL_ERROR
);
915 nmd
.ctr
.ctr1
.count
= omd
.ctr
.ctr1
.count
+ rmd
->ctr
.ctr1
.count
;
916 nmd
.ctr
.ctr1
.array
= talloc_array(ar
->sub
.mem_ctx
,
917 struct replPropertyMetaData1
,
919 if (!nmd
.ctr
.ctr1
.array
) return replmd_replicated_request_werror(ar
, WERR_NOMEM
);
921 /* first copy the old meta data */
922 for (i
=0; i
< omd
.ctr
.ctr1
.count
; i
++) {
923 nmd
.ctr
.ctr1
.array
[ni
] = omd
.ctr
.ctr1
.array
[i
];
927 /* now merge in the new meta data */
928 for (i
=0; i
< rmd
->ctr
.ctr1
.count
; i
++) {
931 rmd
->ctr
.ctr1
.array
[i
].local_usn
= seq_num
;
933 for (j
=0; j
< ni
; j
++) {
936 if (rmd
->ctr
.ctr1
.array
[i
].attid
!= nmd
.ctr
.ctr1
.array
[j
].attid
) {
940 cmp
= replmd_replPropertyMetaData1_conflict_compare(&rmd
->ctr
.ctr1
.array
[i
],
941 &nmd
.ctr
.ctr1
.array
[j
]);
943 /* replace the entry */
944 nmd
.ctr
.ctr1
.array
[j
] = rmd
->ctr
.ctr1
.array
[i
];
949 /* we don't want to apply this change so remove the attribute */
950 ldb_msg_remove_element(msg
, &msg
->elements
[i
-removed_attrs
]);
959 nmd
.ctr
.ctr1
.array
[ni
] = rmd
->ctr
.ctr1
.array
[i
];
964 * finally correct the size of the meta_data array
966 nmd
.ctr
.ctr1
.count
= ni
;
969 * the rdn attribute (the alias for the name attribute),
970 * 'cn' for most objects is the last entry in the meta data array
973 * sort the new meta data array
976 struct replPropertyMetaData1
*rdn_p
;
977 uint32_t rdn_idx
= omd
.ctr
.ctr1
.count
- 1;
979 rdn_p
= &nmd
.ctr
.ctr1
.array
[rdn_idx
];
980 replmd_replPropertyMetaDataCtr1_sort(&nmd
.ctr
.ctr1
, &rdn_p
->attid
);
983 /* create the meta data value */
984 nt_status
= ndr_push_struct_blob(&nmd_value
, msg
, &nmd
,
985 (ndr_push_flags_fn_t
)ndr_push_replPropertyMetaDataBlob
);
986 if (!NT_STATUS_IS_OK(nt_status
)) {
987 return replmd_replicated_request_werror(ar
, ntstatus_to_werror(nt_status
));
991 * check if some replicated attributes left, otherwise skip the ldb_modify() call
993 if (msg
->num_elements
== 0) {
994 ldb_debug(ar
->module
->ldb
, LDB_DEBUG_TRACE
, "replmd_replicated_apply_merge[%u]: skip replace\n",
999 ldb_debug(ar
->module
->ldb
, LDB_DEBUG_TRACE
, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
1000 ar
->index_current
, msg
->num_elements
);
1003 * when we now that we'll modify the record, add the whenChanged, uSNChanged
1004 * and replPopertyMetaData attributes
1006 ret
= ldb_msg_add_string(msg
, "whenChanged", ar
->objs
->objects
[ar
->index_current
].when_changed
);
1007 if (ret
!= LDB_SUCCESS
) {
1008 return replmd_replicated_request_error(ar
, ret
);
1010 ret
= samdb_msg_add_uint64(ar
->module
->ldb
, msg
, msg
, "uSNChanged", seq_num
);
1011 if (ret
!= LDB_SUCCESS
) {
1012 return replmd_replicated_request_error(ar
, ret
);
1014 ret
= ldb_msg_add_value(msg
, "replPropertyMetaData", &nmd_value
, NULL
);
1015 if (ret
!= LDB_SUCCESS
) {
1016 return replmd_replicated_request_error(ar
, ret
);
1019 replmd_ldb_message_sort(msg
, ar
->schema
);
1021 /* we want to replace the old values */
1022 for (i
=0; i
< msg
->num_elements
; i
++) {
1023 msg
->elements
[i
].flags
= LDB_FLAG_MOD_REPLACE
;
1026 ret
= ldb_build_mod_req(&ar
->sub
.change_req
,
1032 replmd_replicated_apply_merge_callback
);
1033 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
1035 #ifdef REPLMD_FULL_ASYNC /* TODO: active this code when ldb support full async code */
1036 return ldb_next_request(ar
->module
, ar
->sub
.change_req
);
1038 ret
= ldb_next_request(ar
->module
, ar
->sub
.change_req
);
1039 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
1041 ar
->sub
.change_ret
= ldb_wait(ar
->sub
.search_req
->handle
, LDB_WAIT_ALL
);
1042 if (ar
->sub
.change_ret
!= LDB_SUCCESS
) {
1043 return replmd_replicated_request_error(ar
, ar
->sub
.change_ret
);
1047 talloc_free(ar
->sub
.mem_ctx
);
1048 ZERO_STRUCT(ar
->sub
);
1050 ar
->index_current
++;
1056 static int replmd_replicated_apply_search_callback(struct ldb_context
*ldb
,
1058 struct ldb_reply
*ares
)
1060 struct replmd_replicated_request
*ar
= talloc_get_type(private_data
,
1061 struct replmd_replicated_request
);
1062 bool is_done
= false;
1064 switch (ares
->type
) {
1065 case LDB_REPLY_ENTRY
:
1066 ar
->sub
.search_msg
= talloc_steal(ar
->sub
.mem_ctx
, ares
->message
);
1068 case LDB_REPLY_REFERRAL
:
1069 /* we ignore referrals */
1071 case LDB_REPLY_EXTENDED
:
1072 case LDB_REPLY_DONE
:
1078 #ifdef REPLMD_FULL_ASYNC /* TODO: active this code when ldb support full async code */
1080 ar
->sub
.search_ret
= ldb_wait(ar
->sub
.search_req
->handle
, LDB_WAIT_ALL
);
1081 if (ar
->sub
.search_ret
!= LDB_SUCCESS
) {
1082 return replmd_replicated_request_error(ar
, ar
->sub
.search_ret
);
1084 if (ar
->sub
.search_msg
) {
1085 return replmd_replicated_apply_merge(ar
);
1087 return replmd_replicated_apply_add(ar
);
1093 static int replmd_replicated_apply_search(struct replmd_replicated_request
*ar
)
1099 tmp_str
= ldb_binary_encode(ar
->sub
.mem_ctx
, ar
->objs
->objects
[ar
->index_current
].guid_value
);
1100 if (!tmp_str
) return replmd_replicated_request_werror(ar
, WERR_NOMEM
);
1102 filter
= talloc_asprintf(ar
->sub
.mem_ctx
, "(objectGUID=%s)", tmp_str
);
1103 if (!filter
) return replmd_replicated_request_werror(ar
, WERR_NOMEM
);
1104 talloc_free(tmp_str
);
1106 ret
= ldb_build_search_req(&ar
->sub
.search_req
,
1109 ar
->objs
->partition_dn
,
1115 replmd_replicated_apply_search_callback
);
1116 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
1118 #ifdef REPLMD_FULL_ASYNC /* TODO: active this code when ldb support full async code */
1119 return ldb_next_request(ar
->module
, ar
->sub
.search_req
);
1121 ret
= ldb_next_request(ar
->module
, ar
->sub
.search_req
);
1122 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
1124 ar
->sub
.search_ret
= ldb_wait(ar
->sub
.search_req
->handle
, LDB_WAIT_ALL
);
1125 if (ar
->sub
.search_ret
!= LDB_SUCCESS
) {
1126 return replmd_replicated_request_error(ar
, ar
->sub
.search_ret
);
1128 if (ar
->sub
.search_msg
) {
1129 return replmd_replicated_apply_merge(ar
);
1132 return replmd_replicated_apply_add(ar
);
1136 static int replmd_replicated_apply_next(struct replmd_replicated_request
*ar
)
1138 #ifdef REPLMD_FULL_ASYNC /* TODO: active this code when ldb support full async code */
1139 if (ar
->index_current
>= ar
->objs
->num_objects
) {
1140 return replmd_replicated_uptodate_vector(ar
);
1144 ar
->sub
.mem_ctx
= talloc_new(ar
);
1145 if (!ar
->sub
.mem_ctx
) return replmd_replicated_request_werror(ar
, WERR_NOMEM
);
1147 return replmd_replicated_apply_search(ar
);
1150 static int replmd_replicated_uptodate_modify_callback(struct ldb_context
*ldb
,
1152 struct ldb_reply
*ares
)
1154 #ifdef REPLMD_FULL_ASYNC /* TODO: active this code when ldb support full async code */
1155 struct replmd_replicated_request
*ar
= talloc_get_type(private_data
,
1156 struct replmd_replicated_request
);
1158 ar
->sub
.change_ret
= ldb_wait(ar
->sub
.search_req
->handle
, LDB_WAIT_ALL
);
1159 if (ar
->sub
.change_ret
!= LDB_SUCCESS
) {
1160 return replmd_replicated_request_error(ar
, ar
->sub
.change_ret
);
1163 talloc_free(ar
->sub
.mem_ctx
);
1164 ZERO_STRUCT(ar
->sub
);
1166 return replmd_replicated_request_done(ar
);
1172 static int replmd_drsuapi_DsReplicaCursor2_compare(const struct drsuapi_DsReplicaCursor2
*c1
,
1173 const struct drsuapi_DsReplicaCursor2
*c2
)
1175 return GUID_compare(&c1
->source_dsa_invocation_id
, &c2
->source_dsa_invocation_id
);
1178 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request
*ar
)
1181 struct ldb_message
*msg
;
1182 struct replUpToDateVectorBlob ouv
;
1183 const struct ldb_val
*ouv_value
;
1184 const struct drsuapi_DsReplicaCursor2CtrEx
*ruv
;
1185 struct replUpToDateVectorBlob nuv
;
1186 struct ldb_val nuv_value
;
1187 struct ldb_message_element
*nuv_el
= NULL
;
1188 const struct GUID
*our_invocation_id
;
1189 struct ldb_message_element
*orf_el
= NULL
;
1190 struct repsFromToBlob nrf
;
1191 struct ldb_val
*nrf_value
= NULL
;
1192 struct ldb_message_element
*nrf_el
= NULL
;
1196 time_t t
= time(NULL
);
1200 ruv
= ar
->objs
->uptodateness_vector
;
1206 unix_to_nt_time(&now
, t
);
1209 * we use the next sequence number for our own highest_usn
1210 * because we will do a modify request and this will increment
1213 ret
= ldb_sequence_number(ar
->module
->ldb
, LDB_SEQ_NEXT
, &seq_num
);
1214 if (ret
!= LDB_SUCCESS
) {
1215 return replmd_replicated_request_error(ar
, ret
);
1219 * first create the new replUpToDateVector
1221 ouv_value
= ldb_msg_find_ldb_val(ar
->sub
.search_msg
, "replUpToDateVector");
1223 nt_status
= ndr_pull_struct_blob(ouv_value
, ar
->sub
.mem_ctx
, &ouv
,
1224 (ndr_pull_flags_fn_t
)ndr_pull_replUpToDateVectorBlob
);
1225 if (!NT_STATUS_IS_OK(nt_status
)) {
1226 return replmd_replicated_request_werror(ar
, ntstatus_to_werror(nt_status
));
1229 if (ouv
.version
!= 2) {
1230 return replmd_replicated_request_werror(ar
, WERR_DS_DRA_INTERNAL_ERROR
);
1235 * the new uptodateness vector will at least
1236 * contain 1 entry, one for the source_dsa
1238 * plus optional values from our old vector and the one from the source_dsa
1240 nuv
.ctr
.ctr2
.count
= 1 + ouv
.ctr
.ctr2
.count
;
1241 if (ruv
) nuv
.ctr
.ctr2
.count
+= ruv
->count
;
1242 nuv
.ctr
.ctr2
.cursors
= talloc_array(ar
->sub
.mem_ctx
,
1243 struct drsuapi_DsReplicaCursor2
,
1244 nuv
.ctr
.ctr2
.count
);
1245 if (!nuv
.ctr
.ctr2
.cursors
) return replmd_replicated_request_werror(ar
, WERR_NOMEM
);
1247 /* first copy the old vector */
1248 for (i
=0; i
< ouv
.ctr
.ctr2
.count
; i
++) {
1249 nuv
.ctr
.ctr2
.cursors
[ni
] = ouv
.ctr
.ctr2
.cursors
[i
];
1253 /* get our invocation_id if we have one already attached to the ldb */
1254 our_invocation_id
= samdb_ntds_invocation_id(ar
->module
->ldb
);
1256 /* merge in the source_dsa vector is available */
1257 for (i
=0; (ruv
&& i
< ruv
->count
); i
++) {
1260 if (our_invocation_id
&&
1261 GUID_equal(&ruv
->cursors
[i
].source_dsa_invocation_id
,
1262 our_invocation_id
)) {
1266 for (j
=0; j
< ni
; j
++) {
1267 if (!GUID_equal(&ruv
->cursors
[i
].source_dsa_invocation_id
,
1268 &nuv
.ctr
.ctr2
.cursors
[j
].source_dsa_invocation_id
)) {
1275 * we update only the highest_usn and not the latest_sync_success time,
1276 * because the last success stands for direct replication
1278 if (ruv
->cursors
[i
].highest_usn
> nuv
.ctr
.ctr2
.cursors
[j
].highest_usn
) {
1279 nuv
.ctr
.ctr2
.cursors
[j
].highest_usn
= ruv
->cursors
[i
].highest_usn
;
1284 if (found
) continue;
1286 /* if it's not there yet, add it */
1287 nuv
.ctr
.ctr2
.cursors
[ni
] = ruv
->cursors
[i
];
1292 * merge in the current highwatermark for the source_dsa
1295 for (j
=0; j
< ni
; j
++) {
1296 if (!GUID_equal(&ar
->objs
->source_dsa
->source_dsa_invocation_id
,
1297 &nuv
.ctr
.ctr2
.cursors
[j
].source_dsa_invocation_id
)) {
1304 * here we update the highest_usn and last_sync_success time
1305 * because we're directly replicating from the source_dsa
1307 * and use the tmp_highest_usn because this is what we have just applied
1310 nuv
.ctr
.ctr2
.cursors
[j
].highest_usn
= ar
->objs
->source_dsa
->highwatermark
.tmp_highest_usn
;
1311 nuv
.ctr
.ctr2
.cursors
[j
].last_sync_success
= now
;
1316 * here we update the highest_usn and last_sync_success time
1317 * because we're directly replicating from the source_dsa
1319 * and use the tmp_highest_usn because this is what we have just applied
1322 nuv
.ctr
.ctr2
.cursors
[ni
].source_dsa_invocation_id
= ar
->objs
->source_dsa
->source_dsa_invocation_id
;
1323 nuv
.ctr
.ctr2
.cursors
[ni
].highest_usn
= ar
->objs
->source_dsa
->highwatermark
.tmp_highest_usn
;
1324 nuv
.ctr
.ctr2
.cursors
[ni
].last_sync_success
= now
;
1329 * finally correct the size of the cursors array
1331 nuv
.ctr
.ctr2
.count
= ni
;
1336 qsort(nuv
.ctr
.ctr2
.cursors
, nuv
.ctr
.ctr2
.count
,
1337 sizeof(struct drsuapi_DsReplicaCursor2
),
1338 (comparison_fn_t
)replmd_drsuapi_DsReplicaCursor2_compare
);
1341 * create the change ldb_message
1343 msg
= ldb_msg_new(ar
->sub
.mem_ctx
);
1344 if (!msg
) return replmd_replicated_request_werror(ar
, WERR_NOMEM
);
1345 msg
->dn
= ar
->sub
.search_msg
->dn
;
1347 nt_status
= ndr_push_struct_blob(&nuv_value
, msg
, &nuv
,
1348 (ndr_push_flags_fn_t
)ndr_push_replUpToDateVectorBlob
);
1349 if (!NT_STATUS_IS_OK(nt_status
)) {
1350 return replmd_replicated_request_werror(ar
, ntstatus_to_werror(nt_status
));
1352 ret
= ldb_msg_add_value(msg
, "replUpToDateVector", &nuv_value
, &nuv_el
);
1353 if (ret
!= LDB_SUCCESS
) {
1354 return replmd_replicated_request_error(ar
, ret
);
1356 nuv_el
->flags
= LDB_FLAG_MOD_REPLACE
;
1359 * now create the new repsFrom value from the given repsFromTo1 structure
1363 nrf
.ctr
.ctr1
= *ar
->objs
->source_dsa
;
1364 /* and fix some values... */
1365 nrf
.ctr
.ctr1
.consecutive_sync_failures
= 0;
1366 nrf
.ctr
.ctr1
.last_success
= now
;
1367 nrf
.ctr
.ctr1
.last_attempt
= now
;
1368 nrf
.ctr
.ctr1
.result_last_attempt
= WERR_OK
;
1369 nrf
.ctr
.ctr1
.highwatermark
.highest_usn
= nrf
.ctr
.ctr1
.highwatermark
.tmp_highest_usn
;
1372 * first see if we already have a repsFrom value for the current source dsa
1373 * if so we'll later replace this value
1375 orf_el
= ldb_msg_find_element(ar
->sub
.search_msg
, "repsFrom");
1377 for (i
=0; i
< orf_el
->num_values
; i
++) {
1378 struct repsFromToBlob
*trf
;
1380 trf
= talloc(ar
->sub
.mem_ctx
, struct repsFromToBlob
);
1381 if (!trf
) return replmd_replicated_request_werror(ar
, WERR_NOMEM
);
1383 nt_status
= ndr_pull_struct_blob(&orf_el
->values
[i
], trf
, trf
,
1384 (ndr_pull_flags_fn_t
)ndr_pull_repsFromToBlob
);
1385 if (!NT_STATUS_IS_OK(nt_status
)) {
1386 return replmd_replicated_request_werror(ar
, ntstatus_to_werror(nt_status
));
1389 if (trf
->version
!= 1) {
1390 return replmd_replicated_request_werror(ar
, WERR_DS_DRA_INTERNAL_ERROR
);
1394 * we compare the source dsa objectGUID not the invocation_id
1395 * because we want only one repsFrom value per source dsa
1396 * and when the invocation_id of the source dsa has changed we don't need
1397 * the old repsFrom with the old invocation_id
1399 if (!GUID_equal(&trf
->ctr
.ctr1
.source_dsa_obj_guid
,
1400 &ar
->objs
->source_dsa
->source_dsa_obj_guid
)) {
1406 nrf_value
= &orf_el
->values
[i
];
1411 * copy over all old values to the new ldb_message
1413 ret
= ldb_msg_add_empty(msg
, "repsFrom", 0, &nrf_el
);
1414 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
1419 * if we haven't found an old repsFrom value for the current source dsa
1420 * we'll add a new value
1423 struct ldb_val zero_value
;
1424 ZERO_STRUCT(zero_value
);
1425 ret
= ldb_msg_add_value(msg
, "repsFrom", &zero_value
, &nrf_el
);
1426 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
1428 nrf_value
= &nrf_el
->values
[nrf_el
->num_values
- 1];
1431 /* we now fill the value which is already attached to ldb_message */
1432 nt_status
= ndr_push_struct_blob(nrf_value
, msg
, &nrf
,
1433 (ndr_push_flags_fn_t
)ndr_push_repsFromToBlob
);
1434 if (!NT_STATUS_IS_OK(nt_status
)) {
1435 return replmd_replicated_request_werror(ar
, ntstatus_to_werror(nt_status
));
1439 * the ldb_message_element for the attribute, has all the old values and the new one
1440 * so we'll replace the whole attribute with all values
1442 nrf_el
->flags
= LDB_FLAG_MOD_REPLACE
;
1444 /* prepare the ldb_modify() request */
1445 ret
= ldb_build_mod_req(&ar
->sub
.change_req
,
1451 replmd_replicated_uptodate_modify_callback
);
1452 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
1454 #ifdef REPLMD_FULL_ASYNC /* TODO: active this code when ldb support full async code */
1455 return ldb_next_request(ar
->module
, ar
->sub
.change_req
);
1457 ret
= ldb_next_request(ar
->module
, ar
->sub
.change_req
);
1458 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
1460 ar
->sub
.change_ret
= ldb_wait(ar
->sub
.search_req
->handle
, LDB_WAIT_ALL
);
1461 if (ar
->sub
.change_ret
!= LDB_SUCCESS
) {
1462 return replmd_replicated_request_error(ar
, ar
->sub
.change_ret
);
1465 talloc_free(ar
->sub
.mem_ctx
);
1466 ZERO_STRUCT(ar
->sub
);
1468 return replmd_replicated_request_done(ar
);
1472 static int replmd_replicated_uptodate_search_callback(struct ldb_context
*ldb
,
1474 struct ldb_reply
*ares
)
1476 struct replmd_replicated_request
*ar
= talloc_get_type(private_data
,
1477 struct replmd_replicated_request
);
1478 bool is_done
= false;
1480 switch (ares
->type
) {
1481 case LDB_REPLY_ENTRY
:
1482 ar
->sub
.search_msg
= talloc_steal(ar
->sub
.mem_ctx
, ares
->message
);
1484 case LDB_REPLY_REFERRAL
:
1485 /* we ignore referrals */
1487 case LDB_REPLY_EXTENDED
:
1488 case LDB_REPLY_DONE
:
1494 #ifdef REPLMD_FULL_ASYNC /* TODO: active this code when ldb support full async code */
1496 ar
->sub
.search_ret
= ldb_wait(ar
->sub
.search_req
->handle
, LDB_WAIT_ALL
);
1497 if (ar
->sub
.search_ret
!= LDB_SUCCESS
) {
1498 return replmd_replicated_request_error(ar
, ar
->sub
.search_ret
);
1500 if (!ar
->sub
.search_msg
) {
1501 return replmd_replicated_request_werror(ar
, WERR_DS_DRA_INTERNAL_ERROR
);
1504 return replmd_replicated_uptodate_modify(ar
);
1510 static int replmd_replicated_uptodate_search(struct replmd_replicated_request
*ar
)
1513 static const char *attrs
[] = {
1514 "replUpToDateVector",
1519 ret
= ldb_build_search_req(&ar
->sub
.search_req
,
1522 ar
->objs
->partition_dn
,
1528 replmd_replicated_uptodate_search_callback
);
1529 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
1531 #ifdef REPLMD_FULL_ASYNC /* TODO: active this code when ldb support full async code */
1532 return ldb_next_request(ar
->module
, ar
->sub
.search_req
);
1534 ret
= ldb_next_request(ar
->module
, ar
->sub
.search_req
);
1535 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
1537 ar
->sub
.search_ret
= ldb_wait(ar
->sub
.search_req
->handle
, LDB_WAIT_ALL
);
1538 if (ar
->sub
.search_ret
!= LDB_SUCCESS
) {
1539 return replmd_replicated_request_error(ar
, ar
->sub
.search_ret
);
1541 if (!ar
->sub
.search_msg
) {
1542 return replmd_replicated_request_werror(ar
, WERR_DS_DRA_INTERNAL_ERROR
);
1545 return replmd_replicated_uptodate_modify(ar
);
1549 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request
*ar
)
1551 ar
->sub
.mem_ctx
= talloc_new(ar
);
1552 if (!ar
->sub
.mem_ctx
) return replmd_replicated_request_werror(ar
, WERR_NOMEM
);
1554 return replmd_replicated_uptodate_search(ar
);
1557 static int replmd_extended_replicated_objects(struct ldb_module
*module
, struct ldb_request
*req
)
1559 struct dsdb_extended_replicated_objects
*objs
;
1560 struct replmd_replicated_request
*ar
;
1562 ldb_debug(module
->ldb
, LDB_DEBUG_TRACE
, "replmd_extended_replicated_objects\n");
1564 objs
= talloc_get_type(req
->op
.extended
.data
, struct dsdb_extended_replicated_objects
);
1566 ldb_debug(module
->ldb
, LDB_DEBUG_FATAL
, "replmd_extended_replicated_objects: invalid extended data\n");
1567 return LDB_ERR_PROTOCOL_ERROR
;
1570 if (objs
->version
!= DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION
) {
1571 ldb_debug(module
->ldb
, LDB_DEBUG_FATAL
, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
1572 objs
->version
, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION
);
1573 return LDB_ERR_PROTOCOL_ERROR
;
1576 ar
= replmd_replicated_init_handle(module
, req
, objs
);
1578 return LDB_ERR_OPERATIONS_ERROR
;
1581 #ifdef REPLMD_FULL_ASYNC /* TODO: active this code when ldb support full async code */
1582 return replmd_replicated_apply_next(ar
);
1584 while (ar
->index_current
< ar
->objs
->num_objects
&&
1585 req
->handle
->state
!= LDB_ASYNC_DONE
) {
1586 replmd_replicated_apply_next(ar
);
1589 if (req
->handle
->state
!= LDB_ASYNC_DONE
) {
1590 replmd_replicated_uptodate_vector(ar
);
1597 static int replmd_extended(struct ldb_module
*module
, struct ldb_request
*req
)
1599 if (strcmp(req
->op
.extended
.oid
, DSDB_EXTENDED_REPLICATED_OBJECTS_OID
) == 0) {
1600 return replmd_extended_replicated_objects(module
, req
);
1603 return ldb_next_request(module
, req
);
1606 static int replmd_wait_none(struct ldb_handle
*handle
) {
1607 struct replmd_replicated_request
*ar
;
1609 if (!handle
|| !handle
->private_data
) {
1610 return LDB_ERR_OPERATIONS_ERROR
;
1613 ar
= talloc_get_type(handle
->private_data
, struct replmd_replicated_request
);
1615 return LDB_ERR_OPERATIONS_ERROR
;
1618 /* we do only sync calls */
1619 if (handle
->state
!= LDB_ASYNC_DONE
) {
1620 return LDB_ERR_OPERATIONS_ERROR
;
1623 return handle
->status
;
1626 static int replmd_wait_all(struct ldb_handle
*handle
) {
1630 while (handle
->state
!= LDB_ASYNC_DONE
) {
1631 ret
= replmd_wait_none(handle
);
1632 if (ret
!= LDB_SUCCESS
) {
1637 return handle
->status
;
1640 static int replmd_wait(struct ldb_handle
*handle
, enum ldb_wait_type type
)
1642 if (type
== LDB_WAIT_ALL
) {
1643 return replmd_wait_all(handle
);
1645 return replmd_wait_none(handle
);
1649 static const struct ldb_module_ops replmd_ops
= {
1650 .name
= "repl_meta_data",
1652 .modify
= replmd_modify
,
1653 .extended
= replmd_extended
,
1657 int repl_meta_data_module_init(void)
1659 return ldb_register_module(&replmd_ops
);