4 Copyright (C) Simo Sorce 2004-2008
5 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
6 Copyright (C) Andrew Tridgell 2005
7 Copyright (C) Stefan Metzmacher <metze@samba.org> 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 3 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, see <http://www.gnu.org/licenses/>.
30 * Component: ldb repl_meta_data module
32 * Description: - add a unique objectGUID onto every new record,
33 * - handle whenCreated, whenChanged timestamps
34 * - handle uSNCreated, uSNChanged numbers
35 * - handle replPropertyMetaData attribute
38 * Author: Stefan Metzmacher
42 #include "ldb_module.h"
43 #include "dsdb/samdb/samdb.h"
44 #include "../libds/common/flags.h"
45 #include "librpc/gen_ndr/ndr_misc.h"
46 #include "librpc/gen_ndr/ndr_drsuapi.h"
47 #include "librpc/gen_ndr/ndr_drsblobs.h"
48 #include "param/param.h"
50 struct replmd_replicated_request
{
51 struct ldb_module
*module
;
52 struct ldb_request
*req
;
54 const struct dsdb_schema
*schema
;
56 struct dsdb_extended_replicated_objects
*objs
;
58 /* the controls we pass down */
59 struct ldb_control
**controls
;
61 uint32_t index_current
;
63 struct ldb_message
*search_msg
;
66 static struct replmd_replicated_request
*replmd_ctx_init(struct ldb_module
*module
,
67 struct ldb_request
*req
)
69 struct ldb_context
*ldb
;
70 struct replmd_replicated_request
*ac
;
72 ldb
= ldb_module_get_ctx(module
);
74 ac
= talloc_zero(req
, struct replmd_replicated_request
);
86 add a time element to a record
88 static int add_time_element(struct ldb_message
*msg
, const char *attr
, time_t t
)
90 struct ldb_message_element
*el
;
93 if (ldb_msg_find_element(msg
, attr
) != NULL
) {
97 s
= ldb_timestring(msg
, t
);
99 return LDB_ERR_OPERATIONS_ERROR
;
102 if (ldb_msg_add_string(msg
, attr
, s
) != LDB_SUCCESS
) {
103 return LDB_ERR_OPERATIONS_ERROR
;
106 el
= ldb_msg_find_element(msg
, attr
);
107 /* always set as replace. This works because on add ops, the flag
109 el
->flags
= LDB_FLAG_MOD_REPLACE
;
115 add a uint64_t element to a record
117 static int add_uint64_element(struct ldb_message
*msg
, const char *attr
, uint64_t v
)
119 struct ldb_message_element
*el
;
121 if (ldb_msg_find_element(msg
, attr
) != NULL
) {
125 if (ldb_msg_add_fmt(msg
, attr
, "%llu", (unsigned long long)v
) != LDB_SUCCESS
) {
126 return LDB_ERR_OPERATIONS_ERROR
;
129 el
= ldb_msg_find_element(msg
, attr
);
130 /* always set as replace. This works because on add ops, the flag
132 el
->flags
= LDB_FLAG_MOD_REPLACE
;
137 static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1
*m1
,
138 const struct replPropertyMetaData1
*m2
,
139 const uint32_t *rdn_attid
)
141 if (m1
->attid
== m2
->attid
) {
146 * the rdn attribute should be at the end!
147 * so we need to return a value greater than zero
148 * which means m1 is greater than m2
150 if (m1
->attid
== *rdn_attid
) {
155 * the rdn attribute should be at the end!
156 * so we need to return a value less than zero
157 * which means m2 is greater than m1
159 if (m2
->attid
== *rdn_attid
) {
163 return m1
->attid
- m2
->attid
;
166 static void replmd_replPropertyMetaDataCtr1_sort(struct replPropertyMetaDataCtr1
*ctr1
,
167 const uint32_t *rdn_attid
)
169 ldb_qsort(ctr1
->array
, ctr1
->count
, sizeof(struct replPropertyMetaData1
),
170 discard_const_p(void, rdn_attid
), (ldb_qsort_cmp_fn_t
)replmd_replPropertyMetaData1_attid_sort
);
173 static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element
*e1
,
174 const struct ldb_message_element
*e2
,
175 const struct dsdb_schema
*schema
)
177 const struct dsdb_attribute
*a1
;
178 const struct dsdb_attribute
*a2
;
181 * TODO: make this faster by caching the dsdb_attribute pointer
182 * on the ldb_messag_element
185 a1
= dsdb_attribute_by_lDAPDisplayName(schema
, e1
->name
);
186 a2
= dsdb_attribute_by_lDAPDisplayName(schema
, e2
->name
);
189 * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
193 return strcasecmp(e1
->name
, e2
->name
);
196 return a1
->attributeID_id
- a2
->attributeID_id
;
199 static void replmd_ldb_message_sort(struct ldb_message
*msg
,
200 const struct dsdb_schema
*schema
)
202 ldb_qsort(msg
->elements
, msg
->num_elements
, sizeof(struct ldb_message_element
),
203 discard_const_p(void, schema
), (ldb_qsort_cmp_fn_t
)replmd_ldb_message_element_attid_sort
);
206 static int replmd_op_callback(struct ldb_request
*req
, struct ldb_reply
*ares
)
208 struct ldb_context
*ldb
;
209 struct replmd_replicated_request
*ac
;
211 ac
= talloc_get_type(req
->context
, struct replmd_replicated_request
);
212 ldb
= ldb_module_get_ctx(ac
->module
);
215 return ldb_module_done(ac
->req
, NULL
, NULL
,
216 LDB_ERR_OPERATIONS_ERROR
);
218 if (ares
->error
!= LDB_SUCCESS
) {
219 return ldb_module_done(ac
->req
, ares
->controls
,
220 ares
->response
, ares
->error
);
223 if (ares
->type
!= LDB_REPLY_DONE
) {
224 ldb_set_errstring(ldb
,
225 "invalid ldb_reply_type in callback");
227 return ldb_module_done(ac
->req
, NULL
, NULL
,
228 LDB_ERR_OPERATIONS_ERROR
);
231 return ldb_module_done(ac
->req
, ares
->controls
,
232 ares
->response
, LDB_SUCCESS
);
235 static int replmd_add(struct ldb_module
*module
, struct ldb_request
*req
)
237 struct ldb_context
*ldb
;
238 struct replmd_replicated_request
*ac
;
239 const struct dsdb_schema
*schema
;
240 enum ndr_err_code ndr_err
;
241 struct ldb_request
*down_req
;
242 struct ldb_message
*msg
;
243 const struct dsdb_attribute
*rdn_attr
= NULL
;
245 struct ldb_val guid_value
;
246 struct replPropertyMetaDataBlob nmd
;
247 struct ldb_val nmd_value
;
249 const struct GUID
*our_invocation_id
;
250 time_t t
= time(NULL
);
256 /* do not manipulate our control entries */
257 if (ldb_dn_is_special(req
->op
.add
.message
->dn
)) {
258 return ldb_next_request(module
, req
);
261 ldb
= ldb_module_get_ctx(module
);
263 ldb_debug(ldb
, LDB_DEBUG_TRACE
, "replmd_add\n");
265 schema
= dsdb_get_schema(ldb
);
267 ldb_debug_set(ldb
, LDB_DEBUG_FATAL
,
268 "replmd_modify: no dsdb_schema loaded");
269 return LDB_ERR_CONSTRAINT_VIOLATION
;
272 ac
= replmd_ctx_init(module
, req
);
274 return LDB_ERR_OPERATIONS_ERROR
;
279 if (ldb_msg_find_element(req
->op
.add
.message
, "objectGUID") != NULL
) {
280 ldb_debug_set(ldb
, LDB_DEBUG_ERROR
,
281 "replmd_add: it's not allowed to add an object with objectGUID\n");
282 return LDB_ERR_UNWILLING_TO_PERFORM
;
285 /* Get a sequence number from the backend */
286 ret
= ldb_sequence_number(ldb
, LDB_SEQ_NEXT
, &seq_num
);
287 if (ret
!= LDB_SUCCESS
) {
292 guid
= GUID_random();
294 /* get our invicationId */
295 our_invocation_id
= samdb_ntds_invocation_id(ldb
);
296 if (!our_invocation_id
) {
297 ldb_debug_set(ldb
, LDB_DEBUG_ERROR
,
298 "replmd_add: unable to find invocationId\n");
299 return LDB_ERR_OPERATIONS_ERROR
;
302 /* we have to copy the message as the caller might have it as a const */
303 msg
= ldb_msg_copy_shallow(ac
, req
->op
.add
.message
);
306 return LDB_ERR_OPERATIONS_ERROR
;
309 /* generated times */
310 unix_to_nt_time(&now
, t
);
311 time_str
= ldb_timestring(msg
, t
);
313 return LDB_ERR_OPERATIONS_ERROR
;
317 * remove autogenerated attributes
319 ldb_msg_remove_attr(msg
, "whenCreated");
320 ldb_msg_remove_attr(msg
, "whenChanged");
321 ldb_msg_remove_attr(msg
, "uSNCreated");
322 ldb_msg_remove_attr(msg
, "uSNChanged");
323 ldb_msg_remove_attr(msg
, "replPropertyMetaData");
326 * readd replicated attributes
328 ret
= ldb_msg_add_string(msg
, "whenCreated", time_str
);
329 if (ret
!= LDB_SUCCESS
) {
331 return LDB_ERR_OPERATIONS_ERROR
;
334 /* build the replication meta_data */
337 nmd
.ctr
.ctr1
.count
= msg
->num_elements
;
338 nmd
.ctr
.ctr1
.array
= talloc_array(msg
,
339 struct replPropertyMetaData1
,
341 if (!nmd
.ctr
.ctr1
.array
) {
343 return LDB_ERR_OPERATIONS_ERROR
;
346 for (i
=0; i
< msg
->num_elements
; i
++) {
347 struct ldb_message_element
*e
= &msg
->elements
[i
];
348 struct replPropertyMetaData1
*m
= &nmd
.ctr
.ctr1
.array
[ni
];
349 const struct dsdb_attribute
*sa
;
351 if (e
->name
[0] == '@') continue;
353 sa
= dsdb_attribute_by_lDAPDisplayName(schema
, e
->name
);
355 ldb_debug_set(ldb
, LDB_DEBUG_ERROR
,
356 "replmd_add: attribute '%s' not defined in schema\n",
358 return LDB_ERR_NO_SUCH_ATTRIBUTE
;
361 if ((sa
->systemFlags
& 0x00000001) || (sa
->systemFlags
& 0x00000004)) {
362 /* if the attribute is not replicated (0x00000001)
363 * or constructed (0x00000004) it has no metadata
368 m
->attid
= sa
->attributeID_id
;
370 m
->originating_change_time
= now
;
371 m
->originating_invocation_id
= *our_invocation_id
;
372 m
->originating_usn
= seq_num
;
373 m
->local_usn
= seq_num
;
376 if (ldb_attr_cmp(e
->name
, ldb_dn_get_rdn_name(msg
->dn
))) {
381 /* fix meta data count */
382 nmd
.ctr
.ctr1
.count
= ni
;
385 * sort meta data array, and move the rdn attribute entry to the end
387 replmd_replPropertyMetaDataCtr1_sort(&nmd
.ctr
.ctr1
, &rdn_attr
->attributeID_id
);
389 /* generated NDR encoded values */
390 ndr_err
= ndr_push_struct_blob(&guid_value
, msg
,
393 (ndr_push_flags_fn_t
)ndr_push_GUID
);
394 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
396 return LDB_ERR_OPERATIONS_ERROR
;
398 ndr_err
= ndr_push_struct_blob(&nmd_value
, msg
,
399 lp_iconv_convenience(ldb_get_opaque(ldb
, "loadparm")),
401 (ndr_push_flags_fn_t
)ndr_push_replPropertyMetaDataBlob
);
402 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
404 return LDB_ERR_OPERATIONS_ERROR
;
408 * add the autogenerated values
410 ret
= ldb_msg_add_value(msg
, "objectGUID", &guid_value
, NULL
);
411 if (ret
!= LDB_SUCCESS
) {
413 return LDB_ERR_OPERATIONS_ERROR
;
415 ret
= ldb_msg_add_string(msg
, "whenChanged", time_str
);
416 if (ret
!= LDB_SUCCESS
) {
418 return LDB_ERR_OPERATIONS_ERROR
;
420 ret
= samdb_msg_add_uint64(ldb
, msg
, msg
, "uSNCreated", seq_num
);
421 if (ret
!= LDB_SUCCESS
) {
423 return LDB_ERR_OPERATIONS_ERROR
;
425 ret
= samdb_msg_add_uint64(ldb
, msg
, msg
, "uSNChanged", seq_num
);
426 if (ret
!= LDB_SUCCESS
) {
428 return LDB_ERR_OPERATIONS_ERROR
;
430 ret
= ldb_msg_add_value(msg
, "replPropertyMetaData", &nmd_value
, NULL
);
431 if (ret
!= LDB_SUCCESS
) {
433 return LDB_ERR_OPERATIONS_ERROR
;
437 * sort the attributes by attid before storing the object
439 replmd_ldb_message_sort(msg
, schema
);
441 ret
= ldb_build_add_req(&down_req
, ldb
, ac
,
444 ac
, replmd_op_callback
,
446 if (ret
!= LDB_SUCCESS
) {
450 /* go on with the call chain */
451 return ldb_next_request(module
, down_req
);
454 static int replmd_modify(struct ldb_module
*module
, struct ldb_request
*req
)
456 struct ldb_context
*ldb
;
457 struct replmd_replicated_request
*ac
;
458 const struct dsdb_schema
*schema
;
459 struct ldb_request
*down_req
;
460 struct ldb_message
*msg
;
462 time_t t
= time(NULL
);
465 /* do not manipulate our control entries */
466 if (ldb_dn_is_special(req
->op
.mod
.message
->dn
)) {
467 return ldb_next_request(module
, req
);
470 ldb
= ldb_module_get_ctx(module
);
472 ldb_debug(ldb
, LDB_DEBUG_TRACE
, "replmd_modify\n");
474 schema
= dsdb_get_schema(ldb
);
476 ldb_debug_set(ldb
, LDB_DEBUG_FATAL
,
477 "replmd_modify: no dsdb_schema loaded");
478 return LDB_ERR_CONSTRAINT_VIOLATION
;
481 ac
= replmd_ctx_init(module
, req
);
483 return LDB_ERR_OPERATIONS_ERROR
;
488 /* we have to copy the message as the caller might have it as a const */
489 msg
= ldb_msg_copy_shallow(ac
, req
->op
.mod
.message
);
492 return LDB_ERR_OPERATIONS_ERROR
;
496 * - get the whole old object
497 * - if the old object doesn't exist report an error
498 * - give an error when a readonly attribute should
500 * - merge the changed into the old object
501 * if the caller set values to the same value
502 * ignore the attribute, return success when no
503 * attribute was changed
504 * - calculate the new replPropertyMetaData attribute
507 if (add_time_element(msg
, "whenChanged", t
) != LDB_SUCCESS
) {
509 return LDB_ERR_OPERATIONS_ERROR
;
512 /* Get a sequence number from the backend */
513 ret
= ldb_sequence_number(ldb
, LDB_SEQ_NEXT
, &seq_num
);
514 if (ret
== LDB_SUCCESS
) {
515 if (add_uint64_element(msg
, "uSNChanged", seq_num
) != LDB_SUCCESS
) {
517 return LDB_ERR_OPERATIONS_ERROR
;
522 * - sort the attributes by attid with replmd_ldb_message_sort()
523 * - replace the old object with the newly constructed one
526 ret
= ldb_build_mod_req(&down_req
, ldb
, ac
,
529 ac
, replmd_op_callback
,
531 if (ret
!= LDB_SUCCESS
) {
534 talloc_steal(down_req
, msg
);
536 /* go on with the call chain */
537 return ldb_next_request(module
, down_req
);
540 static int replmd_replicated_request_error(struct replmd_replicated_request
*ar
, int ret
)
545 static int replmd_replicated_request_werror(struct replmd_replicated_request
*ar
, WERROR status
)
547 int ret
= LDB_ERR_OTHER
;
548 /* TODO: do some error mapping */
552 static int replmd_replicated_apply_next(struct replmd_replicated_request
*ar
);
554 static int replmd_replicated_apply_add_callback(struct ldb_request
*req
,
555 struct ldb_reply
*ares
)
557 struct ldb_context
*ldb
;
558 struct replmd_replicated_request
*ar
= talloc_get_type(req
->context
,
559 struct replmd_replicated_request
);
562 ldb
= ldb_module_get_ctx(ar
->module
);
565 return ldb_module_done(ar
->req
, NULL
, NULL
,
566 LDB_ERR_OPERATIONS_ERROR
);
568 if (ares
->error
!= LDB_SUCCESS
) {
569 return ldb_module_done(ar
->req
, ares
->controls
,
570 ares
->response
, ares
->error
);
573 if (ares
->type
!= LDB_REPLY_DONE
) {
574 ldb_set_errstring(ldb
, "Invalid reply type\n!");
575 return ldb_module_done(ar
->req
, NULL
, NULL
,
576 LDB_ERR_OPERATIONS_ERROR
);
582 ret
= replmd_replicated_apply_next(ar
);
583 if (ret
!= LDB_SUCCESS
) {
584 return ldb_module_done(ar
->req
, NULL
, NULL
, ret
);
590 static int replmd_replicated_apply_add(struct replmd_replicated_request
*ar
)
592 struct ldb_context
*ldb
;
593 struct ldb_request
*change_req
;
594 enum ndr_err_code ndr_err
;
595 struct ldb_message
*msg
;
596 struct replPropertyMetaDataBlob
*md
;
597 struct ldb_val md_value
;
603 * TODO: check if the parent object exist
607 * TODO: handle the conflict case where an object with the
611 ldb
= ldb_module_get_ctx(ar
->module
);
612 msg
= ar
->objs
->objects
[ar
->index_current
].msg
;
613 md
= ar
->objs
->objects
[ar
->index_current
].meta_data
;
615 ret
= ldb_sequence_number(ldb
, LDB_SEQ_NEXT
, &seq_num
);
616 if (ret
!= LDB_SUCCESS
) {
617 return replmd_replicated_request_error(ar
, ret
);
620 ret
= ldb_msg_add_value(msg
, "objectGUID", &ar
->objs
->objects
[ar
->index_current
].guid_value
, NULL
);
621 if (ret
!= LDB_SUCCESS
) {
622 return replmd_replicated_request_error(ar
, ret
);
625 ret
= ldb_msg_add_string(msg
, "whenChanged", ar
->objs
->objects
[ar
->index_current
].when_changed
);
626 if (ret
!= LDB_SUCCESS
) {
627 return replmd_replicated_request_error(ar
, ret
);
630 ret
= samdb_msg_add_uint64(ldb
, msg
, msg
, "uSNCreated", seq_num
);
631 if (ret
!= LDB_SUCCESS
) {
632 return replmd_replicated_request_error(ar
, ret
);
635 ret
= samdb_msg_add_uint64(ldb
, msg
, msg
, "uSNChanged", seq_num
);
636 if (ret
!= LDB_SUCCESS
) {
637 return replmd_replicated_request_error(ar
, ret
);
641 * the meta data array is already sorted by the caller
643 for (i
=0; i
< md
->ctr
.ctr1
.count
; i
++) {
644 md
->ctr
.ctr1
.array
[i
].local_usn
= seq_num
;
646 ndr_err
= ndr_push_struct_blob(&md_value
, msg
,
647 lp_iconv_convenience(ldb_get_opaque(ldb
, "loadparm")),
649 (ndr_push_flags_fn_t
)ndr_push_replPropertyMetaDataBlob
);
650 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
651 NTSTATUS nt_status
= ndr_map_error2ntstatus(ndr_err
);
652 return replmd_replicated_request_werror(ar
, ntstatus_to_werror(nt_status
));
654 ret
= ldb_msg_add_value(msg
, "replPropertyMetaData", &md_value
, NULL
);
655 if (ret
!= LDB_SUCCESS
) {
656 return replmd_replicated_request_error(ar
, ret
);
659 replmd_ldb_message_sort(msg
, ar
->schema
);
662 char *s
= ldb_ldif_message_string(ldb
, ar
, LDB_CHANGETYPE_ADD
, msg
);
663 DEBUG(4, ("DRS replication add message:\n%s\n", s
));
667 ret
= ldb_build_add_req(&change_req
,
673 replmd_replicated_apply_add_callback
,
675 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
677 return ldb_next_request(ar
->module
, change_req
);
680 static int replmd_replPropertyMetaData1_conflict_compare(struct replPropertyMetaData1
*m1
,
681 struct replPropertyMetaData1
*m2
)
685 if (m1
->version
!= m2
->version
) {
686 return m1
->version
- m2
->version
;
689 if (m1
->originating_change_time
!= m2
->originating_change_time
) {
690 return m1
->originating_change_time
- m2
->originating_change_time
;
693 ret
= GUID_compare(&m1
->originating_invocation_id
, &m2
->originating_invocation_id
);
698 return m1
->originating_usn
- m2
->originating_usn
;
701 static int replmd_replicated_apply_merge_callback(struct ldb_request
*req
,
702 struct ldb_reply
*ares
)
704 struct ldb_context
*ldb
;
705 struct replmd_replicated_request
*ar
= talloc_get_type(req
->context
,
706 struct replmd_replicated_request
);
709 ldb
= ldb_module_get_ctx(ar
->module
);
712 return ldb_module_done(ar
->req
, NULL
, NULL
,
713 LDB_ERR_OPERATIONS_ERROR
);
715 if (ares
->error
!= LDB_SUCCESS
) {
716 return ldb_module_done(ar
->req
, ares
->controls
,
717 ares
->response
, ares
->error
);
720 if (ares
->type
!= LDB_REPLY_DONE
) {
721 ldb_set_errstring(ldb
, "Invalid reply type\n!");
722 return ldb_module_done(ar
->req
, NULL
, NULL
,
723 LDB_ERR_OPERATIONS_ERROR
);
729 ret
= replmd_replicated_apply_next(ar
);
730 if (ret
!= LDB_SUCCESS
) {
731 return ldb_module_done(ar
->req
, NULL
, NULL
, ret
);
737 static int replmd_replicated_apply_merge(struct replmd_replicated_request
*ar
)
739 struct ldb_context
*ldb
;
740 struct ldb_request
*change_req
;
741 enum ndr_err_code ndr_err
;
742 struct ldb_message
*msg
;
743 struct replPropertyMetaDataBlob
*rmd
;
744 struct replPropertyMetaDataBlob omd
;
745 const struct ldb_val
*omd_value
;
746 struct replPropertyMetaDataBlob nmd
;
747 struct ldb_val nmd_value
;
749 uint32_t removed_attrs
= 0;
753 ldb
= ldb_module_get_ctx(ar
->module
);
754 msg
= ar
->objs
->objects
[ar
->index_current
].msg
;
755 rmd
= ar
->objs
->objects
[ar
->index_current
].meta_data
;
760 * TODO: check repl data is correct after a rename
762 if (ldb_dn_compare(msg
->dn
, ar
->search_msg
->dn
) != 0) {
763 ldb_debug(ldb
, LDB_DEBUG_TRACE
, "replmd_replicated_request rename %s => %s\n",
764 ldb_dn_get_linearized(ar
->search_msg
->dn
),
765 ldb_dn_get_linearized(msg
->dn
));
766 if (ldb_rename(ldb
, ar
->search_msg
->dn
, msg
->dn
) != LDB_SUCCESS
) {
767 ldb_debug(ldb
, LDB_DEBUG_FATAL
, "replmd_replicated_request rename %s => %s failed - %s\n",
768 ldb_dn_get_linearized(ar
->search_msg
->dn
),
769 ldb_dn_get_linearized(msg
->dn
),
771 return replmd_replicated_request_werror(ar
, WERR_DS_DRA_DB_ERROR
);
775 ret
= ldb_sequence_number(ldb
, LDB_SEQ_NEXT
, &seq_num
);
776 if (ret
!= LDB_SUCCESS
) {
777 return replmd_replicated_request_error(ar
, ret
);
780 /* find existing meta data */
781 omd_value
= ldb_msg_find_ldb_val(ar
->search_msg
, "replPropertyMetaData");
783 ndr_err
= ndr_pull_struct_blob(omd_value
, ar
,
784 lp_iconv_convenience(ldb_get_opaque(ldb
, "loadparm")), &omd
,
785 (ndr_pull_flags_fn_t
)ndr_pull_replPropertyMetaDataBlob
);
786 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
787 NTSTATUS nt_status
= ndr_map_error2ntstatus(ndr_err
);
788 return replmd_replicated_request_werror(ar
, ntstatus_to_werror(nt_status
));
791 if (omd
.version
!= 1) {
792 return replmd_replicated_request_werror(ar
, WERR_DS_DRA_INTERNAL_ERROR
);
798 nmd
.ctr
.ctr1
.count
= omd
.ctr
.ctr1
.count
+ rmd
->ctr
.ctr1
.count
;
799 nmd
.ctr
.ctr1
.array
= talloc_array(ar
,
800 struct replPropertyMetaData1
,
802 if (!nmd
.ctr
.ctr1
.array
) return replmd_replicated_request_werror(ar
, WERR_NOMEM
);
804 /* first copy the old meta data */
805 for (i
=0; i
< omd
.ctr
.ctr1
.count
; i
++) {
806 nmd
.ctr
.ctr1
.array
[ni
] = omd
.ctr
.ctr1
.array
[i
];
810 /* now merge in the new meta data */
811 for (i
=0; i
< rmd
->ctr
.ctr1
.count
; i
++) {
814 rmd
->ctr
.ctr1
.array
[i
].local_usn
= seq_num
;
816 for (j
=0; j
< ni
; j
++) {
819 if (rmd
->ctr
.ctr1
.array
[i
].attid
!= nmd
.ctr
.ctr1
.array
[j
].attid
) {
823 cmp
= replmd_replPropertyMetaData1_conflict_compare(&rmd
->ctr
.ctr1
.array
[i
],
824 &nmd
.ctr
.ctr1
.array
[j
]);
826 /* replace the entry */
827 nmd
.ctr
.ctr1
.array
[j
] = rmd
->ctr
.ctr1
.array
[i
];
832 /* we don't want to apply this change so remove the attribute */
833 ldb_msg_remove_element(msg
, &msg
->elements
[i
-removed_attrs
]);
842 nmd
.ctr
.ctr1
.array
[ni
] = rmd
->ctr
.ctr1
.array
[i
];
847 * finally correct the size of the meta_data array
849 nmd
.ctr
.ctr1
.count
= ni
;
852 * the rdn attribute (the alias for the name attribute),
853 * 'cn' for most objects is the last entry in the meta data array
856 * sort the new meta data array
859 struct replPropertyMetaData1
*rdn_p
;
860 uint32_t rdn_idx
= omd
.ctr
.ctr1
.count
- 1;
862 rdn_p
= &nmd
.ctr
.ctr1
.array
[rdn_idx
];
863 replmd_replPropertyMetaDataCtr1_sort(&nmd
.ctr
.ctr1
, &rdn_p
->attid
);
866 /* create the meta data value */
867 ndr_err
= ndr_push_struct_blob(&nmd_value
, msg
,
868 lp_iconv_convenience(ldb_get_opaque(ldb
, "loadparm")),
870 (ndr_push_flags_fn_t
)ndr_push_replPropertyMetaDataBlob
);
871 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
872 NTSTATUS nt_status
= ndr_map_error2ntstatus(ndr_err
);
873 return replmd_replicated_request_werror(ar
, ntstatus_to_werror(nt_status
));
877 * check if some replicated attributes left, otherwise skip the ldb_modify() call
879 if (msg
->num_elements
== 0) {
880 ldb_debug(ldb
, LDB_DEBUG_TRACE
, "replmd_replicated_apply_merge[%u]: skip replace\n",
884 return replmd_replicated_apply_next(ar
);
887 ldb_debug(ldb
, LDB_DEBUG_TRACE
, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
888 ar
->index_current
, msg
->num_elements
);
891 * when we know that we'll modify the record, add the whenChanged, uSNChanged
892 * and replPopertyMetaData attributes
894 ret
= ldb_msg_add_string(msg
, "whenChanged", ar
->objs
->objects
[ar
->index_current
].when_changed
);
895 if (ret
!= LDB_SUCCESS
) {
896 return replmd_replicated_request_error(ar
, ret
);
898 ret
= samdb_msg_add_uint64(ldb
, msg
, msg
, "uSNChanged", seq_num
);
899 if (ret
!= LDB_SUCCESS
) {
900 return replmd_replicated_request_error(ar
, ret
);
902 ret
= ldb_msg_add_value(msg
, "replPropertyMetaData", &nmd_value
, NULL
);
903 if (ret
!= LDB_SUCCESS
) {
904 return replmd_replicated_request_error(ar
, ret
);
907 replmd_ldb_message_sort(msg
, ar
->schema
);
909 /* we want to replace the old values */
910 for (i
=0; i
< msg
->num_elements
; i
++) {
911 msg
->elements
[i
].flags
= LDB_FLAG_MOD_REPLACE
;
915 char *s
= ldb_ldif_message_string(ldb
, ar
, LDB_CHANGETYPE_MODIFY
, msg
);
916 DEBUG(4, ("DRS replication modify message:\n%s\n", s
));
920 ret
= ldb_build_mod_req(&change_req
,
926 replmd_replicated_apply_merge_callback
,
928 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
930 return ldb_next_request(ar
->module
, change_req
);
933 static int replmd_replicated_apply_search_callback(struct ldb_request
*req
,
934 struct ldb_reply
*ares
)
936 struct replmd_replicated_request
*ar
= talloc_get_type(req
->context
,
937 struct replmd_replicated_request
);
941 return ldb_module_done(ar
->req
, NULL
, NULL
,
942 LDB_ERR_OPERATIONS_ERROR
);
944 if (ares
->error
!= LDB_SUCCESS
&&
945 ares
->error
!= LDB_ERR_NO_SUCH_OBJECT
) {
946 return ldb_module_done(ar
->req
, ares
->controls
,
947 ares
->response
, ares
->error
);
950 switch (ares
->type
) {
951 case LDB_REPLY_ENTRY
:
952 ar
->search_msg
= talloc_steal(ar
, ares
->message
);
955 case LDB_REPLY_REFERRAL
:
956 /* we ignore referrals */
960 if (ar
->search_msg
!= NULL
) {
961 ret
= replmd_replicated_apply_merge(ar
);
963 ret
= replmd_replicated_apply_add(ar
);
965 if (ret
!= LDB_SUCCESS
) {
966 return ldb_module_done(ar
->req
, NULL
, NULL
, ret
);
974 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request
*ar
);
976 static int replmd_replicated_apply_next(struct replmd_replicated_request
*ar
)
978 struct ldb_context
*ldb
;
982 struct ldb_request
*search_req
;
984 if (ar
->index_current
>= ar
->objs
->num_objects
) {
985 /* done with it, go to the last op */
986 return replmd_replicated_uptodate_vector(ar
);
989 ldb
= ldb_module_get_ctx(ar
->module
);
990 ar
->search_msg
= NULL
;
992 tmp_str
= ldb_binary_encode(ar
, ar
->objs
->objects
[ar
->index_current
].guid_value
);
993 if (!tmp_str
) return replmd_replicated_request_werror(ar
, WERR_NOMEM
);
995 filter
= talloc_asprintf(ar
, "(objectGUID=%s)", tmp_str
);
996 if (!filter
) return replmd_replicated_request_werror(ar
, WERR_NOMEM
);
997 talloc_free(tmp_str
);
999 ret
= ldb_build_search_req(&search_req
,
1002 ar
->objs
->partition_dn
,
1008 replmd_replicated_apply_search_callback
,
1010 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
1012 return ldb_next_request(ar
->module
, search_req
);
1015 static int replmd_replicated_uptodate_modify_callback(struct ldb_request
*req
,
1016 struct ldb_reply
*ares
)
1018 struct ldb_context
*ldb
;
1019 struct replmd_replicated_request
*ar
= talloc_get_type(req
->context
,
1020 struct replmd_replicated_request
);
1021 ldb
= ldb_module_get_ctx(ar
->module
);
1024 return ldb_module_done(ar
->req
, NULL
, NULL
,
1025 LDB_ERR_OPERATIONS_ERROR
);
1027 if (ares
->error
!= LDB_SUCCESS
) {
1028 return ldb_module_done(ar
->req
, ares
->controls
,
1029 ares
->response
, ares
->error
);
1032 if (ares
->type
!= LDB_REPLY_DONE
) {
1033 ldb_set_errstring(ldb
, "Invalid reply type\n!");
1034 return ldb_module_done(ar
->req
, NULL
, NULL
,
1035 LDB_ERR_OPERATIONS_ERROR
);
1040 return ldb_module_done(ar
->req
, NULL
, NULL
, LDB_SUCCESS
);
1043 static int replmd_drsuapi_DsReplicaCursor2_compare(const struct drsuapi_DsReplicaCursor2
*c1
,
1044 const struct drsuapi_DsReplicaCursor2
*c2
)
1046 return GUID_compare(&c1
->source_dsa_invocation_id
, &c2
->source_dsa_invocation_id
);
1049 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request
*ar
)
1051 struct ldb_context
*ldb
;
1052 struct ldb_request
*change_req
;
1053 enum ndr_err_code ndr_err
;
1054 struct ldb_message
*msg
;
1055 struct replUpToDateVectorBlob ouv
;
1056 const struct ldb_val
*ouv_value
;
1057 const struct drsuapi_DsReplicaCursor2CtrEx
*ruv
;
1058 struct replUpToDateVectorBlob nuv
;
1059 struct ldb_val nuv_value
;
1060 struct ldb_message_element
*nuv_el
= NULL
;
1061 const struct GUID
*our_invocation_id
;
1062 struct ldb_message_element
*orf_el
= NULL
;
1063 struct repsFromToBlob nrf
;
1064 struct ldb_val
*nrf_value
= NULL
;
1065 struct ldb_message_element
*nrf_el
= NULL
;
1069 time_t t
= time(NULL
);
1073 ldb
= ldb_module_get_ctx(ar
->module
);
1074 ruv
= ar
->objs
->uptodateness_vector
;
1080 unix_to_nt_time(&now
, t
);
1083 * we use the next sequence number for our own highest_usn
1084 * because we will do a modify request and this will increment
1087 ret
= ldb_sequence_number(ldb
, LDB_SEQ_NEXT
, &seq_num
);
1088 if (ret
!= LDB_SUCCESS
) {
1089 return replmd_replicated_request_error(ar
, ret
);
1093 * first create the new replUpToDateVector
1095 ouv_value
= ldb_msg_find_ldb_val(ar
->search_msg
, "replUpToDateVector");
1097 ndr_err
= ndr_pull_struct_blob(ouv_value
, ar
,
1098 lp_iconv_convenience(ldb_get_opaque(ldb
, "loadparm")), &ouv
,
1099 (ndr_pull_flags_fn_t
)ndr_pull_replUpToDateVectorBlob
);
1100 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1101 NTSTATUS nt_status
= ndr_map_error2ntstatus(ndr_err
);
1102 return replmd_replicated_request_werror(ar
, ntstatus_to_werror(nt_status
));
1105 if (ouv
.version
!= 2) {
1106 return replmd_replicated_request_werror(ar
, WERR_DS_DRA_INTERNAL_ERROR
);
1111 * the new uptodateness vector will at least
1112 * contain 1 entry, one for the source_dsa
1114 * plus optional values from our old vector and the one from the source_dsa
1116 nuv
.ctr
.ctr2
.count
= 1 + ouv
.ctr
.ctr2
.count
;
1117 if (ruv
) nuv
.ctr
.ctr2
.count
+= ruv
->count
;
1118 nuv
.ctr
.ctr2
.cursors
= talloc_array(ar
,
1119 struct drsuapi_DsReplicaCursor2
,
1120 nuv
.ctr
.ctr2
.count
);
1121 if (!nuv
.ctr
.ctr2
.cursors
) return replmd_replicated_request_werror(ar
, WERR_NOMEM
);
1123 /* first copy the old vector */
1124 for (i
=0; i
< ouv
.ctr
.ctr2
.count
; i
++) {
1125 nuv
.ctr
.ctr2
.cursors
[ni
] = ouv
.ctr
.ctr2
.cursors
[i
];
1129 /* get our invocation_id if we have one already attached to the ldb */
1130 our_invocation_id
= samdb_ntds_invocation_id(ldb
);
1132 /* merge in the source_dsa vector is available */
1133 for (i
=0; (ruv
&& i
< ruv
->count
); i
++) {
1136 if (our_invocation_id
&&
1137 GUID_equal(&ruv
->cursors
[i
].source_dsa_invocation_id
,
1138 our_invocation_id
)) {
1142 for (j
=0; j
< ni
; j
++) {
1143 if (!GUID_equal(&ruv
->cursors
[i
].source_dsa_invocation_id
,
1144 &nuv
.ctr
.ctr2
.cursors
[j
].source_dsa_invocation_id
)) {
1151 * we update only the highest_usn and not the latest_sync_success time,
1152 * because the last success stands for direct replication
1154 if (ruv
->cursors
[i
].highest_usn
> nuv
.ctr
.ctr2
.cursors
[j
].highest_usn
) {
1155 nuv
.ctr
.ctr2
.cursors
[j
].highest_usn
= ruv
->cursors
[i
].highest_usn
;
1160 if (found
) continue;
1162 /* if it's not there yet, add it */
1163 nuv
.ctr
.ctr2
.cursors
[ni
] = ruv
->cursors
[i
];
1168 * merge in the current highwatermark for the source_dsa
1171 for (j
=0; j
< ni
; j
++) {
1172 if (!GUID_equal(&ar
->objs
->source_dsa
->source_dsa_invocation_id
,
1173 &nuv
.ctr
.ctr2
.cursors
[j
].source_dsa_invocation_id
)) {
1180 * here we update the highest_usn and last_sync_success time
1181 * because we're directly replicating from the source_dsa
1183 * and use the tmp_highest_usn because this is what we have just applied
1186 nuv
.ctr
.ctr2
.cursors
[j
].highest_usn
= ar
->objs
->source_dsa
->highwatermark
.tmp_highest_usn
;
1187 nuv
.ctr
.ctr2
.cursors
[j
].last_sync_success
= now
;
1192 * here we update the highest_usn and last_sync_success time
1193 * because we're directly replicating from the source_dsa
1195 * and use the tmp_highest_usn because this is what we have just applied
1198 nuv
.ctr
.ctr2
.cursors
[ni
].source_dsa_invocation_id
= ar
->objs
->source_dsa
->source_dsa_invocation_id
;
1199 nuv
.ctr
.ctr2
.cursors
[ni
].highest_usn
= ar
->objs
->source_dsa
->highwatermark
.tmp_highest_usn
;
1200 nuv
.ctr
.ctr2
.cursors
[ni
].last_sync_success
= now
;
1205 * finally correct the size of the cursors array
1207 nuv
.ctr
.ctr2
.count
= ni
;
1212 qsort(nuv
.ctr
.ctr2
.cursors
, nuv
.ctr
.ctr2
.count
,
1213 sizeof(struct drsuapi_DsReplicaCursor2
),
1214 (comparison_fn_t
)replmd_drsuapi_DsReplicaCursor2_compare
);
1217 * create the change ldb_message
1219 msg
= ldb_msg_new(ar
);
1220 if (!msg
) return replmd_replicated_request_werror(ar
, WERR_NOMEM
);
1221 msg
->dn
= ar
->search_msg
->dn
;
1223 ndr_err
= ndr_push_struct_blob(&nuv_value
, msg
,
1224 lp_iconv_convenience(ldb_get_opaque(ldb
, "loadparm")),
1226 (ndr_push_flags_fn_t
)ndr_push_replUpToDateVectorBlob
);
1227 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1228 NTSTATUS nt_status
= ndr_map_error2ntstatus(ndr_err
);
1229 return replmd_replicated_request_werror(ar
, ntstatus_to_werror(nt_status
));
1231 ret
= ldb_msg_add_value(msg
, "replUpToDateVector", &nuv_value
, &nuv_el
);
1232 if (ret
!= LDB_SUCCESS
) {
1233 return replmd_replicated_request_error(ar
, ret
);
1235 nuv_el
->flags
= LDB_FLAG_MOD_REPLACE
;
1238 * now create the new repsFrom value from the given repsFromTo1 structure
1242 nrf
.ctr
.ctr1
= *ar
->objs
->source_dsa
;
1243 /* and fix some values... */
1244 nrf
.ctr
.ctr1
.consecutive_sync_failures
= 0;
1245 nrf
.ctr
.ctr1
.last_success
= now
;
1246 nrf
.ctr
.ctr1
.last_attempt
= now
;
1247 nrf
.ctr
.ctr1
.result_last_attempt
= WERR_OK
;
1248 nrf
.ctr
.ctr1
.highwatermark
.highest_usn
= nrf
.ctr
.ctr1
.highwatermark
.tmp_highest_usn
;
1251 * first see if we already have a repsFrom value for the current source dsa
1252 * if so we'll later replace this value
1254 orf_el
= ldb_msg_find_element(ar
->search_msg
, "repsFrom");
1256 for (i
=0; i
< orf_el
->num_values
; i
++) {
1257 struct repsFromToBlob
*trf
;
1259 trf
= talloc(ar
, struct repsFromToBlob
);
1260 if (!trf
) return replmd_replicated_request_werror(ar
, WERR_NOMEM
);
1262 ndr_err
= ndr_pull_struct_blob(&orf_el
->values
[i
], trf
, lp_iconv_convenience(ldb_get_opaque(ldb
, "loadparm")), trf
,
1263 (ndr_pull_flags_fn_t
)ndr_pull_repsFromToBlob
);
1264 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1265 NTSTATUS nt_status
= ndr_map_error2ntstatus(ndr_err
);
1266 return replmd_replicated_request_werror(ar
, ntstatus_to_werror(nt_status
));
1269 if (trf
->version
!= 1) {
1270 return replmd_replicated_request_werror(ar
, WERR_DS_DRA_INTERNAL_ERROR
);
1274 * we compare the source dsa objectGUID not the invocation_id
1275 * because we want only one repsFrom value per source dsa
1276 * and when the invocation_id of the source dsa has changed we don't need
1277 * the old repsFrom with the old invocation_id
1279 if (!GUID_equal(&trf
->ctr
.ctr1
.source_dsa_obj_guid
,
1280 &ar
->objs
->source_dsa
->source_dsa_obj_guid
)) {
1286 nrf_value
= &orf_el
->values
[i
];
1291 * copy over all old values to the new ldb_message
1293 ret
= ldb_msg_add_empty(msg
, "repsFrom", 0, &nrf_el
);
1294 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
1299 * if we haven't found an old repsFrom value for the current source dsa
1300 * we'll add a new value
1303 struct ldb_val zero_value
;
1304 ZERO_STRUCT(zero_value
);
1305 ret
= ldb_msg_add_value(msg
, "repsFrom", &zero_value
, &nrf_el
);
1306 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
1308 nrf_value
= &nrf_el
->values
[nrf_el
->num_values
- 1];
1311 /* we now fill the value which is already attached to ldb_message */
1312 ndr_err
= ndr_push_struct_blob(nrf_value
, msg
,
1313 lp_iconv_convenience(ldb_get_opaque(ldb
, "loadparm")),
1315 (ndr_push_flags_fn_t
)ndr_push_repsFromToBlob
);
1316 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1317 NTSTATUS nt_status
= ndr_map_error2ntstatus(ndr_err
);
1318 return replmd_replicated_request_werror(ar
, ntstatus_to_werror(nt_status
));
1322 * the ldb_message_element for the attribute, has all the old values and the new one
1323 * so we'll replace the whole attribute with all values
1325 nrf_el
->flags
= LDB_FLAG_MOD_REPLACE
;
1328 char *s
= ldb_ldif_message_string(ldb
, ar
, LDB_CHANGETYPE_MODIFY
, msg
);
1329 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s
));
1333 /* prepare the ldb_modify() request */
1334 ret
= ldb_build_mod_req(&change_req
,
1340 replmd_replicated_uptodate_modify_callback
,
1342 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
1344 return ldb_next_request(ar
->module
, change_req
);
1347 static int replmd_replicated_uptodate_search_callback(struct ldb_request
*req
,
1348 struct ldb_reply
*ares
)
1350 struct replmd_replicated_request
*ar
= talloc_get_type(req
->context
,
1351 struct replmd_replicated_request
);
1355 return ldb_module_done(ar
->req
, NULL
, NULL
,
1356 LDB_ERR_OPERATIONS_ERROR
);
1358 if (ares
->error
!= LDB_SUCCESS
&&
1359 ares
->error
!= LDB_ERR_NO_SUCH_OBJECT
) {
1360 return ldb_module_done(ar
->req
, ares
->controls
,
1361 ares
->response
, ares
->error
);
1364 switch (ares
->type
) {
1365 case LDB_REPLY_ENTRY
:
1366 ar
->search_msg
= talloc_steal(ar
, ares
->message
);
1369 case LDB_REPLY_REFERRAL
:
1370 /* we ignore referrals */
1373 case LDB_REPLY_DONE
:
1374 if (ar
->search_msg
== NULL
) {
1375 ret
= replmd_replicated_request_werror(ar
, WERR_DS_DRA_INTERNAL_ERROR
);
1377 ret
= replmd_replicated_uptodate_modify(ar
);
1379 if (ret
!= LDB_SUCCESS
) {
1380 return ldb_module_done(ar
->req
, NULL
, NULL
, ret
);
1389 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request
*ar
)
1391 struct ldb_context
*ldb
;
1393 static const char *attrs
[] = {
1394 "replUpToDateVector",
1398 struct ldb_request
*search_req
;
1400 ldb
= ldb_module_get_ctx(ar
->module
);
1401 ar
->search_msg
= NULL
;
1403 ret
= ldb_build_search_req(&search_req
,
1406 ar
->objs
->partition_dn
,
1412 replmd_replicated_uptodate_search_callback
,
1414 if (ret
!= LDB_SUCCESS
) return replmd_replicated_request_error(ar
, ret
);
1416 return ldb_next_request(ar
->module
, search_req
);
1419 static int replmd_extended_replicated_objects(struct ldb_module
*module
, struct ldb_request
*req
)
1421 struct ldb_context
*ldb
;
1422 struct dsdb_extended_replicated_objects
*objs
;
1423 struct replmd_replicated_request
*ar
;
1424 struct ldb_control
**ctrls
;
1427 ldb
= ldb_module_get_ctx(module
);
1429 ldb_debug(ldb
, LDB_DEBUG_TRACE
, "replmd_extended_replicated_objects\n");
1431 objs
= talloc_get_type(req
->op
.extended
.data
, struct dsdb_extended_replicated_objects
);
1433 ldb_debug(ldb
, LDB_DEBUG_FATAL
, "replmd_extended_replicated_objects: invalid extended data\n");
1434 return LDB_ERR_PROTOCOL_ERROR
;
1437 if (objs
->version
!= DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION
) {
1438 ldb_debug(ldb
, LDB_DEBUG_FATAL
, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
1439 objs
->version
, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION
);
1440 return LDB_ERR_PROTOCOL_ERROR
;
1443 ar
= replmd_ctx_init(module
, req
);
1445 return LDB_ERR_OPERATIONS_ERROR
;
1448 ar
->schema
= dsdb_get_schema(ldb
);
1450 ldb_debug_set(ldb
, LDB_DEBUG_FATAL
, "replmd_ctx_init: no loaded schema found\n");
1452 return LDB_ERR_CONSTRAINT_VIOLATION
;
1455 ctrls
= req
->controls
;
1457 if (req
->controls
) {
1458 req
->controls
= talloc_memdup(ar
, req
->controls
,
1459 talloc_get_size(req
->controls
));
1460 if (!req
->controls
) return replmd_replicated_request_werror(ar
, WERR_NOMEM
);
1463 ret
= ldb_request_add_control(req
, DSDB_CONTROL_REPLICATED_UPDATE_OID
, false, NULL
);
1464 if (ret
!= LDB_SUCCESS
) {
1468 ar
->controls
= req
->controls
;
1469 req
->controls
= ctrls
;
1471 return replmd_replicated_apply_next(ar
);
1474 static int replmd_extended(struct ldb_module
*module
, struct ldb_request
*req
)
1476 if (strcmp(req
->op
.extended
.oid
, DSDB_EXTENDED_REPLICATED_OBJECTS_OID
) == 0) {
1477 return replmd_extended_replicated_objects(module
, req
);
1480 return ldb_next_request(module
, req
);
1483 _PUBLIC_
const struct ldb_module_ops ldb_repl_meta_data_module_ops
= {
1484 .name
= "repl_meta_data",
1486 .modify
= replmd_modify
,
1487 .extended
= replmd_extended
,