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, write to the Free Software
25 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
31 * Component: ldb repl_meta_data module
33 * Description: - add a unique objectGUID onto every new record,
34 * - handle whenCreated, whenChanged timestamps
35 * - handle uSNCreated, uSNChanged numbers
36 * - handle replPropertyMetaData attribute
39 * Author: Stefan Metzmacher
43 #include "lib/ldb/include/ldb.h"
44 #include "lib/ldb/include/ldb_errors.h"
45 #include "lib/ldb/include/ldb_private.h"
46 #include "dsdb/samdb/samdb.h"
47 #include "dsdb/common/flags.h"
48 #include "librpc/gen_ndr/ndr_misc.h"
49 #include "librpc/gen_ndr/ndr_drsuapi.h"
50 #include "librpc/gen_ndr/ndr_drsblobs.h"
52 struct replmd_replicated_request {
53 struct ldb_module *module;
54 struct ldb_handle *handle;
55 struct ldb_request *orig_req;
57 const struct dsdb_schema *schema;
59 struct dsdb_extended_replicated_objects *objs;
61 uint32_t index_current;
65 struct ldb_request *search_req;
66 struct ldb_message *search_msg;
68 struct ldb_request *change_req;
73 static struct replmd_replicated_request *replmd_replicated_init_handle(struct ldb_module *module,
74 struct ldb_request *req,
75 struct dsdb_extended_replicated_objects *objs)
77 struct replmd_replicated_request *ar;
79 const struct dsdb_schema *schema;
81 schema = dsdb_get_schema(module->ldb);
83 ldb_debug_set(module->ldb, LDB_DEBUG_FATAL,
84 "replmd_replicated_init_handle: no loaded schema found\n");
88 h = talloc_zero(req, struct ldb_handle);
90 ldb_set_errstring(module->ldb, "Out of Memory");
95 h->state = LDB_ASYNC_PENDING;
96 h->status = LDB_SUCCESS;
98 ar = talloc_zero(h, struct replmd_replicated_request);
100 ldb_set_errstring(module->ldb, "Out of Memory");
105 h->private_data = ar;
119 add a time element to a record
121 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
123 struct ldb_message_element *el;
126 if (ldb_msg_find_element(msg, attr) != NULL) {
130 s = ldb_timestring(msg, t);
135 if (ldb_msg_add_string(msg, attr, s) != 0) {
139 el = ldb_msg_find_element(msg, attr);
140 /* always set as replace. This works because on add ops, the flag
142 el->flags = LDB_FLAG_MOD_REPLACE;
148 add a uint64_t element to a record
150 static int add_uint64_element(struct ldb_message *msg, const char *attr, uint64_t v)
152 struct ldb_message_element *el;
154 if (ldb_msg_find_element(msg, attr) != NULL) {
158 if (ldb_msg_add_fmt(msg, attr, "%llu", (unsigned long long)v) != 0) {
162 el = ldb_msg_find_element(msg, attr);
163 /* always set as replace. This works because on add ops, the flag
165 el->flags = LDB_FLAG_MOD_REPLACE;
170 static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1 *m1,
171 const struct replPropertyMetaData1 *m2,
172 const uint32_t *rdn_attid)
174 if (m1->attid == m2->attid) {
179 * the rdn attribute should be at the end!
180 * so we need to return a value greater than zero
181 * which means m1 is greater than m2
183 if (m1->attid == *rdn_attid) {
188 * the rdn attribute should be at the end!
189 * so we need to return a value less than zero
190 * which means m2 is greater than m1
192 if (m2->attid == *rdn_attid) {
196 return m1->attid - m2->attid;
199 static void replmd_replPropertyMetaDataCtr1_sort(struct replPropertyMetaDataCtr1 *ctr1,
200 const uint32_t *rdn_attid)
202 ldb_qsort(ctr1->array, ctr1->count, sizeof(struct replPropertyMetaData1),
203 discard_const_p(void, rdn_attid), (ldb_qsort_cmp_fn_t)replmd_replPropertyMetaData1_attid_sort);
206 static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1,
207 const struct ldb_message_element *e2,
208 const struct dsdb_schema *schema)
210 const struct dsdb_attribute *a1;
211 const struct dsdb_attribute *a2;
214 * TODO: make this faster by caching the dsdb_attribute pointer
215 * on the ldb_messag_element
218 a1 = dsdb_attribute_by_lDAPDisplayName(schema, e1->name);
219 a2 = dsdb_attribute_by_lDAPDisplayName(schema, e2->name);
222 * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
226 return strcasecmp(e1->name, e2->name);
229 return a1->attributeID_id - a2->attributeID_id;
232 static void replmd_ldb_message_sort(struct ldb_message *msg,
233 const struct dsdb_schema *schema)
235 ldb_qsort(msg->elements, msg->num_elements, sizeof(struct ldb_message_element),
236 discard_const_p(void, schema), (ldb_qsort_cmp_fn_t)replmd_ldb_message_element_attid_sort);
239 static int replmd_prepare_originating(struct ldb_module *module, struct ldb_request *req,
240 struct ldb_dn *dn, const char *fn_name,
241 int (*fn)(struct ldb_module *,
242 struct ldb_request *,
243 const struct dsdb_schema *,
244 const struct dsdb_control_current_partition *))
246 const struct dsdb_schema *schema;
247 const struct ldb_control *partition_ctrl;
248 const struct dsdb_control_current_partition *partition;
250 /* do not manipulate our control entries */
251 if (ldb_dn_is_special(dn)) {
252 return ldb_next_request(module, req);
255 schema = dsdb_get_schema(module->ldb);
257 ldb_debug_set(module->ldb, LDB_DEBUG_FATAL,
258 "%s: no dsdb_schema loaded",
260 return LDB_ERR_CONSTRAINT_VIOLATION;
263 partition_ctrl = ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID);
264 if (!partition_ctrl) {
265 ldb_debug_set(module->ldb, LDB_DEBUG_FATAL,
266 "%s: no current partition control found",
268 return LDB_ERR_CONSTRAINT_VIOLATION;
271 partition = talloc_get_type(partition_ctrl->data,
272 struct dsdb_control_current_partition);
274 ldb_debug_set(module->ldb, LDB_DEBUG_FATAL,
275 "%s: current partition control contains invalid data",
277 return LDB_ERR_CONSTRAINT_VIOLATION;
280 if (partition->version != DSDB_CONTROL_CURRENT_PARTITION_VERSION) {
281 ldb_debug_set(module->ldb, LDB_DEBUG_FATAL,
282 "%s: current partition control contains invalid version [%u != %u]\n",
283 fn_name, partition->version, DSDB_CONTROL_CURRENT_PARTITION_VERSION);
284 return LDB_ERR_CONSTRAINT_VIOLATION;
287 return fn(module, req, schema, partition);
290 static int replmd_add_originating(struct ldb_module *module,
291 struct ldb_request *req,
292 const struct dsdb_schema *schema,
293 const struct dsdb_control_current_partition *partition)
296 struct ldb_request *down_req;
297 struct ldb_message *msg;
298 uint32_t instance_type;
299 struct ldb_dn *new_dn;
300 const char *rdn_name;
301 const char *rdn_name_upper;
302 const struct ldb_val *rdn_value = NULL;
303 const struct dsdb_attribute *rdn_attr = NULL;
305 struct ldb_val guid_value;
306 struct replPropertyMetaDataBlob nmd;
307 struct ldb_val nmd_value;
309 const struct GUID *our_invocation_id;
310 time_t t = time(NULL);
316 ldb_debug(module->ldb, LDB_DEBUG_TRACE, "replmd_add_originating\n");
318 if (ldb_msg_find_element(req->op.add.message, "objectGUID")) {
319 ldb_debug_set(module->ldb, LDB_DEBUG_ERROR,
320 "replmd_add_originating: it's not allowed to add an object with objectGUID\n");
321 return LDB_ERR_UNWILLING_TO_PERFORM;
324 if (ldb_msg_find_element(req->op.add.message, "instanceType")) {
325 ldb_debug_set(module->ldb, LDB_DEBUG_ERROR,
326 "replmd_add_originating: it's not allowed to add an object with instanceType\n");
327 return LDB_ERR_UNWILLING_TO_PERFORM;
330 /* Get a sequence number from the backend */
331 ret = ldb_sequence_number(module->ldb, LDB_SEQ_NEXT, &seq_num);
332 if (ret != LDB_SUCCESS) {
337 guid = GUID_random();
339 /* get our invicationId */
340 our_invocation_id = samdb_ntds_invocation_id(module->ldb);
341 if (!our_invocation_id) {
342 ldb_debug_set(module->ldb, LDB_DEBUG_ERROR,
343 "replmd_add_originating: unable to find invocationId\n");
344 return LDB_ERR_OPERATIONS_ERROR;
347 /* create a copy of the request */
348 down_req = talloc(req, struct ldb_request);
349 if (down_req == NULL) {
350 ldb_oom(module->ldb);
351 return LDB_ERR_OPERATIONS_ERROR;
355 /* we have to copy the message as the caller might have it as a const */
356 down_req->op.add.message = msg = ldb_msg_copy_shallow(down_req, req->op.add.message);
358 talloc_free(down_req);
359 ldb_oom(module->ldb);
360 return LDB_ERR_OPERATIONS_ERROR;
363 /* generated times */
364 unix_to_nt_time(&now, t);
365 time_str = ldb_timestring(msg, t);
367 talloc_free(down_req);
368 return LDB_ERR_OPERATIONS_ERROR;
372 * get details of the rdn name
374 rdn_name = ldb_dn_get_rdn_name(msg->dn);
376 talloc_free(down_req);
377 ldb_oom(module->ldb);
378 return LDB_ERR_OPERATIONS_ERROR;
380 rdn_attr = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
382 talloc_free(down_req);
383 return LDB_ERR_OPERATIONS_ERROR;
385 rdn_value = ldb_dn_get_rdn_val(msg->dn);
387 talloc_free(down_req);
388 ldb_oom(module->ldb);
389 return LDB_ERR_OPERATIONS_ERROR;
393 * remove autogenerated attributes
395 ldb_msg_remove_attr(msg, rdn_name);
396 ldb_msg_remove_attr(msg, "name");
397 ldb_msg_remove_attr(msg, "whenCreated");
398 ldb_msg_remove_attr(msg, "whenChanged");
399 ldb_msg_remove_attr(msg, "uSNCreated");
400 ldb_msg_remove_attr(msg, "uSNChanged");
401 ldb_msg_remove_attr(msg, "replPropertyMetaData");
404 * TODO: construct a new DN out of:
406 * - the upper case of rdn_attr->LDAPDisplayName
409 new_dn = ldb_dn_copy(msg, msg->dn);
411 talloc_free(down_req);
412 ldb_oom(module->ldb);
413 return LDB_ERR_OPERATIONS_ERROR;
415 rdn_name_upper = strupper_talloc(msg, rdn_attr->lDAPDisplayName);
416 if (!rdn_name_upper) {
417 talloc_free(down_req);
418 ldb_oom(module->ldb);
419 return LDB_ERR_OPERATIONS_ERROR;
421 ret = ldb_dn_set_component(new_dn, 0, rdn_name_upper, *rdn_value);
422 if (ret != LDB_SUCCESS) {
423 talloc_free(down_req);
424 ldb_oom(module->ldb);
425 return LDB_ERR_OPERATIONS_ERROR;
430 * TODO: calculate correct instance type
432 instance_type = INSTANCE_TYPE_WRITE;
433 if (ldb_dn_compare(partition->dn, msg->dn) == 0) {
434 instance_type |= INSTANCE_TYPE_IS_NC_HEAD;
435 if (ldb_dn_compare(msg->dn, samdb_base_dn(module->ldb)) != 0) {
436 instance_type |= INSTANCE_TYPE_NC_ABOVE;
441 * readd replicated attributes
443 ret = ldb_msg_add_value(msg, rdn_attr->lDAPDisplayName, rdn_value, NULL);
444 if (ret != LDB_SUCCESS) {
445 talloc_free(down_req);
446 ldb_oom(module->ldb);
447 return LDB_ERR_OPERATIONS_ERROR;
449 ret = ldb_msg_add_value(msg, "name", rdn_value, NULL);
450 if (ret != LDB_SUCCESS) {
451 talloc_free(down_req);
452 ldb_oom(module->ldb);
453 return LDB_ERR_OPERATIONS_ERROR;
455 ret = ldb_msg_add_string(msg, "whenCreated", time_str);
456 if (ret != LDB_SUCCESS) {
457 talloc_free(down_req);
458 ldb_oom(module->ldb);
459 return LDB_ERR_OPERATIONS_ERROR;
461 ret = ldb_msg_add_fmt(msg, "instanceType", "%u", instance_type);
462 if (ret != LDB_SUCCESS) {
463 talloc_free(down_req);
464 ldb_oom(module->ldb);
465 return LDB_ERR_OPERATIONS_ERROR;
468 /* build the replication meta_data */
471 nmd.ctr.ctr1.count = msg->num_elements;
472 nmd.ctr.ctr1.array = talloc_array(msg,
473 struct replPropertyMetaData1,
475 if (!nmd.ctr.ctr1.array) {
476 talloc_free(down_req);
477 ldb_oom(module->ldb);
478 return LDB_ERR_OPERATIONS_ERROR;
481 for (i=0; i < msg->num_elements; i++) {
482 struct ldb_message_element *e = &msg->elements[i];
483 struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
484 const struct dsdb_attribute *sa;
486 if (e->name[0] == '@') continue;
488 sa = dsdb_attribute_by_lDAPDisplayName(schema, e->name);
490 ldb_debug_set(module->ldb, LDB_DEBUG_ERROR,
491 "replmd_add_originating: attribute '%s' not defined in schema\n",
493 talloc_free(down_req);
494 return LDB_ERR_NO_SUCH_ATTRIBUTE;
497 if ((sa->systemFlags & 0x00000001) || (sa->systemFlags & 0x00000004)) {
498 /* if the attribute is not replicated (0x00000001)
499 * or constructed (0x00000004) it has no metadata
504 m->attid = sa->attributeID_id;
506 m->originating_change_time = now;
507 m->originating_invocation_id = *our_invocation_id;
508 m->originating_usn = seq_num;
509 m->local_usn = seq_num;
513 /* fix meta data count */
514 nmd.ctr.ctr1.count = ni;
517 * sort meta data array, and move the rdn attribute entry to the end
519 replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, &rdn_attr->attributeID_id);
521 /* generated NDR encoded values */
522 nt_status = ndr_push_struct_blob(&guid_value, msg, &guid,
523 (ndr_push_flags_fn_t)ndr_push_GUID);
524 if (!NT_STATUS_IS_OK(nt_status)) {
525 talloc_free(down_req);
526 ldb_oom(module->ldb);
527 return LDB_ERR_OPERATIONS_ERROR;
529 nt_status = ndr_push_struct_blob(&nmd_value, msg, &nmd,
530 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
531 if (!NT_STATUS_IS_OK(nt_status)) {
532 talloc_free(down_req);
533 ldb_oom(module->ldb);
534 return LDB_ERR_OPERATIONS_ERROR;
538 * add the autogenerated values
540 ret = ldb_msg_add_value(msg, "objectGUID", &guid_value, NULL);
541 if (ret != LDB_SUCCESS) {
542 talloc_free(down_req);
543 ldb_oom(module->ldb);
544 return LDB_ERR_OPERATIONS_ERROR;
546 ret = ldb_msg_add_string(msg, "whenChanged", time_str);
547 if (ret != LDB_SUCCESS) {
548 talloc_free(down_req);
549 ldb_oom(module->ldb);
550 return LDB_ERR_OPERATIONS_ERROR;
552 ret = samdb_msg_add_uint64(module->ldb, msg, msg, "uSNCreated", seq_num);
553 if (ret != LDB_SUCCESS) {
554 talloc_free(down_req);
555 ldb_oom(module->ldb);
556 return LDB_ERR_OPERATIONS_ERROR;
558 ret = samdb_msg_add_uint64(module->ldb, msg, msg, "uSNChanged", seq_num);
559 if (ret != LDB_SUCCESS) {
560 talloc_free(down_req);
561 ldb_oom(module->ldb);
562 return LDB_ERR_OPERATIONS_ERROR;
564 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
565 if (ret != LDB_SUCCESS) {
566 talloc_free(down_req);
567 ldb_oom(module->ldb);
568 return LDB_ERR_OPERATIONS_ERROR;
572 * sort the attributes by attid before storing the object
574 replmd_ldb_message_sort(msg, schema);
576 ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
578 /* go on with the call chain */
579 ret = ldb_next_request(module, down_req);
581 /* do not free down_req as the call results may be linked to it,
582 * it will be freed when the upper level request get freed */
583 if (ret == LDB_SUCCESS) {
584 req->handle = down_req->handle;
590 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
592 return replmd_prepare_originating(module, req, req->op.add.message->dn,
593 "replmd_add", replmd_add_originating);
596 static int replmd_modify_originating(struct ldb_module *module,
597 struct ldb_request *req,
598 const struct dsdb_schema *schema,
599 const struct dsdb_control_current_partition *partition)
601 struct ldb_request *down_req;
602 struct ldb_message *msg;
604 time_t t = time(NULL);
607 ldb_debug(module->ldb, LDB_DEBUG_TRACE, "replmd_modify_originating\n");
609 down_req = talloc(req, struct ldb_request);
610 if (down_req == NULL) {
611 return LDB_ERR_OPERATIONS_ERROR;
616 /* we have to copy the message as the caller might have it as a const */
617 down_req->op.mod.message = msg = ldb_msg_copy_shallow(down_req, req->op.mod.message);
619 talloc_free(down_req);
620 return LDB_ERR_OPERATIONS_ERROR;
623 if (add_time_element(msg, "whenChanged", t) != 0) {
624 talloc_free(down_req);
625 return LDB_ERR_OPERATIONS_ERROR;
628 /* Get a sequence number from the backend */
629 ret = ldb_sequence_number(module->ldb, LDB_SEQ_NEXT, &seq_num);
630 if (ret == LDB_SUCCESS) {
631 if (add_uint64_element(msg, "uSNChanged", seq_num) != 0) {
632 talloc_free(down_req);
633 return LDB_ERR_OPERATIONS_ERROR;
637 ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
639 /* go on with the call chain */
640 ret = ldb_next_request(module, down_req);
642 /* do not free down_req as the call results may be linked to it,
643 * it will be freed when the upper level request get freed */
644 if (ret == LDB_SUCCESS) {
645 req->handle = down_req->handle;
651 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
653 return replmd_prepare_originating(module, req, req->op.mod.message->dn,
654 "replmd_modify", replmd_modify_originating);
657 static int replmd_replicated_request_reply_helper(struct replmd_replicated_request *ar, int ret)
659 struct ldb_reply *ares = NULL;
661 ar->handle->status = ret;
662 ar->handle->state = LDB_ASYNC_DONE;
664 if (!ar->orig_req->callback) {
668 /* we're done and need to report the success to the caller */
669 ares = talloc_zero(ar, struct ldb_reply);
671 ar->handle->status = LDB_ERR_OPERATIONS_ERROR;
672 ar->handle->state = LDB_ASYNC_DONE;
673 return LDB_ERR_OPERATIONS_ERROR;
676 ares->type = LDB_REPLY_EXTENDED;
677 ares->response = NULL;
679 return ar->orig_req->callback(ar->module->ldb, ar->orig_req->context, ares);
682 static int replmd_replicated_request_done(struct replmd_replicated_request *ar)
684 return replmd_replicated_request_reply_helper(ar, LDB_SUCCESS);
687 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
689 return replmd_replicated_request_reply_helper(ar, ret);
692 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
694 int ret = LDB_ERR_OTHER;
695 /* TODO: do some error mapping */
696 return replmd_replicated_request_reply_helper(ar, ret);
699 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar);
701 static int replmd_replicated_apply_add_callback(struct ldb_context *ldb,
703 struct ldb_reply *ares)
705 #ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */
706 struct replmd_replicated_request *ar = talloc_get_type(private_data,
707 struct replmd_replicated_request);
709 ar->sub.change_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
710 if (ar->sub.change_ret != LDB_SUCCESS) {
711 return replmd_replicated_request_error(ar, ar->sub.change_ret);
714 talloc_free(ar->sub.mem_ctx);
715 ZERO_STRUCT(ar->sub);
719 return replmd_replicated_apply_next(ar);
725 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
728 struct ldb_message *msg;
729 struct replPropertyMetaDataBlob *md;
730 struct ldb_val md_value;
736 * TODO: check if the parent object exist
740 * TODO: handle the conflict case where an object with the
744 msg = ar->objs->objects[ar->index_current].msg;
745 md = ar->objs->objects[ar->index_current].meta_data;
747 ret = ldb_sequence_number(ar->module->ldb, LDB_SEQ_NEXT, &seq_num);
748 if (ret != LDB_SUCCESS) {
749 return replmd_replicated_request_error(ar, ret);
752 ret = ldb_msg_add_value(msg, "objectGUID", &ar->objs->objects[ar->index_current].guid_value, NULL);
753 if (ret != LDB_SUCCESS) {
754 return replmd_replicated_request_error(ar, ret);
757 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
758 if (ret != LDB_SUCCESS) {
759 return replmd_replicated_request_error(ar, ret);
762 ret = samdb_msg_add_uint64(ar->module->ldb, msg, msg, "uSNCreated", seq_num);
763 if (ret != LDB_SUCCESS) {
764 return replmd_replicated_request_error(ar, ret);
767 ret = samdb_msg_add_uint64(ar->module->ldb, msg, msg, "uSNChanged", seq_num);
768 if (ret != LDB_SUCCESS) {
769 return replmd_replicated_request_error(ar, ret);
773 * the meta data array is already sorted by the caller
775 for (i=0; i < md->ctr.ctr1.count; i++) {
776 md->ctr.ctr1.array[i].local_usn = seq_num;
778 nt_status = ndr_push_struct_blob(&md_value, msg, md,
779 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
780 if (!NT_STATUS_IS_OK(nt_status)) {
781 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
783 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
784 if (ret != LDB_SUCCESS) {
785 return replmd_replicated_request_error(ar, ret);
788 replmd_ldb_message_sort(msg, ar->schema);
790 ret = ldb_build_add_req(&ar->sub.change_req,
796 replmd_replicated_apply_add_callback);
797 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
799 #ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */
800 return ldb_next_request(ar->module, ar->sub.change_req);
802 ret = ldb_next_request(ar->module, ar->sub.change_req);
803 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
805 ar->sub.change_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
806 if (ar->sub.change_ret != LDB_SUCCESS) {
807 return replmd_replicated_request_error(ar, ar->sub.change_ret);
810 talloc_free(ar->sub.mem_ctx);
811 ZERO_STRUCT(ar->sub);
819 static int replmd_replPropertyMetaData1_conflict_compare(struct replPropertyMetaData1 *m1,
820 struct replPropertyMetaData1 *m2)
824 if (m1->version != m2->version) {
825 return m1->version - m2->version;
828 if (m1->originating_change_time != m2->originating_change_time) {
829 return m1->originating_change_time - m2->originating_change_time;
832 ret = GUID_compare(&m1->originating_invocation_id, &m2->originating_invocation_id);
837 return m1->originating_usn - m2->originating_usn;
840 static int replmd_replicated_apply_merge_callback(struct ldb_context *ldb,
842 struct ldb_reply *ares)
844 #ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */
845 struct replmd_replicated_request *ar = talloc_get_type(private_data,
846 struct replmd_replicated_request);
848 ret = ldb_next_request(ar->module, ar->sub.change_req);
849 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
851 ar->sub.change_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
852 if (ar->sub.change_ret != LDB_SUCCESS) {
853 return replmd_replicated_request_error(ar, ar->sub.change_ret);
856 talloc_free(ar->sub.mem_ctx);
857 ZERO_STRUCT(ar->sub);
867 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
870 struct ldb_message *msg;
871 struct replPropertyMetaDataBlob *rmd;
872 struct replPropertyMetaDataBlob omd;
873 const struct ldb_val *omd_value;
874 struct replPropertyMetaDataBlob nmd;
875 struct ldb_val nmd_value;
877 uint32_t removed_attrs = 0;
881 msg = ar->objs->objects[ar->index_current].msg;
882 rmd = ar->objs->objects[ar->index_current].meta_data;
887 * TODO: add rename conflict handling
889 if (ldb_dn_compare(msg->dn, ar->sub.search_msg->dn) != 0) {
890 ldb_debug_set(ar->module->ldb, LDB_DEBUG_FATAL, "replmd_replicated_apply_merge[%u]: rename not supported",
892 ldb_debug(ar->module->ldb, LDB_DEBUG_FATAL, "%s => %s\n",
893 ldb_dn_get_linearized(ar->sub.search_msg->dn),
894 ldb_dn_get_linearized(msg->dn));
895 return replmd_replicated_request_werror(ar, WERR_NOT_SUPPORTED);
898 ret = ldb_sequence_number(ar->module->ldb, LDB_SEQ_NEXT, &seq_num);
899 if (ret != LDB_SUCCESS) {
900 return replmd_replicated_request_error(ar, ret);
903 /* find existing meta data */
904 omd_value = ldb_msg_find_ldb_val(ar->sub.search_msg, "replPropertyMetaData");
906 nt_status = ndr_pull_struct_blob(omd_value, ar->sub.mem_ctx, &omd,
907 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
908 if (!NT_STATUS_IS_OK(nt_status)) {
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 nt_status = ndr_push_struct_blob(&nmd_value, msg, &nmd,
989 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
990 if (!NT_STATUS_IS_OK(nt_status)) {
991 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
995 * check if some replicated attributes left, otherwise skip the ldb_modify() call
997 if (msg->num_elements == 0) {
998 ldb_debug(ar->module->ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
1003 ldb_debug(ar->module->ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
1004 ar->index_current, msg->num_elements);
1007 * when we now that we'll modify the record, add the whenChanged, uSNChanged
1008 * and replPopertyMetaData attributes
1010 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
1011 if (ret != LDB_SUCCESS) {
1012 return replmd_replicated_request_error(ar, ret);
1014 ret = samdb_msg_add_uint64(ar->module->ldb, msg, msg, "uSNChanged", seq_num);
1015 if (ret != LDB_SUCCESS) {
1016 return replmd_replicated_request_error(ar, ret);
1018 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
1019 if (ret != LDB_SUCCESS) {
1020 return replmd_replicated_request_error(ar, ret);
1023 replmd_ldb_message_sort(msg, ar->schema);
1025 /* we want to replace the old values */
1026 for (i=0; i < msg->num_elements; i++) {
1027 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
1030 ret = ldb_build_mod_req(&ar->sub.change_req,
1036 replmd_replicated_apply_merge_callback);
1037 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1039 #ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */
1040 return ldb_next_request(ar->module, ar->sub.change_req);
1042 ret = ldb_next_request(ar->module, ar->sub.change_req);
1043 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1045 ar->sub.change_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
1046 if (ar->sub.change_ret != LDB_SUCCESS) {
1047 return replmd_replicated_request_error(ar, ar->sub.change_ret);
1051 talloc_free(ar->sub.mem_ctx);
1052 ZERO_STRUCT(ar->sub);
1054 ar->index_current++;
1060 static int replmd_replicated_apply_search_callback(struct ldb_context *ldb,
1062 struct ldb_reply *ares)
1064 struct replmd_replicated_request *ar = talloc_get_type(private_data,
1065 struct replmd_replicated_request);
1066 bool is_done = false;
1068 switch (ares->type) {
1069 case LDB_REPLY_ENTRY:
1070 ar->sub.search_msg = talloc_steal(ar->sub.mem_ctx, ares->message);
1072 case LDB_REPLY_REFERRAL:
1073 /* we ignore referrals */
1075 case LDB_REPLY_EXTENDED:
1076 case LDB_REPLY_DONE:
1082 #ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */
1084 ar->sub.search_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
1085 if (ar->sub.search_ret != LDB_SUCCESS) {
1086 return replmd_replicated_request_error(ar, ar->sub.search_ret);
1088 if (ar->sub.search_msg) {
1089 return replmd_replicated_apply_merge(ar);
1091 return replmd_replicated_apply_add(ar);
1097 static int replmd_replicated_apply_search(struct replmd_replicated_request *ar)
1103 tmp_str = ldb_binary_encode(ar->sub.mem_ctx, ar->objs->objects[ar->index_current].guid_value);
1104 if (!tmp_str) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1106 filter = talloc_asprintf(ar->sub.mem_ctx, "(objectGUID=%s)", tmp_str);
1107 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1108 talloc_free(tmp_str);
1110 ret = ldb_build_search_req(&ar->sub.search_req,
1113 ar->objs->partition_dn,
1119 replmd_replicated_apply_search_callback);
1120 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1122 #ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */
1123 return ldb_next_request(ar->module, ar->sub.search_req);
1125 ret = ldb_next_request(ar->module, ar->sub.search_req);
1126 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1128 ar->sub.search_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
1129 if (ar->sub.search_ret != LDB_SUCCESS) {
1130 return replmd_replicated_request_error(ar, ar->sub.search_ret);
1132 if (ar->sub.search_msg) {
1133 return replmd_replicated_apply_merge(ar);
1136 return replmd_replicated_apply_add(ar);
1140 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
1142 #ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */
1143 if (ar->index_current >= ar->objs->num_objects) {
1144 return replmd_replicated_uptodate_vector(ar);
1148 ar->sub.mem_ctx = talloc_new(ar);
1149 if (!ar->sub.mem_ctx) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1151 return replmd_replicated_apply_search(ar);
1154 static int replmd_replicated_uptodate_modify_callback(struct ldb_context *ldb,
1156 struct ldb_reply *ares)
1158 #ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */
1159 struct replmd_replicated_request *ar = talloc_get_type(private_data,
1160 struct replmd_replicated_request);
1162 ar->sub.change_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
1163 if (ar->sub.change_ret != LDB_SUCCESS) {
1164 return replmd_replicated_request_error(ar, ar->sub.change_ret);
1167 talloc_free(ar->sub.mem_ctx);
1168 ZERO_STRUCT(ar->sub);
1170 return replmd_replicated_request_done(ar);
1176 static int replmd_drsuapi_DsReplicaCursor2_compare(const struct drsuapi_DsReplicaCursor2 *c1,
1177 const struct drsuapi_DsReplicaCursor2 *c2)
1179 return GUID_compare(&c1->source_dsa_invocation_id, &c2->source_dsa_invocation_id);
1182 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
1185 struct ldb_message *msg;
1186 struct replUpToDateVectorBlob ouv;
1187 const struct ldb_val *ouv_value;
1188 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
1189 struct replUpToDateVectorBlob nuv;
1190 struct ldb_val nuv_value;
1191 struct ldb_message_element *nuv_el = NULL;
1192 const struct GUID *our_invocation_id;
1193 struct ldb_message_element *orf_el = NULL;
1194 struct repsFromToBlob nrf;
1195 struct ldb_val *nrf_value = NULL;
1196 struct ldb_message_element *nrf_el = NULL;
1200 time_t t = time(NULL);
1204 ruv = ar->objs->uptodateness_vector;
1210 unix_to_nt_time(&now, t);
1213 * we use the next sequence number for our own highest_usn
1214 * because we will do a modify request and this will increment
1217 ret = ldb_sequence_number(ar->module->ldb, LDB_SEQ_NEXT, &seq_num);
1218 if (ret != LDB_SUCCESS) {
1219 return replmd_replicated_request_error(ar, ret);
1223 * first create the new replUpToDateVector
1225 ouv_value = ldb_msg_find_ldb_val(ar->sub.search_msg, "replUpToDateVector");
1227 nt_status = ndr_pull_struct_blob(ouv_value, ar->sub.mem_ctx, &ouv,
1228 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
1229 if (!NT_STATUS_IS_OK(nt_status)) {
1230 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
1233 if (ouv.version != 2) {
1234 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
1239 * the new uptodateness vector will at least
1240 * contain 1 entry, one for the source_dsa
1242 * plus optional values from our old vector and the one from the source_dsa
1244 nuv.ctr.ctr2.count = 1 + ouv.ctr.ctr2.count;
1245 if (ruv) nuv.ctr.ctr2.count += ruv->count;
1246 nuv.ctr.ctr2.cursors = talloc_array(ar->sub.mem_ctx,
1247 struct drsuapi_DsReplicaCursor2,
1248 nuv.ctr.ctr2.count);
1249 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1251 /* first copy the old vector */
1252 for (i=0; i < ouv.ctr.ctr2.count; i++) {
1253 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
1257 /* get our invocation_id if we have one already attached to the ldb */
1258 our_invocation_id = samdb_ntds_invocation_id(ar->module->ldb);
1260 /* merge in the source_dsa vector is available */
1261 for (i=0; (ruv && i < ruv->count); i++) {
1264 if (our_invocation_id &&
1265 GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
1266 our_invocation_id)) {
1270 for (j=0; j < ni; j++) {
1271 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
1272 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
1279 * we update only the highest_usn and not the latest_sync_success time,
1280 * because the last success stands for direct replication
1282 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
1283 nuv.ctr.ctr2.cursors[j].highest_usn = ruv->cursors[i].highest_usn;
1288 if (found) continue;
1290 /* if it's not there yet, add it */
1291 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
1296 * merge in the current highwatermark for the source_dsa
1299 for (j=0; j < ni; j++) {
1300 if (!GUID_equal(&ar->objs->source_dsa->source_dsa_invocation_id,
1301 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
1308 * here we update the highest_usn and last_sync_success time
1309 * because we're directly replicating from the source_dsa
1311 * and use the tmp_highest_usn because this is what we have just applied
1314 nuv.ctr.ctr2.cursors[j].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
1315 nuv.ctr.ctr2.cursors[j].last_sync_success = now;
1320 * here we update the highest_usn and last_sync_success time
1321 * because we're directly replicating from the source_dsa
1323 * and use the tmp_highest_usn because this is what we have just applied
1326 nuv.ctr.ctr2.cursors[ni].source_dsa_invocation_id= ar->objs->source_dsa->source_dsa_invocation_id;
1327 nuv.ctr.ctr2.cursors[ni].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
1328 nuv.ctr.ctr2.cursors[ni].last_sync_success = now;
1333 * finally correct the size of the cursors array
1335 nuv.ctr.ctr2.count = ni;
1340 qsort(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count,
1341 sizeof(struct drsuapi_DsReplicaCursor2),
1342 (comparison_fn_t)replmd_drsuapi_DsReplicaCursor2_compare);
1345 * create the change ldb_message
1347 msg = ldb_msg_new(ar->sub.mem_ctx);
1348 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1349 msg->dn = ar->sub.search_msg->dn;
1351 nt_status = ndr_push_struct_blob(&nuv_value, msg, &nuv,
1352 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
1353 if (!NT_STATUS_IS_OK(nt_status)) {
1354 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
1356 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
1357 if (ret != LDB_SUCCESS) {
1358 return replmd_replicated_request_error(ar, ret);
1360 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
1363 * now create the new repsFrom value from the given repsFromTo1 structure
1367 nrf.ctr.ctr1 = *ar->objs->source_dsa;
1368 /* and fix some values... */
1369 nrf.ctr.ctr1.consecutive_sync_failures = 0;
1370 nrf.ctr.ctr1.last_success = now;
1371 nrf.ctr.ctr1.last_attempt = now;
1372 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
1373 nrf.ctr.ctr1.highwatermark.highest_usn = nrf.ctr.ctr1.highwatermark.tmp_highest_usn;
1376 * first see if we already have a repsFrom value for the current source dsa
1377 * if so we'll later replace this value
1379 orf_el = ldb_msg_find_element(ar->sub.search_msg, "repsFrom");
1381 for (i=0; i < orf_el->num_values; i++) {
1382 struct repsFromToBlob *trf;
1384 trf = talloc(ar->sub.mem_ctx, struct repsFromToBlob);
1385 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1387 nt_status = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
1388 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
1389 if (!NT_STATUS_IS_OK(nt_status)) {
1390 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
1393 if (trf->version != 1) {
1394 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
1398 * we compare the source dsa objectGUID not the invocation_id
1399 * because we want only one repsFrom value per source dsa
1400 * and when the invocation_id of the source dsa has changed we don't need
1401 * the old repsFrom with the old invocation_id
1403 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
1404 &ar->objs->source_dsa->source_dsa_obj_guid)) {
1410 nrf_value = &orf_el->values[i];
1415 * copy over all old values to the new ldb_message
1417 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
1418 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1423 * if we haven't found an old repsFrom value for the current source dsa
1424 * we'll add a new value
1427 struct ldb_val zero_value;
1428 ZERO_STRUCT(zero_value);
1429 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
1430 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1432 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
1435 /* we now fill the value which is already attached to ldb_message */
1436 nt_status = ndr_push_struct_blob(nrf_value, msg, &nrf,
1437 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
1438 if (!NT_STATUS_IS_OK(nt_status)) {
1439 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
1443 * the ldb_message_element for the attribute, has all the old values and the new one
1444 * so we'll replace the whole attribute with all values
1446 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
1448 /* prepare the ldb_modify() request */
1449 ret = ldb_build_mod_req(&ar->sub.change_req,
1455 replmd_replicated_uptodate_modify_callback);
1456 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1458 #ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */
1459 return ldb_next_request(ar->module, ar->sub.change_req);
1461 ret = ldb_next_request(ar->module, ar->sub.change_req);
1462 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1464 ar->sub.change_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
1465 if (ar->sub.change_ret != LDB_SUCCESS) {
1466 return replmd_replicated_request_error(ar, ar->sub.change_ret);
1469 talloc_free(ar->sub.mem_ctx);
1470 ZERO_STRUCT(ar->sub);
1472 return replmd_replicated_request_done(ar);
1476 static int replmd_replicated_uptodate_search_callback(struct ldb_context *ldb,
1478 struct ldb_reply *ares)
1480 struct replmd_replicated_request *ar = talloc_get_type(private_data,
1481 struct replmd_replicated_request);
1482 bool is_done = false;
1484 switch (ares->type) {
1485 case LDB_REPLY_ENTRY:
1486 ar->sub.search_msg = talloc_steal(ar->sub.mem_ctx, ares->message);
1488 case LDB_REPLY_REFERRAL:
1489 /* we ignore referrals */
1491 case LDB_REPLY_EXTENDED:
1492 case LDB_REPLY_DONE:
1498 #ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */
1500 ar->sub.search_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
1501 if (ar->sub.search_ret != LDB_SUCCESS) {
1502 return replmd_replicated_request_error(ar, ar->sub.search_ret);
1504 if (!ar->sub.search_msg) {
1505 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
1508 return replmd_replicated_uptodate_modify(ar);
1514 static int replmd_replicated_uptodate_search(struct replmd_replicated_request *ar)
1517 static const char *attrs[] = {
1518 "replUpToDateVector",
1523 ret = ldb_build_search_req(&ar->sub.search_req,
1526 ar->objs->partition_dn,
1532 replmd_replicated_uptodate_search_callback);
1533 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1535 #ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */
1536 return ldb_next_request(ar->module, ar->sub.search_req);
1538 ret = ldb_next_request(ar->module, ar->sub.search_req);
1539 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1541 ar->sub.search_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
1542 if (ar->sub.search_ret != LDB_SUCCESS) {
1543 return replmd_replicated_request_error(ar, ar->sub.search_ret);
1545 if (!ar->sub.search_msg) {
1546 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
1549 return replmd_replicated_uptodate_modify(ar);
1553 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
1555 ar->sub.mem_ctx = talloc_new(ar);
1556 if (!ar->sub.mem_ctx) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1558 return replmd_replicated_uptodate_search(ar);
1561 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
1563 struct dsdb_extended_replicated_objects *objs;
1564 struct replmd_replicated_request *ar;
1566 ldb_debug(module->ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
1568 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
1570 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
1571 return LDB_ERR_PROTOCOL_ERROR;
1574 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
1575 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
1576 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
1577 return LDB_ERR_PROTOCOL_ERROR;
1580 ar = replmd_replicated_init_handle(module, req, objs);
1582 return LDB_ERR_OPERATIONS_ERROR;
1585 #ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */
1586 return replmd_replicated_apply_next(ar);
1588 while (ar->index_current < ar->objs->num_objects &&
1589 req->handle->state != LDB_ASYNC_DONE) {
1590 replmd_replicated_apply_next(ar);
1593 if (req->handle->state != LDB_ASYNC_DONE) {
1594 replmd_replicated_uptodate_vector(ar);
1601 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
1603 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
1604 return replmd_extended_replicated_objects(module, req);
1607 return ldb_next_request(module, req);
1610 static int replmd_wait_none(struct ldb_handle *handle) {
1611 struct replmd_replicated_request *ar;
1613 if (!handle || !handle->private_data) {
1614 return LDB_ERR_OPERATIONS_ERROR;
1617 ar = talloc_get_type(handle->private_data, struct replmd_replicated_request);
1619 return LDB_ERR_OPERATIONS_ERROR;
1622 /* we do only sync calls */
1623 if (handle->state != LDB_ASYNC_DONE) {
1624 return LDB_ERR_OPERATIONS_ERROR;
1627 return handle->status;
1630 static int replmd_wait_all(struct ldb_handle *handle) {
1634 while (handle->state != LDB_ASYNC_DONE) {
1635 ret = replmd_wait_none(handle);
1636 if (ret != LDB_SUCCESS) {
1641 return handle->status;
1644 static int replmd_wait(struct ldb_handle *handle, enum ldb_wait_type type)
1646 if (type == LDB_WAIT_ALL) {
1647 return replmd_wait_all(handle);
1649 return replmd_wait_none(handle);
1653 static const struct ldb_module_ops replmd_ops = {
1654 .name = "repl_meta_data",
1656 .modify = replmd_modify,
1657 .extended = replmd_extended,
1661 int repl_meta_data_module_init(void)
1663 return ldb_register_module(&replmd_ops);