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"
50 #include "param/param.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 *))
245 const struct dsdb_schema *schema;
247 /* do not manipulate our control entries */
248 if (ldb_dn_is_special(dn)) {
249 return ldb_next_request(module, req);
252 schema = dsdb_get_schema(module->ldb);
254 ldb_debug_set(module->ldb, LDB_DEBUG_FATAL,
255 "%s: no dsdb_schema loaded",
257 return LDB_ERR_CONSTRAINT_VIOLATION;
260 return fn(module, req, schema);
263 static int replmd_add_originating(struct ldb_module *module,
264 struct ldb_request *req,
265 const struct dsdb_schema *schema)
267 enum ndr_err_code ndr_err;
268 struct ldb_request *down_req;
269 struct ldb_message *msg;
270 const struct dsdb_attribute *rdn_attr = NULL;
272 struct ldb_val guid_value;
273 struct replPropertyMetaDataBlob nmd;
274 struct ldb_val nmd_value;
276 const struct GUID *our_invocation_id;
277 time_t t = time(NULL);
283 ldb_debug(module->ldb, LDB_DEBUG_TRACE, "replmd_add_originating\n");
285 if (ldb_msg_find_element(req->op.add.message, "objectGUID")) {
286 ldb_debug_set(module->ldb, LDB_DEBUG_ERROR,
287 "replmd_add_originating: it's not allowed to add an object with objectGUID\n");
288 return LDB_ERR_UNWILLING_TO_PERFORM;
291 /* Get a sequence number from the backend */
292 ret = ldb_sequence_number(module->ldb, LDB_SEQ_NEXT, &seq_num);
293 if (ret != LDB_SUCCESS) {
298 guid = GUID_random();
300 /* get our invicationId */
301 our_invocation_id = samdb_ntds_invocation_id(module->ldb);
302 if (!our_invocation_id) {
303 ldb_debug_set(module->ldb, LDB_DEBUG_ERROR,
304 "replmd_add_originating: unable to find invocationId\n");
305 return LDB_ERR_OPERATIONS_ERROR;
308 /* create a copy of the request */
309 down_req = talloc(req, struct ldb_request);
310 if (down_req == NULL) {
311 ldb_oom(module->ldb);
312 return LDB_ERR_OPERATIONS_ERROR;
316 /* we have to copy the message as the caller might have it as a const */
317 down_req->op.add.message = msg = ldb_msg_copy_shallow(down_req, req->op.add.message);
319 talloc_free(down_req);
320 ldb_oom(module->ldb);
321 return LDB_ERR_OPERATIONS_ERROR;
324 /* generated times */
325 unix_to_nt_time(&now, t);
326 time_str = ldb_timestring(msg, t);
328 talloc_free(down_req);
329 return LDB_ERR_OPERATIONS_ERROR;
333 * remove autogenerated attributes
335 ldb_msg_remove_attr(msg, "whenCreated");
336 ldb_msg_remove_attr(msg, "whenChanged");
337 ldb_msg_remove_attr(msg, "uSNCreated");
338 ldb_msg_remove_attr(msg, "uSNChanged");
339 ldb_msg_remove_attr(msg, "replPropertyMetaData");
342 * readd replicated attributes
344 ret = ldb_msg_add_string(msg, "whenCreated", time_str);
345 if (ret != LDB_SUCCESS) {
346 talloc_free(down_req);
347 ldb_oom(module->ldb);
348 return LDB_ERR_OPERATIONS_ERROR;
351 /* build the replication meta_data */
354 nmd.ctr.ctr1.count = msg->num_elements;
355 nmd.ctr.ctr1.array = talloc_array(msg,
356 struct replPropertyMetaData1,
358 if (!nmd.ctr.ctr1.array) {
359 talloc_free(down_req);
360 ldb_oom(module->ldb);
361 return LDB_ERR_OPERATIONS_ERROR;
364 for (i=0; i < msg->num_elements; i++) {
365 struct ldb_message_element *e = &msg->elements[i];
366 struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
367 const struct dsdb_attribute *sa;
369 if (e->name[0] == '@') continue;
371 sa = dsdb_attribute_by_lDAPDisplayName(schema, e->name);
373 ldb_debug_set(module->ldb, LDB_DEBUG_ERROR,
374 "replmd_add_originating: attribute '%s' not defined in schema\n",
376 talloc_free(down_req);
377 return LDB_ERR_NO_SUCH_ATTRIBUTE;
380 if ((sa->systemFlags & 0x00000001) || (sa->systemFlags & 0x00000004)) {
381 /* if the attribute is not replicated (0x00000001)
382 * or constructed (0x00000004) it has no metadata
387 m->attid = sa->attributeID_id;
389 m->originating_change_time = now;
390 m->originating_invocation_id = *our_invocation_id;
391 m->originating_usn = seq_num;
392 m->local_usn = seq_num;
395 if (ldb_attr_cmp(e->name, ldb_dn_get_rdn_name(msg->dn))) {
400 /* fix meta data count */
401 nmd.ctr.ctr1.count = ni;
404 * sort meta data array, and move the rdn attribute entry to the end
406 replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, &rdn_attr->attributeID_id);
408 /* generated NDR encoded values */
409 ndr_err = ndr_push_struct_blob(&guid_value, msg,
412 (ndr_push_flags_fn_t)ndr_push_GUID);
413 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
414 ldb_oom(module->ldb);
415 return LDB_ERR_OPERATIONS_ERROR;
417 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
418 lp_iconv_convenience(ldb_get_opaque(module->ldb, "loadparm")),
420 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
421 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
422 talloc_free(down_req);
423 ldb_oom(module->ldb);
424 return LDB_ERR_OPERATIONS_ERROR;
428 * add the autogenerated values
430 ret = ldb_msg_add_value(msg, "objectGUID", &guid_value, NULL);
431 if (ret != LDB_SUCCESS) {
432 talloc_free(down_req);
433 ldb_oom(module->ldb);
434 return LDB_ERR_OPERATIONS_ERROR;
436 ret = ldb_msg_add_string(msg, "whenChanged", time_str);
437 if (ret != LDB_SUCCESS) {
438 talloc_free(down_req);
439 ldb_oom(module->ldb);
440 return LDB_ERR_OPERATIONS_ERROR;
442 ret = samdb_msg_add_uint64(module->ldb, msg, msg, "uSNCreated", seq_num);
443 if (ret != LDB_SUCCESS) {
444 talloc_free(down_req);
445 ldb_oom(module->ldb);
446 return LDB_ERR_OPERATIONS_ERROR;
448 ret = samdb_msg_add_uint64(module->ldb, msg, msg, "uSNChanged", seq_num);
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_value(msg, "replPropertyMetaData", &nmd_value, NULL);
455 if (ret != LDB_SUCCESS) {
456 talloc_free(down_req);
457 ldb_oom(module->ldb);
458 return LDB_ERR_OPERATIONS_ERROR;
462 * sort the attributes by attid before storing the object
464 replmd_ldb_message_sort(msg, schema);
466 ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
468 /* go on with the call chain */
469 ret = ldb_next_request(module, down_req);
471 /* do not free down_req as the call results may be linked to it,
472 * it will be freed when the upper level request get freed */
473 if (ret == LDB_SUCCESS) {
474 req->handle = down_req->handle;
480 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
482 return replmd_prepare_originating(module, req, req->op.add.message->dn,
483 "replmd_add", replmd_add_originating);
486 static int replmd_modify_originating(struct ldb_module *module,
487 struct ldb_request *req,
488 const struct dsdb_schema *schema)
490 struct ldb_request *down_req;
491 struct ldb_message *msg;
493 time_t t = time(NULL);
496 ldb_debug(module->ldb, LDB_DEBUG_TRACE, "replmd_modify_originating\n");
498 down_req = talloc(req, struct ldb_request);
499 if (down_req == NULL) {
500 return LDB_ERR_OPERATIONS_ERROR;
505 /* we have to copy the message as the caller might have it as a const */
506 down_req->op.mod.message = msg = ldb_msg_copy_shallow(down_req, req->op.mod.message);
508 talloc_free(down_req);
509 return LDB_ERR_OPERATIONS_ERROR;
512 if (add_time_element(msg, "whenChanged", t) != 0) {
513 talloc_free(down_req);
514 return LDB_ERR_OPERATIONS_ERROR;
517 /* Get a sequence number from the backend */
518 ret = ldb_sequence_number(module->ldb, LDB_SEQ_NEXT, &seq_num);
519 if (ret == LDB_SUCCESS) {
520 if (add_uint64_element(msg, "uSNChanged", seq_num) != 0) {
521 talloc_free(down_req);
522 return LDB_ERR_OPERATIONS_ERROR;
526 ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
528 /* go on with the call chain */
529 ret = ldb_next_request(module, down_req);
531 /* do not free down_req as the call results may be linked to it,
532 * it will be freed when the upper level request get freed */
533 if (ret == LDB_SUCCESS) {
534 req->handle = down_req->handle;
540 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
542 return replmd_prepare_originating(module, req, req->op.mod.message->dn,
543 "replmd_modify", replmd_modify_originating);
546 static int replmd_replicated_request_reply_helper(struct replmd_replicated_request *ar, int ret)
548 struct ldb_reply *ares = NULL;
550 ar->handle->status = ret;
551 ar->handle->state = LDB_ASYNC_DONE;
553 if (!ar->orig_req->callback) {
557 /* we're done and need to report the success to the caller */
558 ares = talloc_zero(ar, struct ldb_reply);
560 ar->handle->status = LDB_ERR_OPERATIONS_ERROR;
561 ar->handle->state = LDB_ASYNC_DONE;
562 return LDB_ERR_OPERATIONS_ERROR;
565 ares->type = LDB_REPLY_EXTENDED;
566 ares->response = NULL;
568 return ar->orig_req->callback(ar->module->ldb, ar->orig_req->context, ares);
571 static int replmd_replicated_request_done(struct replmd_replicated_request *ar)
573 return replmd_replicated_request_reply_helper(ar, LDB_SUCCESS);
576 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
578 return replmd_replicated_request_reply_helper(ar, ret);
581 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
583 int ret = LDB_ERR_OTHER;
584 /* TODO: do some error mapping */
585 return replmd_replicated_request_reply_helper(ar, ret);
588 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar);
590 static int replmd_replicated_apply_add_callback(struct ldb_context *ldb,
592 struct ldb_reply *ares)
594 #ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */
595 struct replmd_replicated_request *ar = talloc_get_type(private_data,
596 struct replmd_replicated_request);
598 ar->sub.change_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
599 if (ar->sub.change_ret != LDB_SUCCESS) {
600 return replmd_replicated_request_error(ar, ar->sub.change_ret);
603 talloc_free(ar->sub.mem_ctx);
604 ZERO_STRUCT(ar->sub);
608 return replmd_replicated_apply_next(ar);
614 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
616 enum ndr_err_code ndr_err;
617 struct ldb_message *msg;
618 struct replPropertyMetaDataBlob *md;
619 struct ldb_val md_value;
625 * TODO: check if the parent object exist
629 * TODO: handle the conflict case where an object with the
633 msg = ar->objs->objects[ar->index_current].msg;
634 md = ar->objs->objects[ar->index_current].meta_data;
636 ret = ldb_sequence_number(ar->module->ldb, LDB_SEQ_NEXT, &seq_num);
637 if (ret != LDB_SUCCESS) {
638 return replmd_replicated_request_error(ar, ret);
641 ret = ldb_msg_add_value(msg, "objectGUID", &ar->objs->objects[ar->index_current].guid_value, NULL);
642 if (ret != LDB_SUCCESS) {
643 return replmd_replicated_request_error(ar, ret);
646 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
647 if (ret != LDB_SUCCESS) {
648 return replmd_replicated_request_error(ar, ret);
651 ret = samdb_msg_add_uint64(ar->module->ldb, msg, msg, "uSNCreated", seq_num);
652 if (ret != LDB_SUCCESS) {
653 return replmd_replicated_request_error(ar, ret);
656 ret = samdb_msg_add_uint64(ar->module->ldb, msg, msg, "uSNChanged", seq_num);
657 if (ret != LDB_SUCCESS) {
658 return replmd_replicated_request_error(ar, ret);
662 * the meta data array is already sorted by the caller
664 for (i=0; i < md->ctr.ctr1.count; i++) {
665 md->ctr.ctr1.array[i].local_usn = seq_num;
667 ndr_err = ndr_push_struct_blob(&md_value, msg,
668 lp_iconv_convenience(ldb_get_opaque(ar->module->ldb, "loadparm")),
670 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
671 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
672 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
673 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
675 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
676 if (ret != LDB_SUCCESS) {
677 return replmd_replicated_request_error(ar, ret);
680 replmd_ldb_message_sort(msg, ar->schema);
682 ret = ldb_build_add_req(&ar->sub.change_req,
688 replmd_replicated_apply_add_callback);
689 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
691 #ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */
692 return ldb_next_request(ar->module, ar->sub.change_req);
694 ret = ldb_next_request(ar->module, ar->sub.change_req);
695 if (ret != LDB_SUCCESS) {
696 ldb_asprintf_errstring(ar->module->ldb, "Failed to add replicated object %s: %s", ldb_dn_get_linearized(ar->sub.change_req->op.add.message->dn),
697 ldb_errstring(ar->module->ldb));
698 return replmd_replicated_request_error(ar, ret);
701 ar->sub.change_ret = ldb_wait(ar->sub.change_req->handle, LDB_WAIT_ALL);
702 if (ar->sub.change_ret != LDB_SUCCESS) {
703 ldb_asprintf_errstring(ar->module->ldb, "Failed while waiting on add replicated object %s: %s", ldb_dn_get_linearized(ar->sub.change_req->op.add.message->dn),
704 ldb_errstring(ar->module->ldb));
705 return replmd_replicated_request_error(ar, ar->sub.change_ret);
708 talloc_free(ar->sub.mem_ctx);
709 ZERO_STRUCT(ar->sub);
717 static int replmd_replPropertyMetaData1_conflict_compare(struct replPropertyMetaData1 *m1,
718 struct replPropertyMetaData1 *m2)
722 if (m1->version != m2->version) {
723 return m1->version - m2->version;
726 if (m1->originating_change_time != m2->originating_change_time) {
727 return m1->originating_change_time - m2->originating_change_time;
730 ret = GUID_compare(&m1->originating_invocation_id, &m2->originating_invocation_id);
735 return m1->originating_usn - m2->originating_usn;
738 static int replmd_replicated_apply_merge_callback(struct ldb_context *ldb,
740 struct ldb_reply *ares)
742 #ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */
743 struct replmd_replicated_request *ar = talloc_get_type(private_data,
744 struct replmd_replicated_request);
746 ret = ldb_next_request(ar->module, ar->sub.change_req);
747 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
749 ar->sub.change_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
750 if (ar->sub.change_ret != LDB_SUCCESS) {
751 return replmd_replicated_request_error(ar, ar->sub.change_ret);
754 talloc_free(ar->sub.mem_ctx);
755 ZERO_STRUCT(ar->sub);
765 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
767 enum ndr_err_code ndr_err;
768 struct ldb_message *msg;
769 struct replPropertyMetaDataBlob *rmd;
770 struct replPropertyMetaDataBlob omd;
771 const struct ldb_val *omd_value;
772 struct replPropertyMetaDataBlob nmd;
773 struct ldb_val nmd_value;
775 uint32_t removed_attrs = 0;
779 msg = ar->objs->objects[ar->index_current].msg;
780 rmd = ar->objs->objects[ar->index_current].meta_data;
785 * TODO: add rename conflict handling
787 if (ldb_dn_compare(msg->dn, ar->sub.search_msg->dn) != 0) {
788 ldb_debug_set(ar->module->ldb, LDB_DEBUG_FATAL, "replmd_replicated_apply_merge[%u]: rename not supported",
790 ldb_debug(ar->module->ldb, LDB_DEBUG_FATAL, "%s => %s\n",
791 ldb_dn_get_linearized(ar->sub.search_msg->dn),
792 ldb_dn_get_linearized(msg->dn));
793 return replmd_replicated_request_werror(ar, WERR_NOT_SUPPORTED);
796 ret = ldb_sequence_number(ar->module->ldb, LDB_SEQ_NEXT, &seq_num);
797 if (ret != LDB_SUCCESS) {
798 return replmd_replicated_request_error(ar, ret);
801 /* find existing meta data */
802 omd_value = ldb_msg_find_ldb_val(ar->sub.search_msg, "replPropertyMetaData");
804 ndr_err = ndr_pull_struct_blob(omd_value, ar->sub.mem_ctx,
805 lp_iconv_convenience(ldb_get_opaque(ar->module->ldb, "loadparm")), &omd,
806 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
807 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
808 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
809 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
812 if (omd.version != 1) {
813 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
819 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
820 nmd.ctr.ctr1.array = talloc_array(ar->sub.mem_ctx,
821 struct replPropertyMetaData1,
823 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOMEM);
825 /* first copy the old meta data */
826 for (i=0; i < omd.ctr.ctr1.count; i++) {
827 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
831 /* now merge in the new meta data */
832 for (i=0; i < rmd->ctr.ctr1.count; i++) {
835 rmd->ctr.ctr1.array[i].local_usn = seq_num;
837 for (j=0; j < ni; j++) {
840 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
844 cmp = replmd_replPropertyMetaData1_conflict_compare(&rmd->ctr.ctr1.array[i],
845 &nmd.ctr.ctr1.array[j]);
847 /* replace the entry */
848 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
853 /* we don't want to apply this change so remove the attribute */
854 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
863 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
868 * finally correct the size of the meta_data array
870 nmd.ctr.ctr1.count = ni;
873 * the rdn attribute (the alias for the name attribute),
874 * 'cn' for most objects is the last entry in the meta data array
877 * sort the new meta data array
880 struct replPropertyMetaData1 *rdn_p;
881 uint32_t rdn_idx = omd.ctr.ctr1.count - 1;
883 rdn_p = &nmd.ctr.ctr1.array[rdn_idx];
884 replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, &rdn_p->attid);
887 /* create the meta data value */
888 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
889 lp_iconv_convenience(ldb_get_opaque(ar->module->ldb, "loadparm")),
891 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
892 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
893 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
894 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
898 * check if some replicated attributes left, otherwise skip the ldb_modify() call
900 if (msg->num_elements == 0) {
901 ldb_debug(ar->module->ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
906 ldb_debug(ar->module->ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
907 ar->index_current, msg->num_elements);
910 * when we now that we'll modify the record, add the whenChanged, uSNChanged
911 * and replPopertyMetaData attributes
913 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
914 if (ret != LDB_SUCCESS) {
915 return replmd_replicated_request_error(ar, ret);
917 ret = samdb_msg_add_uint64(ar->module->ldb, msg, msg, "uSNChanged", seq_num);
918 if (ret != LDB_SUCCESS) {
919 return replmd_replicated_request_error(ar, ret);
921 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
922 if (ret != LDB_SUCCESS) {
923 return replmd_replicated_request_error(ar, ret);
926 replmd_ldb_message_sort(msg, ar->schema);
928 /* we want to replace the old values */
929 for (i=0; i < msg->num_elements; i++) {
930 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
933 ret = ldb_build_mod_req(&ar->sub.change_req,
939 replmd_replicated_apply_merge_callback);
940 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
942 #ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */
943 return ldb_next_request(ar->module, ar->sub.change_req);
945 ret = ldb_next_request(ar->module, ar->sub.change_req);
946 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
948 ar->sub.change_ret = ldb_wait(ar->sub.change_req->handle, LDB_WAIT_ALL);
949 if (ar->sub.change_ret != LDB_SUCCESS) {
950 return replmd_replicated_request_error(ar, ar->sub.change_ret);
954 talloc_free(ar->sub.mem_ctx);
955 ZERO_STRUCT(ar->sub);
963 static int replmd_replicated_apply_search_callback(struct ldb_context *ldb,
965 struct ldb_reply *ares)
967 struct replmd_replicated_request *ar = talloc_get_type(private_data,
968 struct replmd_replicated_request);
969 bool is_done = false;
971 switch (ares->type) {
972 case LDB_REPLY_ENTRY:
973 ar->sub.search_msg = talloc_steal(ar->sub.mem_ctx, ares->message);
975 case LDB_REPLY_REFERRAL:
976 /* we ignore referrals */
978 case LDB_REPLY_EXTENDED:
985 #ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */
987 ar->sub.search_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
988 if (ar->sub.search_ret != LDB_SUCCESS) {
989 return replmd_replicated_request_error(ar, ar->sub.search_ret);
991 if (ar->sub.search_msg) {
992 return replmd_replicated_apply_merge(ar);
994 return replmd_replicated_apply_add(ar);
1000 static int replmd_replicated_apply_search(struct replmd_replicated_request *ar)
1006 tmp_str = ldb_binary_encode(ar->sub.mem_ctx, ar->objs->objects[ar->index_current].guid_value);
1007 if (!tmp_str) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1009 filter = talloc_asprintf(ar->sub.mem_ctx, "(objectGUID=%s)", tmp_str);
1010 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1011 talloc_free(tmp_str);
1013 ret = ldb_build_search_req(&ar->sub.search_req,
1016 ar->objs->partition_dn,
1022 replmd_replicated_apply_search_callback);
1023 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1025 #ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */
1026 return ldb_next_request(ar->module, ar->sub.search_req);
1028 ret = ldb_next_request(ar->module, ar->sub.search_req);
1029 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1031 ar->sub.search_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
1032 if (ar->sub.search_ret != LDB_SUCCESS && ar->sub.search_ret != LDB_ERR_NO_SUCH_OBJECT) {
1033 return replmd_replicated_request_error(ar, ar->sub.search_ret);
1035 if (ar->sub.search_msg) {
1036 return replmd_replicated_apply_merge(ar);
1039 return replmd_replicated_apply_add(ar);
1043 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
1045 #ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */
1046 if (ar->index_current >= ar->objs->num_objects) {
1047 return replmd_replicated_uptodate_vector(ar);
1051 ar->sub.mem_ctx = talloc_new(ar);
1052 if (!ar->sub.mem_ctx) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1054 return replmd_replicated_apply_search(ar);
1057 static int replmd_replicated_uptodate_modify_callback(struct ldb_context *ldb,
1059 struct ldb_reply *ares)
1061 #ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */
1062 struct replmd_replicated_request *ar = talloc_get_type(private_data,
1063 struct replmd_replicated_request);
1065 ar->sub.change_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
1066 if (ar->sub.change_ret != LDB_SUCCESS) {
1067 return replmd_replicated_request_error(ar, ar->sub.change_ret);
1070 talloc_free(ar->sub.mem_ctx);
1071 ZERO_STRUCT(ar->sub);
1073 return replmd_replicated_request_done(ar);
1079 static int replmd_drsuapi_DsReplicaCursor2_compare(const struct drsuapi_DsReplicaCursor2 *c1,
1080 const struct drsuapi_DsReplicaCursor2 *c2)
1082 return GUID_compare(&c1->source_dsa_invocation_id, &c2->source_dsa_invocation_id);
1085 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
1087 enum ndr_err_code ndr_err;
1088 struct ldb_message *msg;
1089 struct replUpToDateVectorBlob ouv;
1090 const struct ldb_val *ouv_value;
1091 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
1092 struct replUpToDateVectorBlob nuv;
1093 struct ldb_val nuv_value;
1094 struct ldb_message_element *nuv_el = NULL;
1095 const struct GUID *our_invocation_id;
1096 struct ldb_message_element *orf_el = NULL;
1097 struct repsFromToBlob nrf;
1098 struct ldb_val *nrf_value = NULL;
1099 struct ldb_message_element *nrf_el = NULL;
1103 time_t t = time(NULL);
1107 ruv = ar->objs->uptodateness_vector;
1113 unix_to_nt_time(&now, t);
1116 * we use the next sequence number for our own highest_usn
1117 * because we will do a modify request and this will increment
1120 ret = ldb_sequence_number(ar->module->ldb, LDB_SEQ_NEXT, &seq_num);
1121 if (ret != LDB_SUCCESS) {
1122 return replmd_replicated_request_error(ar, ret);
1126 * first create the new replUpToDateVector
1128 ouv_value = ldb_msg_find_ldb_val(ar->sub.search_msg, "replUpToDateVector");
1130 ndr_err = ndr_pull_struct_blob(ouv_value, ar->sub.mem_ctx,
1131 lp_iconv_convenience(ldb_get_opaque(ar->module->ldb, "loadparm")), &ouv,
1132 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
1133 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1134 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
1135 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
1138 if (ouv.version != 2) {
1139 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
1144 * the new uptodateness vector will at least
1145 * contain 1 entry, one for the source_dsa
1147 * plus optional values from our old vector and the one from the source_dsa
1149 nuv.ctr.ctr2.count = 1 + ouv.ctr.ctr2.count;
1150 if (ruv) nuv.ctr.ctr2.count += ruv->count;
1151 nuv.ctr.ctr2.cursors = talloc_array(ar->sub.mem_ctx,
1152 struct drsuapi_DsReplicaCursor2,
1153 nuv.ctr.ctr2.count);
1154 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1156 /* first copy the old vector */
1157 for (i=0; i < ouv.ctr.ctr2.count; i++) {
1158 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
1162 /* get our invocation_id if we have one already attached to the ldb */
1163 our_invocation_id = samdb_ntds_invocation_id(ar->module->ldb);
1165 /* merge in the source_dsa vector is available */
1166 for (i=0; (ruv && i < ruv->count); i++) {
1169 if (our_invocation_id &&
1170 GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
1171 our_invocation_id)) {
1175 for (j=0; j < ni; j++) {
1176 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
1177 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
1184 * we update only the highest_usn and not the latest_sync_success time,
1185 * because the last success stands for direct replication
1187 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
1188 nuv.ctr.ctr2.cursors[j].highest_usn = ruv->cursors[i].highest_usn;
1193 if (found) continue;
1195 /* if it's not there yet, add it */
1196 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
1201 * merge in the current highwatermark for the source_dsa
1204 for (j=0; j < ni; j++) {
1205 if (!GUID_equal(&ar->objs->source_dsa->source_dsa_invocation_id,
1206 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
1213 * here we update the highest_usn and last_sync_success time
1214 * because we're directly replicating from the source_dsa
1216 * and use the tmp_highest_usn because this is what we have just applied
1219 nuv.ctr.ctr2.cursors[j].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
1220 nuv.ctr.ctr2.cursors[j].last_sync_success = now;
1225 * here we update the highest_usn and last_sync_success time
1226 * because we're directly replicating from the source_dsa
1228 * and use the tmp_highest_usn because this is what we have just applied
1231 nuv.ctr.ctr2.cursors[ni].source_dsa_invocation_id= ar->objs->source_dsa->source_dsa_invocation_id;
1232 nuv.ctr.ctr2.cursors[ni].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
1233 nuv.ctr.ctr2.cursors[ni].last_sync_success = now;
1238 * finally correct the size of the cursors array
1240 nuv.ctr.ctr2.count = ni;
1245 qsort(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count,
1246 sizeof(struct drsuapi_DsReplicaCursor2),
1247 (comparison_fn_t)replmd_drsuapi_DsReplicaCursor2_compare);
1250 * create the change ldb_message
1252 msg = ldb_msg_new(ar->sub.mem_ctx);
1253 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1254 msg->dn = ar->sub.search_msg->dn;
1256 ndr_err = ndr_push_struct_blob(&nuv_value, msg,
1257 lp_iconv_convenience(ldb_get_opaque(ar->module->ldb, "loadparm")),
1259 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
1260 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1261 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
1262 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
1264 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
1265 if (ret != LDB_SUCCESS) {
1266 return replmd_replicated_request_error(ar, ret);
1268 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
1271 * now create the new repsFrom value from the given repsFromTo1 structure
1275 nrf.ctr.ctr1 = *ar->objs->source_dsa;
1276 /* and fix some values... */
1277 nrf.ctr.ctr1.consecutive_sync_failures = 0;
1278 nrf.ctr.ctr1.last_success = now;
1279 nrf.ctr.ctr1.last_attempt = now;
1280 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
1281 nrf.ctr.ctr1.highwatermark.highest_usn = nrf.ctr.ctr1.highwatermark.tmp_highest_usn;
1284 * first see if we already have a repsFrom value for the current source dsa
1285 * if so we'll later replace this value
1287 orf_el = ldb_msg_find_element(ar->sub.search_msg, "repsFrom");
1289 for (i=0; i < orf_el->num_values; i++) {
1290 struct repsFromToBlob *trf;
1292 trf = talloc(ar->sub.mem_ctx, struct repsFromToBlob);
1293 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1295 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, lp_iconv_convenience(ldb_get_opaque(ar->module->ldb, "loadparm")), trf,
1296 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
1297 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1298 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
1299 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
1302 if (trf->version != 1) {
1303 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
1307 * we compare the source dsa objectGUID not the invocation_id
1308 * because we want only one repsFrom value per source dsa
1309 * and when the invocation_id of the source dsa has changed we don't need
1310 * the old repsFrom with the old invocation_id
1312 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
1313 &ar->objs->source_dsa->source_dsa_obj_guid)) {
1319 nrf_value = &orf_el->values[i];
1324 * copy over all old values to the new ldb_message
1326 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
1327 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1332 * if we haven't found an old repsFrom value for the current source dsa
1333 * we'll add a new value
1336 struct ldb_val zero_value;
1337 ZERO_STRUCT(zero_value);
1338 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
1339 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1341 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
1344 /* we now fill the value which is already attached to ldb_message */
1345 ndr_err = ndr_push_struct_blob(nrf_value, msg,
1346 lp_iconv_convenience(ldb_get_opaque(ar->module->ldb, "loadparm")),
1348 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
1349 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1350 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
1351 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
1355 * the ldb_message_element for the attribute, has all the old values and the new one
1356 * so we'll replace the whole attribute with all values
1358 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
1360 /* prepare the ldb_modify() request */
1361 ret = ldb_build_mod_req(&ar->sub.change_req,
1367 replmd_replicated_uptodate_modify_callback);
1368 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1370 #ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */
1371 return ldb_next_request(ar->module, ar->sub.change_req);
1373 ret = ldb_next_request(ar->module, ar->sub.change_req);
1374 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1376 ar->sub.change_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
1377 if (ar->sub.change_ret != LDB_SUCCESS) {
1378 return replmd_replicated_request_error(ar, ar->sub.change_ret);
1381 talloc_free(ar->sub.mem_ctx);
1382 ZERO_STRUCT(ar->sub);
1384 return replmd_replicated_request_done(ar);
1388 static int replmd_replicated_uptodate_search_callback(struct ldb_context *ldb,
1390 struct ldb_reply *ares)
1392 struct replmd_replicated_request *ar = talloc_get_type(private_data,
1393 struct replmd_replicated_request);
1394 bool is_done = false;
1396 switch (ares->type) {
1397 case LDB_REPLY_ENTRY:
1398 ar->sub.search_msg = talloc_steal(ar->sub.mem_ctx, ares->message);
1400 case LDB_REPLY_REFERRAL:
1401 /* we ignore referrals */
1403 case LDB_REPLY_EXTENDED:
1404 case LDB_REPLY_DONE:
1410 #ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */
1412 ar->sub.search_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
1413 if (ar->sub.search_ret != LDB_SUCCESS) {
1414 return replmd_replicated_request_error(ar, ar->sub.search_ret);
1416 if (!ar->sub.search_msg) {
1417 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
1420 return replmd_replicated_uptodate_modify(ar);
1426 static int replmd_replicated_uptodate_search(struct replmd_replicated_request *ar)
1429 static const char *attrs[] = {
1430 "replUpToDateVector",
1435 ret = ldb_build_search_req(&ar->sub.search_req,
1438 ar->objs->partition_dn,
1444 replmd_replicated_uptodate_search_callback);
1445 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1447 #ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */
1448 return ldb_next_request(ar->module, ar->sub.search_req);
1450 ret = ldb_next_request(ar->module, ar->sub.search_req);
1451 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1453 ar->sub.search_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
1454 if (ar->sub.search_ret != LDB_SUCCESS) {
1455 return replmd_replicated_request_error(ar, ar->sub.search_ret);
1457 if (!ar->sub.search_msg) {
1458 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
1461 return replmd_replicated_uptodate_modify(ar);
1465 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
1467 ar->sub.mem_ctx = talloc_new(ar);
1468 if (!ar->sub.mem_ctx) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1470 return replmd_replicated_uptodate_search(ar);
1473 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
1475 struct dsdb_extended_replicated_objects *objs;
1476 struct replmd_replicated_request *ar;
1478 ldb_debug(module->ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
1480 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
1482 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
1483 return LDB_ERR_PROTOCOL_ERROR;
1486 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
1487 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
1488 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
1489 return LDB_ERR_PROTOCOL_ERROR;
1492 ar = replmd_replicated_init_handle(module, req, objs);
1494 return LDB_ERR_OPERATIONS_ERROR;
1497 #ifdef REPLMD_FULL_ASYNC /* TODO: activate this code when ldb support full async code */
1498 return replmd_replicated_apply_next(ar);
1500 while (ar->index_current < ar->objs->num_objects &&
1501 req->handle->state != LDB_ASYNC_DONE) {
1502 replmd_replicated_apply_next(ar);
1505 if (req->handle->state != LDB_ASYNC_DONE) {
1506 replmd_replicated_uptodate_vector(ar);
1513 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
1515 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
1516 return replmd_extended_replicated_objects(module, req);
1519 return ldb_next_request(module, req);
1522 static int replmd_wait_none(struct ldb_handle *handle) {
1523 struct replmd_replicated_request *ar;
1525 if (!handle || !handle->private_data) {
1526 return LDB_ERR_OPERATIONS_ERROR;
1529 ar = talloc_get_type(handle->private_data, struct replmd_replicated_request);
1531 return LDB_ERR_OPERATIONS_ERROR;
1534 /* we do only sync calls */
1535 if (handle->state != LDB_ASYNC_DONE) {
1536 return LDB_ERR_OPERATIONS_ERROR;
1539 return handle->status;
1542 static int replmd_wait_all(struct ldb_handle *handle) {
1546 while (handle->state != LDB_ASYNC_DONE) {
1547 ret = replmd_wait_none(handle);
1548 if (ret != LDB_SUCCESS) {
1553 return handle->status;
1556 static int replmd_wait(struct ldb_handle *handle, enum ldb_wait_type type)
1558 if (type == LDB_WAIT_ALL) {
1559 return replmd_wait_all(handle);
1561 return replmd_wait_none(handle);
1565 static const struct ldb_module_ops replmd_ops = {
1566 .name = "repl_meta_data",
1568 .modify = replmd_modify,
1569 .extended = replmd_extended,
1573 int repl_meta_data_module_init(void)
1575 return ldb_register_module(&replmd_ops);