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 <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 "lib/ldb/include/ldb.h"
43 #include "lib/ldb/include/ldb_errors.h"
44 #include "lib/ldb/include/ldb_private.h"
45 #include "dsdb/samdb/samdb.h"
46 #include "dsdb/common/flags.h"
47 #include "librpc/gen_ndr/ndr_misc.h"
48 #include "librpc/gen_ndr/ndr_drsuapi.h"
49 #include "librpc/gen_ndr/ndr_drsblobs.h"
51 struct replmd_replicated_request {
52 struct ldb_module *module;
53 struct ldb_handle *handle;
54 struct ldb_request *orig_req;
56 const struct dsdb_schema *schema;
58 struct dsdb_extended_replicated_objects *objs;
60 uint32_t index_current;
64 struct ldb_request *search_req;
65 struct ldb_message *search_msg;
67 struct ldb_request *change_req;
72 static struct replmd_replicated_request *replmd_replicated_init_handle(struct ldb_module *module,
73 struct ldb_request *req,
74 struct dsdb_extended_replicated_objects *objs)
76 struct replmd_replicated_request *ar;
78 const struct dsdb_schema *schema;
80 schema = dsdb_get_schema(module->ldb);
82 ldb_debug_set(module->ldb, LDB_DEBUG_FATAL,
83 "replmd_replicated_init_handle: no loaded schema found\n");
87 h = talloc_zero(req, struct ldb_handle);
89 ldb_set_errstring(module->ldb, "Out of Memory");
94 h->state = LDB_ASYNC_PENDING;
95 h->status = LDB_SUCCESS;
97 ar = talloc_zero(h, struct replmd_replicated_request);
99 ldb_set_errstring(module->ldb, "Out of Memory");
104 h->private_data = ar;
118 add a time element to a record
120 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
122 struct ldb_message_element *el;
125 if (ldb_msg_find_element(msg, attr) != NULL) {
129 s = ldb_timestring(msg, t);
134 if (ldb_msg_add_string(msg, attr, s) != 0) {
138 el = ldb_msg_find_element(msg, attr);
139 /* always set as replace. This works because on add ops, the flag
141 el->flags = LDB_FLAG_MOD_REPLACE;
147 add a uint64_t element to a record
149 static int add_uint64_element(struct ldb_message *msg, const char *attr, uint64_t v)
151 struct ldb_message_element *el;
153 if (ldb_msg_find_element(msg, attr) != NULL) {
157 if (ldb_msg_add_fmt(msg, attr, "%llu", (unsigned long long)v) != 0) {
161 el = ldb_msg_find_element(msg, attr);
162 /* always set as replace. This works because on add ops, the flag
164 el->flags = LDB_FLAG_MOD_REPLACE;
169 static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1 *m1,
170 const struct replPropertyMetaData1 *m2,
171 const uint32_t *rdn_attid)
173 if (m1->attid == m2->attid) {
178 * the rdn attribute should be at the end!
179 * so we need to return a value greater than zero
180 * which means m1 is greater than m2
182 if (m1->attid == *rdn_attid) {
187 * the rdn attribute should be at the end!
188 * so we need to return a value less than zero
189 * which means m2 is greater than m1
191 if (m2->attid == *rdn_attid) {
195 return m1->attid - m2->attid;
198 static void replmd_replPropertyMetaDataCtr1_sort(struct replPropertyMetaDataCtr1 *ctr1,
199 const uint32_t *rdn_attid)
201 ldb_qsort(ctr1->array, ctr1->count, sizeof(struct replPropertyMetaData1),
202 discard_const_p(void, rdn_attid), (ldb_qsort_cmp_fn_t)replmd_replPropertyMetaData1_attid_sort);
205 static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1,
206 const struct ldb_message_element *e2,
207 const struct dsdb_schema *schema)
209 const struct dsdb_attribute *a1;
210 const struct dsdb_attribute *a2;
213 * TODO: make this faster by caching the dsdb_attribute pointer
214 * on the ldb_messag_element
217 a1 = dsdb_attribute_by_lDAPDisplayName(schema, e1->name);
218 a2 = dsdb_attribute_by_lDAPDisplayName(schema, e2->name);
221 * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
225 return strcasecmp(e1->name, e2->name);
228 return a1->attributeID_id - a2->attributeID_id;
231 static void replmd_ldb_message_sort(struct ldb_message *msg,
232 const struct dsdb_schema *schema)
234 ldb_qsort(msg->elements, msg->num_elements, sizeof(struct ldb_message_element),
235 discard_const_p(void, schema), (ldb_qsort_cmp_fn_t)replmd_ldb_message_element_attid_sort);
238 static int replmd_prepare_originating(struct ldb_module *module, struct ldb_request *req,
239 struct ldb_dn *dn, const char *fn_name,
240 int (*fn)(struct ldb_module *,
241 struct ldb_request *,
242 const struct dsdb_schema *,
243 const struct dsdb_control_current_partition *))
245 const struct dsdb_schema *schema;
246 const struct ldb_control *partition_ctrl;
247 const struct dsdb_control_current_partition *partition;
249 /* do not manipulate our control entries */
250 if (ldb_dn_is_special(dn)) {
251 return ldb_next_request(module, req);
254 schema = dsdb_get_schema(module->ldb);
256 ldb_debug_set(module->ldb, LDB_DEBUG_FATAL,
257 "%s: no dsdb_schema loaded",
259 return LDB_ERR_CONSTRAINT_VIOLATION;
262 partition_ctrl = ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID);
263 if (!partition_ctrl) {
264 ldb_debug_set(module->ldb, LDB_DEBUG_FATAL,
265 "%s: no current partition control found",
267 return LDB_ERR_CONSTRAINT_VIOLATION;
270 partition = talloc_get_type(partition_ctrl->data,
271 struct dsdb_control_current_partition);
273 ldb_debug_set(module->ldb, LDB_DEBUG_FATAL,
274 "%s: current partition control contains invalid data",
276 return LDB_ERR_CONSTRAINT_VIOLATION;
279 if (partition->version != DSDB_CONTROL_CURRENT_PARTITION_VERSION) {
280 ldb_debug_set(module->ldb, LDB_DEBUG_FATAL,
281 "%s: current partition control contains invalid version [%u != %u]\n",
282 fn_name, partition->version, DSDB_CONTROL_CURRENT_PARTITION_VERSION);
283 return LDB_ERR_CONSTRAINT_VIOLATION;
286 return fn(module, req, schema, partition);
289 static int replmd_add_originating(struct ldb_module *module,
290 struct ldb_request *req,
291 const struct dsdb_schema *schema,
292 const struct dsdb_control_current_partition *partition)
295 struct ldb_request *down_req;
296 struct ldb_message *msg;
297 uint32_t instance_type;
298 struct ldb_dn *new_dn;
299 const char *rdn_name;
300 const char *rdn_name_upper;
301 const struct ldb_val *rdn_value = NULL;
302 const struct dsdb_attribute *rdn_attr = NULL;
304 struct ldb_val guid_value;
305 struct replPropertyMetaDataBlob nmd;
306 struct ldb_val nmd_value;
308 const struct GUID *our_invocation_id;
309 time_t t = time(NULL);
315 ldb_debug(module->ldb, LDB_DEBUG_TRACE, "replmd_add_originating\n");
317 if (ldb_msg_find_element(req->op.add.message, "objectGUID")) {
318 ldb_debug_set(module->ldb, LDB_DEBUG_ERROR,
319 "replmd_add_originating: it's not allowed to add an object with objectGUID\n");
320 return LDB_ERR_UNWILLING_TO_PERFORM;
323 if (ldb_msg_find_element(req->op.add.message, "instanceType")) {
324 ldb_debug_set(module->ldb, LDB_DEBUG_ERROR,
325 "replmd_add_originating: it's not allowed to add an object with instanceType\n");
326 return LDB_ERR_UNWILLING_TO_PERFORM;
329 /* Get a sequence number from the backend */
330 ret = ldb_sequence_number(module->ldb, LDB_SEQ_NEXT, &seq_num);
331 if (ret != LDB_SUCCESS) {
336 guid = GUID_random();
338 /* get our invicationId */
339 our_invocation_id = samdb_ntds_invocation_id(module->ldb);
340 if (!our_invocation_id) {
341 ldb_debug_set(module->ldb, LDB_DEBUG_ERROR,
342 "replmd_add_originating: unable to find invocationId\n");
343 return LDB_ERR_OPERATIONS_ERROR;
346 /* create a copy of the request */
347 down_req = talloc(req, struct ldb_request);
348 if (down_req == NULL) {
349 ldb_oom(module->ldb);
350 return LDB_ERR_OPERATIONS_ERROR;
354 /* we have to copy the message as the caller might have it as a const */
355 down_req->op.add.message = msg = ldb_msg_copy_shallow(down_req, req->op.add.message);
357 talloc_free(down_req);
358 ldb_oom(module->ldb);
359 return LDB_ERR_OPERATIONS_ERROR;
362 /* generated times */
363 unix_to_nt_time(&now, t);
364 time_str = ldb_timestring(msg, t);
366 talloc_free(down_req);
367 return LDB_ERR_OPERATIONS_ERROR;
371 * get details of the rdn name
373 rdn_name = ldb_dn_get_rdn_name(msg->dn);
375 talloc_free(down_req);
376 ldb_oom(module->ldb);
377 return LDB_ERR_OPERATIONS_ERROR;
379 rdn_attr = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
381 talloc_free(down_req);
382 return LDB_ERR_OPERATIONS_ERROR;
384 rdn_value = ldb_dn_get_rdn_val(msg->dn);
386 talloc_free(down_req);
387 ldb_oom(module->ldb);
388 return LDB_ERR_OPERATIONS_ERROR;
392 * remove autogenerated attributes
394 ldb_msg_remove_attr(msg, rdn_name);
395 ldb_msg_remove_attr(msg, "name");
396 ldb_msg_remove_attr(msg, "whenCreated");
397 ldb_msg_remove_attr(msg, "whenChanged");
398 ldb_msg_remove_attr(msg, "uSNCreated");
399 ldb_msg_remove_attr(msg, "uSNChanged");
400 ldb_msg_remove_attr(msg, "replPropertyMetaData");
403 * TODO: construct a new DN out of:
405 * - the upper case of rdn_attr->LDAPDisplayName
408 new_dn = ldb_dn_copy(msg, msg->dn);
410 talloc_free(down_req);
411 ldb_oom(module->ldb);
412 return LDB_ERR_OPERATIONS_ERROR;
414 rdn_name_upper = strupper_talloc(msg, rdn_attr->lDAPDisplayName);
415 if (!rdn_name_upper) {
416 talloc_free(down_req);
417 ldb_oom(module->ldb);
418 return LDB_ERR_OPERATIONS_ERROR;
420 ret = ldb_dn_set_component(new_dn, 0, rdn_name_upper, *rdn_value);
421 if (ret != LDB_SUCCESS) {
422 talloc_free(down_req);
423 ldb_oom(module->ldb);
424 return LDB_ERR_OPERATIONS_ERROR;
429 * TODO: calculate correct instance type
431 instance_type = INSTANCE_TYPE_WRITE;
432 if (ldb_dn_compare(partition->dn, msg->dn) == 0) {
433 instance_type |= INSTANCE_TYPE_IS_NC_HEAD;
434 if (ldb_dn_compare(msg->dn, samdb_base_dn(module->ldb)) != 0) {
435 instance_type |= INSTANCE_TYPE_NC_ABOVE;
440 * readd replicated attributes
442 ret = ldb_msg_add_value(msg, rdn_attr->lDAPDisplayName, rdn_value, NULL);
443 if (ret != LDB_SUCCESS) {
444 talloc_free(down_req);
445 ldb_oom(module->ldb);
446 return LDB_ERR_OPERATIONS_ERROR;
448 ret = ldb_msg_add_value(msg, "name", rdn_value, NULL);
449 if (ret != LDB_SUCCESS) {
450 talloc_free(down_req);
451 ldb_oom(module->ldb);
452 return LDB_ERR_OPERATIONS_ERROR;
454 ret = ldb_msg_add_string(msg, "whenCreated", time_str);
455 if (ret != LDB_SUCCESS) {
456 talloc_free(down_req);
457 ldb_oom(module->ldb);
458 return LDB_ERR_OPERATIONS_ERROR;
460 ret = ldb_msg_add_fmt(msg, "instanceType", "%u", instance_type);
461 if (ret != LDB_SUCCESS) {
462 talloc_free(down_req);
463 ldb_oom(module->ldb);
464 return LDB_ERR_OPERATIONS_ERROR;
467 /* build the replication meta_data */
470 nmd.ctr.ctr1.count = msg->num_elements;
471 nmd.ctr.ctr1.array = talloc_array(msg,
472 struct replPropertyMetaData1,
474 if (!nmd.ctr.ctr1.array) {
475 talloc_free(down_req);
476 ldb_oom(module->ldb);
477 return LDB_ERR_OPERATIONS_ERROR;
480 for (i=0; i < msg->num_elements; i++) {
481 struct ldb_message_element *e = &msg->elements[i];
482 struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
483 const struct dsdb_attribute *sa;
485 if (e->name[0] == '@') continue;
487 sa = dsdb_attribute_by_lDAPDisplayName(schema, e->name);
489 ldb_debug_set(module->ldb, LDB_DEBUG_ERROR,
490 "replmd_add_originating: attribute '%s' not defined in schema\n",
492 talloc_free(down_req);
493 return LDB_ERR_NO_SUCH_ATTRIBUTE;
496 if ((sa->systemFlags & 0x00000001) || (sa->systemFlags & 0x00000004)) {
497 /* if the attribute is not replicated (0x00000001)
498 * or constructed (0x00000004) it has no metadata
503 m->attid = sa->attributeID_id;
505 m->originating_change_time = now;
506 m->originating_invocation_id = *our_invocation_id;
507 m->originating_usn = seq_num;
508 m->local_usn = seq_num;
512 /* fix meta data count */
513 nmd.ctr.ctr1.count = ni;
516 * sort meta data array, and move the rdn attribute entry to the end
518 replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, &rdn_attr->attributeID_id);
520 /* generated NDR encoded values */
521 nt_status = ndr_push_struct_blob(&guid_value, msg, &guid,
522 (ndr_push_flags_fn_t)ndr_push_GUID);
523 if (!NT_STATUS_IS_OK(nt_status)) {
524 talloc_free(down_req);
525 ldb_oom(module->ldb);
526 return LDB_ERR_OPERATIONS_ERROR;
528 nt_status = ndr_push_struct_blob(&nmd_value, msg, &nmd,
529 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
530 if (!NT_STATUS_IS_OK(nt_status)) {
531 talloc_free(down_req);
532 ldb_oom(module->ldb);
533 return LDB_ERR_OPERATIONS_ERROR;
537 * add the autogenerated values
539 ret = ldb_msg_add_value(msg, "objectGUID", &guid_value, NULL);
540 if (ret != LDB_SUCCESS) {
541 talloc_free(down_req);
542 ldb_oom(module->ldb);
543 return LDB_ERR_OPERATIONS_ERROR;
545 ret = ldb_msg_add_string(msg, "whenChanged", time_str);
546 if (ret != LDB_SUCCESS) {
547 talloc_free(down_req);
548 ldb_oom(module->ldb);
549 return LDB_ERR_OPERATIONS_ERROR;
551 ret = samdb_msg_add_uint64(module->ldb, msg, msg, "uSNCreated", seq_num);
552 if (ret != LDB_SUCCESS) {
553 talloc_free(down_req);
554 ldb_oom(module->ldb);
555 return LDB_ERR_OPERATIONS_ERROR;
557 ret = samdb_msg_add_uint64(module->ldb, msg, msg, "uSNChanged", seq_num);
558 if (ret != LDB_SUCCESS) {
559 talloc_free(down_req);
560 ldb_oom(module->ldb);
561 return LDB_ERR_OPERATIONS_ERROR;
563 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
564 if (ret != LDB_SUCCESS) {
565 talloc_free(down_req);
566 ldb_oom(module->ldb);
567 return LDB_ERR_OPERATIONS_ERROR;
571 * sort the attributes by attid before storing the object
573 replmd_ldb_message_sort(msg, schema);
575 ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
577 /* go on with the call chain */
578 ret = ldb_next_request(module, down_req);
580 /* do not free down_req as the call results may be linked to it,
581 * it will be freed when the upper level request get freed */
582 if (ret == LDB_SUCCESS) {
583 req->handle = down_req->handle;
589 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
591 return replmd_prepare_originating(module, req, req->op.add.message->dn,
592 "replmd_add", replmd_add_originating);
595 static int replmd_modify_originating(struct ldb_module *module,
596 struct ldb_request *req,
597 const struct dsdb_schema *schema,
598 const struct dsdb_control_current_partition *partition)
600 struct ldb_request *down_req;
601 struct ldb_message *msg;
603 time_t t = time(NULL);
606 ldb_debug(module->ldb, LDB_DEBUG_TRACE, "replmd_modify_originating\n");
608 down_req = talloc(req, struct ldb_request);
609 if (down_req == NULL) {
610 return LDB_ERR_OPERATIONS_ERROR;
615 /* we have to copy the message as the caller might have it as a const */
616 down_req->op.mod.message = msg = ldb_msg_copy_shallow(down_req, req->op.mod.message);
618 talloc_free(down_req);
619 return LDB_ERR_OPERATIONS_ERROR;
622 if (add_time_element(msg, "whenChanged", t) != 0) {
623 talloc_free(down_req);
624 return LDB_ERR_OPERATIONS_ERROR;
627 /* Get a sequence number from the backend */
628 ret = ldb_sequence_number(module->ldb, LDB_SEQ_NEXT, &seq_num);
629 if (ret == LDB_SUCCESS) {
630 if (add_uint64_element(msg, "uSNChanged", seq_num) != 0) {
631 talloc_free(down_req);
632 return LDB_ERR_OPERATIONS_ERROR;
636 ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
638 /* go on with the call chain */
639 ret = ldb_next_request(module, down_req);
641 /* do not free down_req as the call results may be linked to it,
642 * it will be freed when the upper level request get freed */
643 if (ret == LDB_SUCCESS) {
644 req->handle = down_req->handle;
650 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
652 return replmd_prepare_originating(module, req, req->op.mod.message->dn,
653 "replmd_modify", replmd_modify_originating);
656 static int replmd_replicated_request_reply_helper(struct replmd_replicated_request *ar, int ret)
658 struct ldb_reply *ares = NULL;
660 ar->handle->status = ret;
661 ar->handle->state = LDB_ASYNC_DONE;
663 if (!ar->orig_req->callback) {
667 /* we're done and need to report the success to the caller */
668 ares = talloc_zero(ar, struct ldb_reply);
670 ar->handle->status = LDB_ERR_OPERATIONS_ERROR;
671 ar->handle->state = LDB_ASYNC_DONE;
672 return LDB_ERR_OPERATIONS_ERROR;
675 ares->type = LDB_REPLY_EXTENDED;
676 ares->response = NULL;
678 return ar->orig_req->callback(ar->module->ldb, ar->orig_req->context, ares);
681 static int replmd_replicated_request_done(struct replmd_replicated_request *ar)
683 return replmd_replicated_request_reply_helper(ar, LDB_SUCCESS);
686 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
688 return replmd_replicated_request_reply_helper(ar, ret);
691 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
693 int ret = LDB_ERR_OTHER;
694 /* TODO: do some error mapping */
695 return replmd_replicated_request_reply_helper(ar, ret);
698 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar);
700 static int replmd_replicated_apply_add_callback(struct ldb_context *ldb,
702 struct ldb_reply *ares)
704 #ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */
705 struct replmd_replicated_request *ar = talloc_get_type(private_data,
706 struct replmd_replicated_request);
708 ar->sub.change_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
709 if (ar->sub.change_ret != LDB_SUCCESS) {
710 return replmd_replicated_request_error(ar, ar->sub.change_ret);
713 talloc_free(ar->sub.mem_ctx);
714 ZERO_STRUCT(ar->sub);
718 return replmd_replicated_apply_next(ar);
724 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
727 struct ldb_message *msg;
728 struct replPropertyMetaDataBlob *md;
729 struct ldb_val md_value;
735 * TODO: check if the parent object exist
739 * TODO: handle the conflict case where an object with the
743 msg = ar->objs->objects[ar->index_current].msg;
744 md = ar->objs->objects[ar->index_current].meta_data;
746 ret = ldb_sequence_number(ar->module->ldb, LDB_SEQ_NEXT, &seq_num);
747 if (ret != LDB_SUCCESS) {
748 return replmd_replicated_request_error(ar, ret);
751 ret = ldb_msg_add_value(msg, "objectGUID", &ar->objs->objects[ar->index_current].guid_value, NULL);
752 if (ret != LDB_SUCCESS) {
753 return replmd_replicated_request_error(ar, ret);
756 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
757 if (ret != LDB_SUCCESS) {
758 return replmd_replicated_request_error(ar, ret);
761 ret = samdb_msg_add_uint64(ar->module->ldb, msg, msg, "uSNCreated", seq_num);
762 if (ret != LDB_SUCCESS) {
763 return replmd_replicated_request_error(ar, ret);
766 ret = samdb_msg_add_uint64(ar->module->ldb, msg, msg, "uSNChanged", seq_num);
767 if (ret != LDB_SUCCESS) {
768 return replmd_replicated_request_error(ar, ret);
772 * the meta data array is already sorted by the caller
774 for (i=0; i < md->ctr.ctr1.count; i++) {
775 md->ctr.ctr1.array[i].local_usn = seq_num;
777 nt_status = ndr_push_struct_blob(&md_value, msg, md,
778 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
779 if (!NT_STATUS_IS_OK(nt_status)) {
780 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
782 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
783 if (ret != LDB_SUCCESS) {
784 return replmd_replicated_request_error(ar, ret);
787 replmd_ldb_message_sort(msg, ar->schema);
789 ret = ldb_build_add_req(&ar->sub.change_req,
795 replmd_replicated_apply_add_callback);
796 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
798 #ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */
799 return ldb_next_request(ar->module, ar->sub.change_req);
801 ret = ldb_next_request(ar->module, ar->sub.change_req);
802 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
804 ar->sub.change_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
805 if (ar->sub.change_ret != LDB_SUCCESS) {
806 return replmd_replicated_request_error(ar, ar->sub.change_ret);
809 talloc_free(ar->sub.mem_ctx);
810 ZERO_STRUCT(ar->sub);
818 static int replmd_replPropertyMetaData1_conflict_compare(struct replPropertyMetaData1 *m1,
819 struct replPropertyMetaData1 *m2)
823 if (m1->version != m2->version) {
824 return m1->version - m2->version;
827 if (m1->originating_change_time != m2->originating_change_time) {
828 return m1->originating_change_time - m2->originating_change_time;
831 ret = GUID_compare(&m1->originating_invocation_id, &m2->originating_invocation_id);
836 return m1->originating_usn - m2->originating_usn;
839 static int replmd_replicated_apply_merge_callback(struct ldb_context *ldb,
841 struct ldb_reply *ares)
843 #ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */
844 struct replmd_replicated_request *ar = talloc_get_type(private_data,
845 struct replmd_replicated_request);
847 ret = ldb_next_request(ar->module, ar->sub.change_req);
848 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
850 ar->sub.change_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
851 if (ar->sub.change_ret != LDB_SUCCESS) {
852 return replmd_replicated_request_error(ar, ar->sub.change_ret);
855 talloc_free(ar->sub.mem_ctx);
856 ZERO_STRUCT(ar->sub);
866 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
869 struct ldb_message *msg;
870 struct replPropertyMetaDataBlob *rmd;
871 struct replPropertyMetaDataBlob omd;
872 const struct ldb_val *omd_value;
873 struct replPropertyMetaDataBlob nmd;
874 struct ldb_val nmd_value;
876 uint32_t removed_attrs = 0;
880 msg = ar->objs->objects[ar->index_current].msg;
881 rmd = ar->objs->objects[ar->index_current].meta_data;
886 * TODO: add rename conflict handling
888 if (ldb_dn_compare(msg->dn, ar->sub.search_msg->dn) != 0) {
889 ldb_debug_set(ar->module->ldb, LDB_DEBUG_FATAL, "replmd_replicated_apply_merge[%u]: rename not supported",
891 ldb_debug(ar->module->ldb, LDB_DEBUG_FATAL, "%s => %s\n",
892 ldb_dn_get_linearized(ar->sub.search_msg->dn),
893 ldb_dn_get_linearized(msg->dn));
894 return replmd_replicated_request_werror(ar, WERR_NOT_SUPPORTED);
897 ret = ldb_sequence_number(ar->module->ldb, LDB_SEQ_NEXT, &seq_num);
898 if (ret != LDB_SUCCESS) {
899 return replmd_replicated_request_error(ar, ret);
902 /* find existing meta data */
903 omd_value = ldb_msg_find_ldb_val(ar->sub.search_msg, "replPropertyMetaData");
905 nt_status = ndr_pull_struct_blob(omd_value, ar->sub.mem_ctx, &omd,
906 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
907 if (!NT_STATUS_IS_OK(nt_status)) {
908 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
911 if (omd.version != 1) {
912 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
918 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
919 nmd.ctr.ctr1.array = talloc_array(ar->sub.mem_ctx,
920 struct replPropertyMetaData1,
922 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOMEM);
924 /* first copy the old meta data */
925 for (i=0; i < omd.ctr.ctr1.count; i++) {
926 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
930 /* now merge in the new meta data */
931 for (i=0; i < rmd->ctr.ctr1.count; i++) {
934 rmd->ctr.ctr1.array[i].local_usn = seq_num;
936 for (j=0; j < ni; j++) {
939 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
943 cmp = replmd_replPropertyMetaData1_conflict_compare(&rmd->ctr.ctr1.array[i],
944 &nmd.ctr.ctr1.array[j]);
946 /* replace the entry */
947 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
952 /* we don't want to apply this change so remove the attribute */
953 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
962 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
967 * finally correct the size of the meta_data array
969 nmd.ctr.ctr1.count = ni;
972 * the rdn attribute (the alias for the name attribute),
973 * 'cn' for most objects is the last entry in the meta data array
976 * sort the new meta data array
979 struct replPropertyMetaData1 *rdn_p;
980 uint32_t rdn_idx = omd.ctr.ctr1.count - 1;
982 rdn_p = &nmd.ctr.ctr1.array[rdn_idx];
983 replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, &rdn_p->attid);
986 /* create the meta data value */
987 nt_status = ndr_push_struct_blob(&nmd_value, msg, &nmd,
988 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
989 if (!NT_STATUS_IS_OK(nt_status)) {
990 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
994 * check if some replicated attributes left, otherwise skip the ldb_modify() call
996 if (msg->num_elements == 0) {
997 ldb_debug(ar->module->ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
1002 ldb_debug(ar->module->ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
1003 ar->index_current, msg->num_elements);
1006 * when we now that we'll modify the record, add the whenChanged, uSNChanged
1007 * and replPopertyMetaData attributes
1009 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
1010 if (ret != LDB_SUCCESS) {
1011 return replmd_replicated_request_error(ar, ret);
1013 ret = samdb_msg_add_uint64(ar->module->ldb, msg, msg, "uSNChanged", seq_num);
1014 if (ret != LDB_SUCCESS) {
1015 return replmd_replicated_request_error(ar, ret);
1017 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
1018 if (ret != LDB_SUCCESS) {
1019 return replmd_replicated_request_error(ar, ret);
1022 replmd_ldb_message_sort(msg, ar->schema);
1024 /* we want to replace the old values */
1025 for (i=0; i < msg->num_elements; i++) {
1026 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
1029 ret = ldb_build_mod_req(&ar->sub.change_req,
1035 replmd_replicated_apply_merge_callback);
1036 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1038 #ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */
1039 return ldb_next_request(ar->module, ar->sub.change_req);
1041 ret = ldb_next_request(ar->module, ar->sub.change_req);
1042 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1044 ar->sub.change_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
1045 if (ar->sub.change_ret != LDB_SUCCESS) {
1046 return replmd_replicated_request_error(ar, ar->sub.change_ret);
1050 talloc_free(ar->sub.mem_ctx);
1051 ZERO_STRUCT(ar->sub);
1053 ar->index_current++;
1059 static int replmd_replicated_apply_search_callback(struct ldb_context *ldb,
1061 struct ldb_reply *ares)
1063 struct replmd_replicated_request *ar = talloc_get_type(private_data,
1064 struct replmd_replicated_request);
1065 bool is_done = false;
1067 switch (ares->type) {
1068 case LDB_REPLY_ENTRY:
1069 ar->sub.search_msg = talloc_steal(ar->sub.mem_ctx, ares->message);
1071 case LDB_REPLY_REFERRAL:
1072 /* we ignore referrals */
1074 case LDB_REPLY_EXTENDED:
1075 case LDB_REPLY_DONE:
1081 #ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */
1083 ar->sub.search_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
1084 if (ar->sub.search_ret != LDB_SUCCESS) {
1085 return replmd_replicated_request_error(ar, ar->sub.search_ret);
1087 if (ar->sub.search_msg) {
1088 return replmd_replicated_apply_merge(ar);
1090 return replmd_replicated_apply_add(ar);
1096 static int replmd_replicated_apply_search(struct replmd_replicated_request *ar)
1102 tmp_str = ldb_binary_encode(ar->sub.mem_ctx, ar->objs->objects[ar->index_current].guid_value);
1103 if (!tmp_str) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1105 filter = talloc_asprintf(ar->sub.mem_ctx, "(objectGUID=%s)", tmp_str);
1106 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1107 talloc_free(tmp_str);
1109 ret = ldb_build_search_req(&ar->sub.search_req,
1112 ar->objs->partition_dn,
1118 replmd_replicated_apply_search_callback);
1119 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1121 #ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */
1122 return ldb_next_request(ar->module, ar->sub.search_req);
1124 ret = ldb_next_request(ar->module, ar->sub.search_req);
1125 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1127 ar->sub.search_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
1128 if (ar->sub.search_ret != LDB_SUCCESS) {
1129 return replmd_replicated_request_error(ar, ar->sub.search_ret);
1131 if (ar->sub.search_msg) {
1132 return replmd_replicated_apply_merge(ar);
1135 return replmd_replicated_apply_add(ar);
1139 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
1141 #ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */
1142 if (ar->index_current >= ar->objs->num_objects) {
1143 return replmd_replicated_uptodate_vector(ar);
1147 ar->sub.mem_ctx = talloc_new(ar);
1148 if (!ar->sub.mem_ctx) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1150 return replmd_replicated_apply_search(ar);
1153 static int replmd_replicated_uptodate_modify_callback(struct ldb_context *ldb,
1155 struct ldb_reply *ares)
1157 #ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */
1158 struct replmd_replicated_request *ar = talloc_get_type(private_data,
1159 struct replmd_replicated_request);
1161 ar->sub.change_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
1162 if (ar->sub.change_ret != LDB_SUCCESS) {
1163 return replmd_replicated_request_error(ar, ar->sub.change_ret);
1166 talloc_free(ar->sub.mem_ctx);
1167 ZERO_STRUCT(ar->sub);
1169 return replmd_replicated_request_done(ar);
1175 static int replmd_drsuapi_DsReplicaCursor2_compare(const struct drsuapi_DsReplicaCursor2 *c1,
1176 const struct drsuapi_DsReplicaCursor2 *c2)
1178 return GUID_compare(&c1->source_dsa_invocation_id, &c2->source_dsa_invocation_id);
1181 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
1184 struct ldb_message *msg;
1185 struct replUpToDateVectorBlob ouv;
1186 const struct ldb_val *ouv_value;
1187 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
1188 struct replUpToDateVectorBlob nuv;
1189 struct ldb_val nuv_value;
1190 struct ldb_message_element *nuv_el = NULL;
1191 const struct GUID *our_invocation_id;
1192 struct ldb_message_element *orf_el = NULL;
1193 struct repsFromToBlob nrf;
1194 struct ldb_val *nrf_value = NULL;
1195 struct ldb_message_element *nrf_el = NULL;
1199 time_t t = time(NULL);
1203 ruv = ar->objs->uptodateness_vector;
1209 unix_to_nt_time(&now, t);
1212 * we use the next sequence number for our own highest_usn
1213 * because we will do a modify request and this will increment
1216 ret = ldb_sequence_number(ar->module->ldb, LDB_SEQ_NEXT, &seq_num);
1217 if (ret != LDB_SUCCESS) {
1218 return replmd_replicated_request_error(ar, ret);
1222 * first create the new replUpToDateVector
1224 ouv_value = ldb_msg_find_ldb_val(ar->sub.search_msg, "replUpToDateVector");
1226 nt_status = ndr_pull_struct_blob(ouv_value, ar->sub.mem_ctx, &ouv,
1227 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
1228 if (!NT_STATUS_IS_OK(nt_status)) {
1229 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
1232 if (ouv.version != 2) {
1233 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
1238 * the new uptodateness vector will at least
1239 * contain 1 entry, one for the source_dsa
1241 * plus optional values from our old vector and the one from the source_dsa
1243 nuv.ctr.ctr2.count = 1 + ouv.ctr.ctr2.count;
1244 if (ruv) nuv.ctr.ctr2.count += ruv->count;
1245 nuv.ctr.ctr2.cursors = talloc_array(ar->sub.mem_ctx,
1246 struct drsuapi_DsReplicaCursor2,
1247 nuv.ctr.ctr2.count);
1248 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1250 /* first copy the old vector */
1251 for (i=0; i < ouv.ctr.ctr2.count; i++) {
1252 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
1256 /* get our invocation_id if we have one already attached to the ldb */
1257 our_invocation_id = samdb_ntds_invocation_id(ar->module->ldb);
1259 /* merge in the source_dsa vector is available */
1260 for (i=0; (ruv && i < ruv->count); i++) {
1263 if (our_invocation_id &&
1264 GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
1265 our_invocation_id)) {
1269 for (j=0; j < ni; j++) {
1270 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
1271 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
1278 * we update only the highest_usn and not the latest_sync_success time,
1279 * because the last success stands for direct replication
1281 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
1282 nuv.ctr.ctr2.cursors[j].highest_usn = ruv->cursors[i].highest_usn;
1287 if (found) continue;
1289 /* if it's not there yet, add it */
1290 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
1295 * merge in the current highwatermark for the source_dsa
1298 for (j=0; j < ni; j++) {
1299 if (!GUID_equal(&ar->objs->source_dsa->source_dsa_invocation_id,
1300 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
1307 * here we update the highest_usn and last_sync_success time
1308 * because we're directly replicating from the source_dsa
1310 * and use the tmp_highest_usn because this is what we have just applied
1313 nuv.ctr.ctr2.cursors[j].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
1314 nuv.ctr.ctr2.cursors[j].last_sync_success = now;
1319 * here we update the highest_usn and last_sync_success time
1320 * because we're directly replicating from the source_dsa
1322 * and use the tmp_highest_usn because this is what we have just applied
1325 nuv.ctr.ctr2.cursors[ni].source_dsa_invocation_id= ar->objs->source_dsa->source_dsa_invocation_id;
1326 nuv.ctr.ctr2.cursors[ni].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
1327 nuv.ctr.ctr2.cursors[ni].last_sync_success = now;
1332 * finally correct the size of the cursors array
1334 nuv.ctr.ctr2.count = ni;
1339 qsort(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count,
1340 sizeof(struct drsuapi_DsReplicaCursor2),
1341 (comparison_fn_t)replmd_drsuapi_DsReplicaCursor2_compare);
1344 * create the change ldb_message
1346 msg = ldb_msg_new(ar->sub.mem_ctx);
1347 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1348 msg->dn = ar->sub.search_msg->dn;
1350 nt_status = ndr_push_struct_blob(&nuv_value, msg, &nuv,
1351 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
1352 if (!NT_STATUS_IS_OK(nt_status)) {
1353 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
1355 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
1356 if (ret != LDB_SUCCESS) {
1357 return replmd_replicated_request_error(ar, ret);
1359 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
1362 * now create the new repsFrom value from the given repsFromTo1 structure
1366 nrf.ctr.ctr1 = *ar->objs->source_dsa;
1367 /* and fix some values... */
1368 nrf.ctr.ctr1.consecutive_sync_failures = 0;
1369 nrf.ctr.ctr1.last_success = now;
1370 nrf.ctr.ctr1.last_attempt = now;
1371 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
1372 nrf.ctr.ctr1.highwatermark.highest_usn = nrf.ctr.ctr1.highwatermark.tmp_highest_usn;
1375 * first see if we already have a repsFrom value for the current source dsa
1376 * if so we'll later replace this value
1378 orf_el = ldb_msg_find_element(ar->sub.search_msg, "repsFrom");
1380 for (i=0; i < orf_el->num_values; i++) {
1381 struct repsFromToBlob *trf;
1383 trf = talloc(ar->sub.mem_ctx, struct repsFromToBlob);
1384 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1386 nt_status = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
1387 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
1388 if (!NT_STATUS_IS_OK(nt_status)) {
1389 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
1392 if (trf->version != 1) {
1393 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
1397 * we compare the source dsa objectGUID not the invocation_id
1398 * because we want only one repsFrom value per source dsa
1399 * and when the invocation_id of the source dsa has changed we don't need
1400 * the old repsFrom with the old invocation_id
1402 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
1403 &ar->objs->source_dsa->source_dsa_obj_guid)) {
1409 nrf_value = &orf_el->values[i];
1414 * copy over all old values to the new ldb_message
1416 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
1417 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1422 * if we haven't found an old repsFrom value for the current source dsa
1423 * we'll add a new value
1426 struct ldb_val zero_value;
1427 ZERO_STRUCT(zero_value);
1428 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
1429 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1431 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
1434 /* we now fill the value which is already attached to ldb_message */
1435 nt_status = ndr_push_struct_blob(nrf_value, msg, &nrf,
1436 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
1437 if (!NT_STATUS_IS_OK(nt_status)) {
1438 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
1442 * the ldb_message_element for the attribute, has all the old values and the new one
1443 * so we'll replace the whole attribute with all values
1445 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
1447 /* prepare the ldb_modify() request */
1448 ret = ldb_build_mod_req(&ar->sub.change_req,
1454 replmd_replicated_uptodate_modify_callback);
1455 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1457 #ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */
1458 return ldb_next_request(ar->module, ar->sub.change_req);
1460 ret = ldb_next_request(ar->module, ar->sub.change_req);
1461 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1463 ar->sub.change_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
1464 if (ar->sub.change_ret != LDB_SUCCESS) {
1465 return replmd_replicated_request_error(ar, ar->sub.change_ret);
1468 talloc_free(ar->sub.mem_ctx);
1469 ZERO_STRUCT(ar->sub);
1471 return replmd_replicated_request_done(ar);
1475 static int replmd_replicated_uptodate_search_callback(struct ldb_context *ldb,
1477 struct ldb_reply *ares)
1479 struct replmd_replicated_request *ar = talloc_get_type(private_data,
1480 struct replmd_replicated_request);
1481 bool is_done = false;
1483 switch (ares->type) {
1484 case LDB_REPLY_ENTRY:
1485 ar->sub.search_msg = talloc_steal(ar->sub.mem_ctx, ares->message);
1487 case LDB_REPLY_REFERRAL:
1488 /* we ignore referrals */
1490 case LDB_REPLY_EXTENDED:
1491 case LDB_REPLY_DONE:
1497 #ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */
1499 ar->sub.search_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
1500 if (ar->sub.search_ret != LDB_SUCCESS) {
1501 return replmd_replicated_request_error(ar, ar->sub.search_ret);
1503 if (!ar->sub.search_msg) {
1504 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
1507 return replmd_replicated_uptodate_modify(ar);
1513 static int replmd_replicated_uptodate_search(struct replmd_replicated_request *ar)
1516 static const char *attrs[] = {
1517 "replUpToDateVector",
1522 ret = ldb_build_search_req(&ar->sub.search_req,
1525 ar->objs->partition_dn,
1531 replmd_replicated_uptodate_search_callback);
1532 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1534 #ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */
1535 return ldb_next_request(ar->module, ar->sub.search_req);
1537 ret = ldb_next_request(ar->module, ar->sub.search_req);
1538 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1540 ar->sub.search_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
1541 if (ar->sub.search_ret != LDB_SUCCESS) {
1542 return replmd_replicated_request_error(ar, ar->sub.search_ret);
1544 if (!ar->sub.search_msg) {
1545 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
1548 return replmd_replicated_uptodate_modify(ar);
1552 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
1554 ar->sub.mem_ctx = talloc_new(ar);
1555 if (!ar->sub.mem_ctx) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1557 return replmd_replicated_uptodate_search(ar);
1560 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
1562 struct dsdb_extended_replicated_objects *objs;
1563 struct replmd_replicated_request *ar;
1565 ldb_debug(module->ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
1567 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
1569 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
1570 return LDB_ERR_PROTOCOL_ERROR;
1573 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
1574 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
1575 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
1576 return LDB_ERR_PROTOCOL_ERROR;
1579 ar = replmd_replicated_init_handle(module, req, objs);
1581 return LDB_ERR_OPERATIONS_ERROR;
1584 #ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */
1585 return replmd_replicated_apply_next(ar);
1587 while (ar->index_current < ar->objs->num_objects &&
1588 req->handle->state != LDB_ASYNC_DONE) {
1589 replmd_replicated_apply_next(ar);
1592 if (req->handle->state != LDB_ASYNC_DONE) {
1593 replmd_replicated_uptodate_vector(ar);
1600 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
1602 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
1603 return replmd_extended_replicated_objects(module, req);
1606 return ldb_next_request(module, req);
1609 static int replmd_wait_none(struct ldb_handle *handle) {
1610 struct replmd_replicated_request *ar;
1612 if (!handle || !handle->private_data) {
1613 return LDB_ERR_OPERATIONS_ERROR;
1616 ar = talloc_get_type(handle->private_data, struct replmd_replicated_request);
1618 return LDB_ERR_OPERATIONS_ERROR;
1621 /* we do only sync calls */
1622 if (handle->state != LDB_ASYNC_DONE) {
1623 return LDB_ERR_OPERATIONS_ERROR;
1626 return handle->status;
1629 static int replmd_wait_all(struct ldb_handle *handle) {
1633 while (handle->state != LDB_ASYNC_DONE) {
1634 ret = replmd_wait_none(handle);
1635 if (ret != LDB_SUCCESS) {
1640 return handle->status;
1643 static int replmd_wait(struct ldb_handle *handle, enum ldb_wait_type type)
1645 if (type == LDB_WAIT_ALL) {
1646 return replmd_wait_all(handle);
1648 return replmd_wait_none(handle);
1652 static const struct ldb_module_ops replmd_ops = {
1653 .name = "repl_meta_data",
1655 .modify = replmd_modify,
1656 .extended = replmd_extended,
1660 int repl_meta_data_module_init(void)
1662 return ldb_register_module(&replmd_ops);