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)
294 enum ndr_err_code ndr_err;
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 ndr_err = ndr_push_struct_blob(&guid_value, msg, &guid,
522 (ndr_push_flags_fn_t)ndr_push_GUID);
523 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
524 ldb_oom(module->ldb);
525 return LDB_ERR_OPERATIONS_ERROR;
527 ndr_err = ndr_push_struct_blob(&nmd_value, msg, &nmd,
528 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
529 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
530 talloc_free(down_req);
531 ldb_oom(module->ldb);
532 return LDB_ERR_OPERATIONS_ERROR;
536 * add the autogenerated values
538 ret = ldb_msg_add_value(msg, "objectGUID", &guid_value, NULL);
539 if (ret != LDB_SUCCESS) {
540 talloc_free(down_req);
541 ldb_oom(module->ldb);
542 return LDB_ERR_OPERATIONS_ERROR;
544 ret = ldb_msg_add_string(msg, "whenChanged", time_str);
545 if (ret != LDB_SUCCESS) {
546 talloc_free(down_req);
547 ldb_oom(module->ldb);
548 return LDB_ERR_OPERATIONS_ERROR;
550 ret = samdb_msg_add_uint64(module->ldb, msg, msg, "uSNCreated", seq_num);
551 if (ret != LDB_SUCCESS) {
552 talloc_free(down_req);
553 ldb_oom(module->ldb);
554 return LDB_ERR_OPERATIONS_ERROR;
556 ret = samdb_msg_add_uint64(module->ldb, msg, msg, "uSNChanged", seq_num);
557 if (ret != LDB_SUCCESS) {
558 talloc_free(down_req);
559 ldb_oom(module->ldb);
560 return LDB_ERR_OPERATIONS_ERROR;
562 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
563 if (ret != LDB_SUCCESS) {
564 talloc_free(down_req);
565 ldb_oom(module->ldb);
566 return LDB_ERR_OPERATIONS_ERROR;
570 * sort the attributes by attid before storing the object
572 replmd_ldb_message_sort(msg, schema);
574 ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
576 /* go on with the call chain */
577 ret = ldb_next_request(module, down_req);
579 /* do not free down_req as the call results may be linked to it,
580 * it will be freed when the upper level request get freed */
581 if (ret == LDB_SUCCESS) {
582 req->handle = down_req->handle;
588 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
590 return replmd_prepare_originating(module, req, req->op.add.message->dn,
591 "replmd_add", replmd_add_originating);
594 static int replmd_modify_originating(struct ldb_module *module,
595 struct ldb_request *req,
596 const struct dsdb_schema *schema,
597 const struct dsdb_control_current_partition *partition)
599 struct ldb_request *down_req;
600 struct ldb_message *msg;
602 time_t t = time(NULL);
605 ldb_debug(module->ldb, LDB_DEBUG_TRACE, "replmd_modify_originating\n");
607 down_req = talloc(req, struct ldb_request);
608 if (down_req == NULL) {
609 return LDB_ERR_OPERATIONS_ERROR;
614 /* we have to copy the message as the caller might have it as a const */
615 down_req->op.mod.message = msg = ldb_msg_copy_shallow(down_req, req->op.mod.message);
617 talloc_free(down_req);
618 return LDB_ERR_OPERATIONS_ERROR;
621 if (add_time_element(msg, "whenChanged", t) != 0) {
622 talloc_free(down_req);
623 return LDB_ERR_OPERATIONS_ERROR;
626 /* Get a sequence number from the backend */
627 ret = ldb_sequence_number(module->ldb, LDB_SEQ_NEXT, &seq_num);
628 if (ret == LDB_SUCCESS) {
629 if (add_uint64_element(msg, "uSNChanged", seq_num) != 0) {
630 talloc_free(down_req);
631 return LDB_ERR_OPERATIONS_ERROR;
635 ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
637 /* go on with the call chain */
638 ret = ldb_next_request(module, down_req);
640 /* do not free down_req as the call results may be linked to it,
641 * it will be freed when the upper level request get freed */
642 if (ret == LDB_SUCCESS) {
643 req->handle = down_req->handle;
649 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
651 return replmd_prepare_originating(module, req, req->op.mod.message->dn,
652 "replmd_modify", replmd_modify_originating);
655 static int replmd_replicated_request_reply_helper(struct replmd_replicated_request *ar, int ret)
657 struct ldb_reply *ares = NULL;
659 ar->handle->status = ret;
660 ar->handle->state = LDB_ASYNC_DONE;
662 if (!ar->orig_req->callback) {
666 /* we're done and need to report the success to the caller */
667 ares = talloc_zero(ar, struct ldb_reply);
669 ar->handle->status = LDB_ERR_OPERATIONS_ERROR;
670 ar->handle->state = LDB_ASYNC_DONE;
671 return LDB_ERR_OPERATIONS_ERROR;
674 ares->type = LDB_REPLY_EXTENDED;
675 ares->response = NULL;
677 return ar->orig_req->callback(ar->module->ldb, ar->orig_req->context, ares);
680 static int replmd_replicated_request_done(struct replmd_replicated_request *ar)
682 return replmd_replicated_request_reply_helper(ar, LDB_SUCCESS);
685 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
687 return replmd_replicated_request_reply_helper(ar, ret);
690 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
692 int ret = LDB_ERR_OTHER;
693 /* TODO: do some error mapping */
694 return replmd_replicated_request_reply_helper(ar, ret);
697 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar);
699 static int replmd_replicated_apply_add_callback(struct ldb_context *ldb,
701 struct ldb_reply *ares)
703 #ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */
704 struct replmd_replicated_request *ar = talloc_get_type(private_data,
705 struct replmd_replicated_request);
707 ar->sub.change_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
708 if (ar->sub.change_ret != LDB_SUCCESS) {
709 return replmd_replicated_request_error(ar, ar->sub.change_ret);
712 talloc_free(ar->sub.mem_ctx);
713 ZERO_STRUCT(ar->sub);
717 return replmd_replicated_apply_next(ar);
723 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
725 enum ndr_err_code ndr_err;
726 struct ldb_message *msg;
727 struct replPropertyMetaDataBlob *md;
728 struct ldb_val md_value;
734 * TODO: check if the parent object exist
738 * TODO: handle the conflict case where an object with the
742 msg = ar->objs->objects[ar->index_current].msg;
743 md = ar->objs->objects[ar->index_current].meta_data;
745 ret = ldb_sequence_number(ar->module->ldb, LDB_SEQ_NEXT, &seq_num);
746 if (ret != LDB_SUCCESS) {
747 return replmd_replicated_request_error(ar, ret);
750 ret = ldb_msg_add_value(msg, "objectGUID", &ar->objs->objects[ar->index_current].guid_value, NULL);
751 if (ret != LDB_SUCCESS) {
752 return replmd_replicated_request_error(ar, ret);
755 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
756 if (ret != LDB_SUCCESS) {
757 return replmd_replicated_request_error(ar, ret);
760 ret = samdb_msg_add_uint64(ar->module->ldb, msg, msg, "uSNCreated", seq_num);
761 if (ret != LDB_SUCCESS) {
762 return replmd_replicated_request_error(ar, ret);
765 ret = samdb_msg_add_uint64(ar->module->ldb, msg, msg, "uSNChanged", seq_num);
766 if (ret != LDB_SUCCESS) {
767 return replmd_replicated_request_error(ar, ret);
771 * the meta data array is already sorted by the caller
773 for (i=0; i < md->ctr.ctr1.count; i++) {
774 md->ctr.ctr1.array[i].local_usn = seq_num;
776 ndr_err = ndr_push_struct_blob(&md_value, msg, md,
777 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
778 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
779 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
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)
868 enum ndr_err_code ndr_err;
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 ndr_err = ndr_pull_struct_blob(omd_value, ar->sub.mem_ctx, &omd,
906 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
907 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
908 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
909 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
912 if (omd.version != 1) {
913 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
919 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
920 nmd.ctr.ctr1.array = talloc_array(ar->sub.mem_ctx,
921 struct replPropertyMetaData1,
923 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOMEM);
925 /* first copy the old meta data */
926 for (i=0; i < omd.ctr.ctr1.count; i++) {
927 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
931 /* now merge in the new meta data */
932 for (i=0; i < rmd->ctr.ctr1.count; i++) {
935 rmd->ctr.ctr1.array[i].local_usn = seq_num;
937 for (j=0; j < ni; j++) {
940 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
944 cmp = replmd_replPropertyMetaData1_conflict_compare(&rmd->ctr.ctr1.array[i],
945 &nmd.ctr.ctr1.array[j]);
947 /* replace the entry */
948 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
953 /* we don't want to apply this change so remove the attribute */
954 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
963 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
968 * finally correct the size of the meta_data array
970 nmd.ctr.ctr1.count = ni;
973 * the rdn attribute (the alias for the name attribute),
974 * 'cn' for most objects is the last entry in the meta data array
977 * sort the new meta data array
980 struct replPropertyMetaData1 *rdn_p;
981 uint32_t rdn_idx = omd.ctr.ctr1.count - 1;
983 rdn_p = &nmd.ctr.ctr1.array[rdn_idx];
984 replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, &rdn_p->attid);
987 /* create the meta data value */
988 ndr_err = ndr_push_struct_blob(&nmd_value, msg, &nmd,
989 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
990 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
991 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
992 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
996 * check if some replicated attributes left, otherwise skip the ldb_modify() call
998 if (msg->num_elements == 0) {
999 ldb_debug(ar->module->ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
1004 ldb_debug(ar->module->ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
1005 ar->index_current, msg->num_elements);
1008 * when we now that we'll modify the record, add the whenChanged, uSNChanged
1009 * and replPopertyMetaData attributes
1011 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
1012 if (ret != LDB_SUCCESS) {
1013 return replmd_replicated_request_error(ar, ret);
1015 ret = samdb_msg_add_uint64(ar->module->ldb, msg, msg, "uSNChanged", seq_num);
1016 if (ret != LDB_SUCCESS) {
1017 return replmd_replicated_request_error(ar, ret);
1019 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
1020 if (ret != LDB_SUCCESS) {
1021 return replmd_replicated_request_error(ar, ret);
1024 replmd_ldb_message_sort(msg, ar->schema);
1026 /* we want to replace the old values */
1027 for (i=0; i < msg->num_elements; i++) {
1028 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
1031 ret = ldb_build_mod_req(&ar->sub.change_req,
1037 replmd_replicated_apply_merge_callback);
1038 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1040 #ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */
1041 return ldb_next_request(ar->module, ar->sub.change_req);
1043 ret = ldb_next_request(ar->module, ar->sub.change_req);
1044 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1046 ar->sub.change_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
1047 if (ar->sub.change_ret != LDB_SUCCESS) {
1048 return replmd_replicated_request_error(ar, ar->sub.change_ret);
1052 talloc_free(ar->sub.mem_ctx);
1053 ZERO_STRUCT(ar->sub);
1055 ar->index_current++;
1061 static int replmd_replicated_apply_search_callback(struct ldb_context *ldb,
1063 struct ldb_reply *ares)
1065 struct replmd_replicated_request *ar = talloc_get_type(private_data,
1066 struct replmd_replicated_request);
1067 bool is_done = false;
1069 switch (ares->type) {
1070 case LDB_REPLY_ENTRY:
1071 ar->sub.search_msg = talloc_steal(ar->sub.mem_ctx, ares->message);
1073 case LDB_REPLY_REFERRAL:
1074 /* we ignore referrals */
1076 case LDB_REPLY_EXTENDED:
1077 case LDB_REPLY_DONE:
1083 #ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */
1085 ar->sub.search_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
1086 if (ar->sub.search_ret != LDB_SUCCESS) {
1087 return replmd_replicated_request_error(ar, ar->sub.search_ret);
1089 if (ar->sub.search_msg) {
1090 return replmd_replicated_apply_merge(ar);
1092 return replmd_replicated_apply_add(ar);
1098 static int replmd_replicated_apply_search(struct replmd_replicated_request *ar)
1104 tmp_str = ldb_binary_encode(ar->sub.mem_ctx, ar->objs->objects[ar->index_current].guid_value);
1105 if (!tmp_str) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1107 filter = talloc_asprintf(ar->sub.mem_ctx, "(objectGUID=%s)", tmp_str);
1108 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1109 talloc_free(tmp_str);
1111 ret = ldb_build_search_req(&ar->sub.search_req,
1114 ar->objs->partition_dn,
1120 replmd_replicated_apply_search_callback);
1121 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1123 #ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */
1124 return ldb_next_request(ar->module, ar->sub.search_req);
1126 ret = ldb_next_request(ar->module, ar->sub.search_req);
1127 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1129 ar->sub.search_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
1130 if (ar->sub.search_ret != LDB_SUCCESS) {
1131 return replmd_replicated_request_error(ar, ar->sub.search_ret);
1133 if (ar->sub.search_msg) {
1134 return replmd_replicated_apply_merge(ar);
1137 return replmd_replicated_apply_add(ar);
1141 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
1143 #ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */
1144 if (ar->index_current >= ar->objs->num_objects) {
1145 return replmd_replicated_uptodate_vector(ar);
1149 ar->sub.mem_ctx = talloc_new(ar);
1150 if (!ar->sub.mem_ctx) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1152 return replmd_replicated_apply_search(ar);
1155 static int replmd_replicated_uptodate_modify_callback(struct ldb_context *ldb,
1157 struct ldb_reply *ares)
1159 #ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */
1160 struct replmd_replicated_request *ar = talloc_get_type(private_data,
1161 struct replmd_replicated_request);
1163 ar->sub.change_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
1164 if (ar->sub.change_ret != LDB_SUCCESS) {
1165 return replmd_replicated_request_error(ar, ar->sub.change_ret);
1168 talloc_free(ar->sub.mem_ctx);
1169 ZERO_STRUCT(ar->sub);
1171 return replmd_replicated_request_done(ar);
1177 static int replmd_drsuapi_DsReplicaCursor2_compare(const struct drsuapi_DsReplicaCursor2 *c1,
1178 const struct drsuapi_DsReplicaCursor2 *c2)
1180 return GUID_compare(&c1->source_dsa_invocation_id, &c2->source_dsa_invocation_id);
1183 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
1185 enum ndr_err_code ndr_err;
1186 struct ldb_message *msg;
1187 struct replUpToDateVectorBlob ouv;
1188 const struct ldb_val *ouv_value;
1189 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
1190 struct replUpToDateVectorBlob nuv;
1191 struct ldb_val nuv_value;
1192 struct ldb_message_element *nuv_el = NULL;
1193 const struct GUID *our_invocation_id;
1194 struct ldb_message_element *orf_el = NULL;
1195 struct repsFromToBlob nrf;
1196 struct ldb_val *nrf_value = NULL;
1197 struct ldb_message_element *nrf_el = NULL;
1201 time_t t = time(NULL);
1205 ruv = ar->objs->uptodateness_vector;
1211 unix_to_nt_time(&now, t);
1214 * we use the next sequence number for our own highest_usn
1215 * because we will do a modify request and this will increment
1218 ret = ldb_sequence_number(ar->module->ldb, LDB_SEQ_NEXT, &seq_num);
1219 if (ret != LDB_SUCCESS) {
1220 return replmd_replicated_request_error(ar, ret);
1224 * first create the new replUpToDateVector
1226 ouv_value = ldb_msg_find_ldb_val(ar->sub.search_msg, "replUpToDateVector");
1228 ndr_err = ndr_pull_struct_blob(ouv_value, ar->sub.mem_ctx, &ouv,
1229 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
1230 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1231 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
1232 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
1235 if (ouv.version != 2) {
1236 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
1241 * the new uptodateness vector will at least
1242 * contain 1 entry, one for the source_dsa
1244 * plus optional values from our old vector and the one from the source_dsa
1246 nuv.ctr.ctr2.count = 1 + ouv.ctr.ctr2.count;
1247 if (ruv) nuv.ctr.ctr2.count += ruv->count;
1248 nuv.ctr.ctr2.cursors = talloc_array(ar->sub.mem_ctx,
1249 struct drsuapi_DsReplicaCursor2,
1250 nuv.ctr.ctr2.count);
1251 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1253 /* first copy the old vector */
1254 for (i=0; i < ouv.ctr.ctr2.count; i++) {
1255 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
1259 /* get our invocation_id if we have one already attached to the ldb */
1260 our_invocation_id = samdb_ntds_invocation_id(ar->module->ldb);
1262 /* merge in the source_dsa vector is available */
1263 for (i=0; (ruv && i < ruv->count); i++) {
1266 if (our_invocation_id &&
1267 GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
1268 our_invocation_id)) {
1272 for (j=0; j < ni; j++) {
1273 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
1274 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
1281 * we update only the highest_usn and not the latest_sync_success time,
1282 * because the last success stands for direct replication
1284 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
1285 nuv.ctr.ctr2.cursors[j].highest_usn = ruv->cursors[i].highest_usn;
1290 if (found) continue;
1292 /* if it's not there yet, add it */
1293 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
1298 * merge in the current highwatermark for the source_dsa
1301 for (j=0; j < ni; j++) {
1302 if (!GUID_equal(&ar->objs->source_dsa->source_dsa_invocation_id,
1303 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
1310 * here we update the highest_usn and last_sync_success time
1311 * because we're directly replicating from the source_dsa
1313 * and use the tmp_highest_usn because this is what we have just applied
1316 nuv.ctr.ctr2.cursors[j].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
1317 nuv.ctr.ctr2.cursors[j].last_sync_success = now;
1322 * here we update the highest_usn and last_sync_success time
1323 * because we're directly replicating from the source_dsa
1325 * and use the tmp_highest_usn because this is what we have just applied
1328 nuv.ctr.ctr2.cursors[ni].source_dsa_invocation_id= ar->objs->source_dsa->source_dsa_invocation_id;
1329 nuv.ctr.ctr2.cursors[ni].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
1330 nuv.ctr.ctr2.cursors[ni].last_sync_success = now;
1335 * finally correct the size of the cursors array
1337 nuv.ctr.ctr2.count = ni;
1342 qsort(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count,
1343 sizeof(struct drsuapi_DsReplicaCursor2),
1344 (comparison_fn_t)replmd_drsuapi_DsReplicaCursor2_compare);
1347 * create the change ldb_message
1349 msg = ldb_msg_new(ar->sub.mem_ctx);
1350 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1351 msg->dn = ar->sub.search_msg->dn;
1353 ndr_err = ndr_push_struct_blob(&nuv_value, msg, &nuv,
1354 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
1355 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1356 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
1357 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
1359 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
1360 if (ret != LDB_SUCCESS) {
1361 return replmd_replicated_request_error(ar, ret);
1363 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
1366 * now create the new repsFrom value from the given repsFromTo1 structure
1370 nrf.ctr.ctr1 = *ar->objs->source_dsa;
1371 /* and fix some values... */
1372 nrf.ctr.ctr1.consecutive_sync_failures = 0;
1373 nrf.ctr.ctr1.last_success = now;
1374 nrf.ctr.ctr1.last_attempt = now;
1375 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
1376 nrf.ctr.ctr1.highwatermark.highest_usn = nrf.ctr.ctr1.highwatermark.tmp_highest_usn;
1379 * first see if we already have a repsFrom value for the current source dsa
1380 * if so we'll later replace this value
1382 orf_el = ldb_msg_find_element(ar->sub.search_msg, "repsFrom");
1384 for (i=0; i < orf_el->num_values; i++) {
1385 struct repsFromToBlob *trf;
1387 trf = talloc(ar->sub.mem_ctx, struct repsFromToBlob);
1388 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1390 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
1391 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
1392 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1393 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
1394 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
1397 if (trf->version != 1) {
1398 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
1402 * we compare the source dsa objectGUID not the invocation_id
1403 * because we want only one repsFrom value per source dsa
1404 * and when the invocation_id of the source dsa has changed we don't need
1405 * the old repsFrom with the old invocation_id
1407 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
1408 &ar->objs->source_dsa->source_dsa_obj_guid)) {
1414 nrf_value = &orf_el->values[i];
1419 * copy over all old values to the new ldb_message
1421 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
1422 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1427 * if we haven't found an old repsFrom value for the current source dsa
1428 * we'll add a new value
1431 struct ldb_val zero_value;
1432 ZERO_STRUCT(zero_value);
1433 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
1434 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1436 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
1439 /* we now fill the value which is already attached to ldb_message */
1440 ndr_err = ndr_push_struct_blob(nrf_value, msg, &nrf,
1441 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
1442 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1443 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
1444 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
1448 * the ldb_message_element for the attribute, has all the old values and the new one
1449 * so we'll replace the whole attribute with all values
1451 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
1453 /* prepare the ldb_modify() request */
1454 ret = ldb_build_mod_req(&ar->sub.change_req,
1460 replmd_replicated_uptodate_modify_callback);
1461 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1463 #ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */
1464 return ldb_next_request(ar->module, ar->sub.change_req);
1466 ret = ldb_next_request(ar->module, ar->sub.change_req);
1467 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1469 ar->sub.change_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
1470 if (ar->sub.change_ret != LDB_SUCCESS) {
1471 return replmd_replicated_request_error(ar, ar->sub.change_ret);
1474 talloc_free(ar->sub.mem_ctx);
1475 ZERO_STRUCT(ar->sub);
1477 return replmd_replicated_request_done(ar);
1481 static int replmd_replicated_uptodate_search_callback(struct ldb_context *ldb,
1483 struct ldb_reply *ares)
1485 struct replmd_replicated_request *ar = talloc_get_type(private_data,
1486 struct replmd_replicated_request);
1487 bool is_done = false;
1489 switch (ares->type) {
1490 case LDB_REPLY_ENTRY:
1491 ar->sub.search_msg = talloc_steal(ar->sub.mem_ctx, ares->message);
1493 case LDB_REPLY_REFERRAL:
1494 /* we ignore referrals */
1496 case LDB_REPLY_EXTENDED:
1497 case LDB_REPLY_DONE:
1503 #ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */
1505 ar->sub.search_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
1506 if (ar->sub.search_ret != LDB_SUCCESS) {
1507 return replmd_replicated_request_error(ar, ar->sub.search_ret);
1509 if (!ar->sub.search_msg) {
1510 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
1513 return replmd_replicated_uptodate_modify(ar);
1519 static int replmd_replicated_uptodate_search(struct replmd_replicated_request *ar)
1522 const char *attrs[] = {
1523 "replUpToDateVector",
1528 ret = ldb_build_search_req(&ar->sub.search_req,
1531 ar->objs->partition_dn,
1537 replmd_replicated_uptodate_search_callback);
1538 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1540 #ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */
1541 return ldb_next_request(ar->module, ar->sub.search_req);
1543 ret = ldb_next_request(ar->module, ar->sub.search_req);
1544 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1546 ar->sub.search_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
1547 if (ar->sub.search_ret != LDB_SUCCESS) {
1548 return replmd_replicated_request_error(ar, ar->sub.search_ret);
1550 if (!ar->sub.search_msg) {
1551 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
1554 return replmd_replicated_uptodate_modify(ar);
1558 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
1560 ar->sub.mem_ctx = talloc_new(ar);
1561 if (!ar->sub.mem_ctx) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1563 return replmd_replicated_uptodate_search(ar);
1566 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
1568 struct dsdb_extended_replicated_objects *objs;
1569 struct replmd_replicated_request *ar;
1571 ldb_debug(module->ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
1573 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
1575 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
1576 return LDB_ERR_PROTOCOL_ERROR;
1579 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
1580 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
1581 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
1582 return LDB_ERR_PROTOCOL_ERROR;
1585 ar = replmd_replicated_init_handle(module, req, objs);
1587 return LDB_ERR_OPERATIONS_ERROR;
1590 #ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */
1591 return replmd_replicated_apply_next(ar);
1593 while (ar->index_current < ar->objs->num_objects &&
1594 req->handle->state != LDB_ASYNC_DONE) {
1595 replmd_replicated_apply_next(ar);
1598 if (req->handle->state != LDB_ASYNC_DONE) {
1599 replmd_replicated_uptodate_vector(ar);
1606 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
1608 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
1609 return replmd_extended_replicated_objects(module, req);
1612 return ldb_next_request(module, req);
1615 static int replmd_wait_none(struct ldb_handle *handle) {
1616 struct replmd_replicated_request *ar;
1618 if (!handle || !handle->private_data) {
1619 return LDB_ERR_OPERATIONS_ERROR;
1622 ar = talloc_get_type(handle->private_data, struct replmd_replicated_request);
1624 return LDB_ERR_OPERATIONS_ERROR;
1627 /* we do only sync calls */
1628 if (handle->state != LDB_ASYNC_DONE) {
1629 return LDB_ERR_OPERATIONS_ERROR;
1632 return handle->status;
1635 static int replmd_wait_all(struct ldb_handle *handle) {
1639 while (handle->state != LDB_ASYNC_DONE) {
1640 ret = replmd_wait_none(handle);
1641 if (ret != LDB_SUCCESS) {
1646 return handle->status;
1649 static int replmd_wait(struct ldb_handle *handle, enum ldb_wait_type type)
1651 if (type == LDB_WAIT_ALL) {
1652 return replmd_wait_all(handle);
1654 return replmd_wait_none(handle);
1658 static const struct ldb_module_ops replmd_ops = {
1659 .name = "repl_meta_data",
1661 .modify = replmd_modify,
1662 .extended = replmd_extended,
1666 int repl_meta_data_module_init(void)
1668 return ldb_register_module(&replmd_ops);