r21496: A number of ldb control and LDAP changes, surrounding the
[Samba/ekacnet.git] / source4 / dsdb / samdb / ldb_modules / repl_meta_data.c
blob10f3da243b542b9101f374ef189c2635adb76de6
1 /*
2 ldb database library
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 2007
9 ** NOTE! The following LGPL license applies to the ldb
10 ** library. This does NOT imply that all of Samba is released
11 ** under the LGPL
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 2 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
29 * Name: ldb
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
38 * Author: Simo Sorce
39 * Author: Stefan Metzmacher
42 #include "includes.h"
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;
63 struct {
64 TALLOC_CTX *mem_ctx;
65 struct ldb_request *search_req;
66 struct ldb_message *search_msg;
67 int search_ret;
68 struct ldb_request *change_req;
69 int change_ret;
70 } sub;
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;
78 struct ldb_handle *h;
79 const struct dsdb_schema *schema;
81 schema = dsdb_get_schema(module->ldb);
82 if (!schema) {
83 ldb_debug_set(module->ldb, LDB_DEBUG_FATAL,
84 "replmd_replicated_init_handle: no loaded schema found\n");
85 return NULL;
88 h = talloc_zero(req, struct ldb_handle);
89 if (h == NULL) {
90 ldb_set_errstring(module->ldb, "Out of Memory");
91 return NULL;
94 h->module = module;
95 h->state = LDB_ASYNC_PENDING;
96 h->status = LDB_SUCCESS;
98 ar = talloc_zero(h, struct replmd_replicated_request);
99 if (ar == NULL) {
100 ldb_set_errstring(module->ldb, "Out of Memory");
101 talloc_free(h);
102 return NULL;
105 h->private_data = ar;
107 ar->module = module;
108 ar->handle = h;
109 ar->orig_req = req;
110 ar->schema = schema;
111 ar->objs = objs;
113 req->handle = h;
115 return 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;
124 char *s;
126 if (ldb_msg_find_element(msg, attr) != NULL) {
127 return 0;
130 s = ldb_timestring(msg, t);
131 if (s == NULL) {
132 return -1;
135 if (ldb_msg_add_string(msg, attr, s) != 0) {
136 return -1;
139 el = ldb_msg_find_element(msg, attr);
140 /* always set as replace. This works because on add ops, the flag
141 is ignored */
142 el->flags = LDB_FLAG_MOD_REPLACE;
144 return 0;
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) {
155 return 0;
158 if (ldb_msg_add_fmt(msg, attr, "%llu", (unsigned long long)v) != 0) {
159 return -1;
162 el = ldb_msg_find_element(msg, attr);
163 /* always set as replace. This works because on add ops, the flag
164 is ignored */
165 el->flags = LDB_FLAG_MOD_REPLACE;
167 return 0;
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) {
175 return 0;
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) {
184 return 1;
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) {
193 return -1;
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
223 * in the schema
225 if (!a1 || !a2) {
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);
256 if (!schema) {
257 ldb_debug_set(module->ldb, LDB_DEBUG_FATAL,
258 "%s: no dsdb_schema loaded",
259 fn_name);
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",
267 fn_name);
268 return LDB_ERR_CONSTRAINT_VIOLATION;
271 partition = talloc_get_type(partition_ctrl->data,
272 struct dsdb_control_current_partition);
273 if (!partition) {
274 ldb_debug_set(module->ldb, LDB_DEBUG_FATAL,
275 "%s: current partition control contains invalid data",
276 fn_name);
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)
295 NTSTATUS nt_status;
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;
304 struct GUID guid;
305 struct ldb_val guid_value;
306 struct replPropertyMetaDataBlob nmd;
307 struct ldb_val nmd_value;
308 uint64_t seq_num;
309 const struct GUID *our_invocation_id;
310 time_t t = time(NULL);
311 NTTIME now;
312 char *time_str;
313 int ret;
314 uint32_t i, ni=0;
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) {
333 return ret;
336 /* a new GUID */
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;
353 *down_req = *req;
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);
357 if (msg == NULL) {
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);
366 if (!time_str) {
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);
375 if (!rdn_name) {
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);
381 if (!rdn_attr) {
382 talloc_free(down_req);
383 return LDB_ERR_OPERATIONS_ERROR;
385 rdn_value = ldb_dn_get_rdn_val(msg->dn);
386 if (!rdn_value) {
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:
405 * - the parent DN
406 * - the upper case of rdn_attr->LDAPDisplayName
407 * - rdn_value
409 new_dn = ldb_dn_copy(msg, msg->dn);
410 if (!new_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;
427 msg->dn = new_dn;
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 */
469 ZERO_STRUCT(nmd);
470 nmd.version = 1;
471 nmd.ctr.ctr1.count = msg->num_elements;
472 nmd.ctr.ctr1.array = talloc_array(msg,
473 struct replPropertyMetaData1,
474 nmd.ctr.ctr1.count);
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 sa = dsdb_attribute_by_lDAPDisplayName(schema, e->name);
487 if (!sa) {
488 ldb_debug_set(module->ldb, LDB_DEBUG_ERROR,
489 "replmd_add_originating: attribute '%s' not defined in schema\n",
490 e->name);
491 talloc_free(down_req);
492 return LDB_ERR_NO_SUCH_ATTRIBUTE;
495 if (sa->systemFlags & 0x00000001) {
496 /* attribute is not replicated so it has no meta data */
497 continue;
500 m->attid = sa->attributeID_id;
501 m->version = 1;
502 m->orginating_time = now;
503 m->orginating_invocation_id = *our_invocation_id;
504 m->orginating_usn = seq_num;
505 m->local_usn = seq_num;
506 ni++;
509 /* fix meta data count */
510 nmd.ctr.ctr1.count = ni;
513 * sort meta data array, and move the rdn attribute entry to the end
515 replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, &rdn_attr->attributeID_id);
517 /* generated NDR encoded values */
518 nt_status = ndr_push_struct_blob(&guid_value, msg, &guid,
519 (ndr_push_flags_fn_t)ndr_push_GUID);
520 if (!NT_STATUS_IS_OK(nt_status)) {
521 talloc_free(down_req);
522 ldb_oom(module->ldb);
523 return LDB_ERR_OPERATIONS_ERROR;
525 nt_status = ndr_push_struct_blob(&nmd_value, msg, &nmd,
526 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
527 if (!NT_STATUS_IS_OK(nt_status)) {
528 talloc_free(down_req);
529 ldb_oom(module->ldb);
530 return LDB_ERR_OPERATIONS_ERROR;
534 * add the autogenerated values
536 ret = ldb_msg_add_value(msg, "objectGUID", &guid_value, NULL);
537 if (ret != LDB_SUCCESS) {
538 talloc_free(down_req);
539 ldb_oom(module->ldb);
540 return LDB_ERR_OPERATIONS_ERROR;
542 ret = ldb_msg_add_string(msg, "whenChanged", time_str);
543 if (ret != LDB_SUCCESS) {
544 talloc_free(down_req);
545 ldb_oom(module->ldb);
546 return LDB_ERR_OPERATIONS_ERROR;
548 ret = samdb_msg_add_uint64(module->ldb, msg, msg, "uSNCreated", seq_num);
549 if (ret != LDB_SUCCESS) {
550 talloc_free(down_req);
551 ldb_oom(module->ldb);
552 return LDB_ERR_OPERATIONS_ERROR;
554 ret = samdb_msg_add_uint64(module->ldb, msg, msg, "uSNChanged", seq_num);
555 if (ret != LDB_SUCCESS) {
556 talloc_free(down_req);
557 ldb_oom(module->ldb);
558 return LDB_ERR_OPERATIONS_ERROR;
560 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
561 if (ret != LDB_SUCCESS) {
562 talloc_free(down_req);
563 ldb_oom(module->ldb);
564 return LDB_ERR_OPERATIONS_ERROR;
568 * sort the attributes by attid before storing the object
570 replmd_ldb_message_sort(msg, schema);
572 ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
574 /* go on with the call chain */
575 ret = ldb_next_request(module, down_req);
577 /* do not free down_req as the call results may be linked to it,
578 * it will be freed when the upper level request get freed */
579 if (ret == LDB_SUCCESS) {
580 req->handle = down_req->handle;
583 return ret;
586 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
588 return replmd_prepare_originating(module, req, req->op.add.message->dn,
589 "replmd_add", replmd_add_originating);
592 static int replmd_modify_originating(struct ldb_module *module,
593 struct ldb_request *req,
594 const struct dsdb_schema *schema,
595 const struct dsdb_control_current_partition *partition)
597 struct ldb_request *down_req;
598 struct ldb_message *msg;
599 int ret;
600 time_t t = time(NULL);
601 uint64_t seq_num;
603 ldb_debug(module->ldb, LDB_DEBUG_TRACE, "replmd_modify_originating\n");
605 down_req = talloc(req, struct ldb_request);
606 if (down_req == NULL) {
607 return LDB_ERR_OPERATIONS_ERROR;
610 *down_req = *req;
612 /* we have to copy the message as the caller might have it as a const */
613 down_req->op.mod.message = msg = ldb_msg_copy_shallow(down_req, req->op.mod.message);
614 if (msg == NULL) {
615 talloc_free(down_req);
616 return LDB_ERR_OPERATIONS_ERROR;
619 if (add_time_element(msg, "whenChanged", t) != 0) {
620 talloc_free(down_req);
621 return LDB_ERR_OPERATIONS_ERROR;
624 /* Get a sequence number from the backend */
625 ret = ldb_sequence_number(module->ldb, LDB_SEQ_NEXT, &seq_num);
626 if (ret == LDB_SUCCESS) {
627 if (add_uint64_element(msg, "uSNChanged", seq_num) != 0) {
628 talloc_free(down_req);
629 return LDB_ERR_OPERATIONS_ERROR;
633 ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
635 /* go on with the call chain */
636 ret = ldb_next_request(module, down_req);
638 /* do not free down_req as the call results may be linked to it,
639 * it will be freed when the upper level request get freed */
640 if (ret == LDB_SUCCESS) {
641 req->handle = down_req->handle;
644 return ret;
647 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
649 return replmd_prepare_originating(module, req, req->op.mod.message->dn,
650 "replmd_modify", replmd_modify_originating);
653 static int replmd_replicated_request_reply_helper(struct replmd_replicated_request *ar, int ret)
655 struct ldb_reply *ares = NULL;
657 ar->handle->status = ret;
658 ar->handle->state = LDB_ASYNC_DONE;
660 if (!ar->orig_req->callback) {
661 return LDB_SUCCESS;
664 /* we're done and need to report the success to the caller */
665 ares = talloc_zero(ar, struct ldb_reply);
666 if (!ares) {
667 ar->handle->status = LDB_ERR_OPERATIONS_ERROR;
668 ar->handle->state = LDB_ASYNC_DONE;
669 return LDB_ERR_OPERATIONS_ERROR;
672 ares->type = LDB_REPLY_EXTENDED;
673 ares->response = NULL;
675 return ar->orig_req->callback(ar->module->ldb, ar->orig_req->context, ares);
678 static int replmd_replicated_request_done(struct replmd_replicated_request *ar)
680 return replmd_replicated_request_reply_helper(ar, LDB_SUCCESS);
683 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
685 return replmd_replicated_request_reply_helper(ar, ret);
688 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
690 int ret = LDB_ERR_OTHER;
691 /* TODO: do some error mapping */
692 return replmd_replicated_request_reply_helper(ar, ret);
695 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar);
697 static int replmd_replicated_apply_add_callback(struct ldb_context *ldb,
698 void *private_data,
699 struct ldb_reply *ares)
701 #ifdef REPLMD_FULL_ASYNC /* TODO: active this code when ldb support full async code */
702 struct replmd_replicated_request *ar = talloc_get_type(private_data,
703 struct replmd_replicated_request);
705 ar->sub.change_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
706 if (ar->sub.change_ret != LDB_SUCCESS) {
707 return replmd_replicated_request_error(ar, ar->sub.change_ret);
710 talloc_free(ar->sub.mem_ctx);
711 ZERO_STRUCT(ar->sub);
713 ar->index_current++;
715 return replmd_replicated_apply_next(ar);
716 #else
717 return LDB_SUCCESS;
718 #endif
721 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
723 NTSTATUS nt_status;
724 struct ldb_message *msg;
725 struct replPropertyMetaDataBlob *md;
726 struct ldb_val md_value;
727 uint32_t i;
728 uint64_t seq_num;
729 int ret;
732 * TODO: check if the parent object exist
736 * TODO: handle the conflict case where an object with the
737 * same name exist
740 msg = ar->objs->objects[ar->index_current].msg;
741 md = ar->objs->objects[ar->index_current].meta_data;
743 ret = ldb_sequence_number(ar->module->ldb, LDB_SEQ_NEXT, &seq_num);
744 if (ret != LDB_SUCCESS) {
745 return replmd_replicated_request_error(ar, ret);
748 ret = ldb_msg_add_value(msg, "objectGUID", &ar->objs->objects[ar->index_current].guid_value, NULL);
749 if (ret != LDB_SUCCESS) {
750 return replmd_replicated_request_error(ar, ret);
753 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
754 if (ret != LDB_SUCCESS) {
755 return replmd_replicated_request_error(ar, ret);
758 ret = samdb_msg_add_uint64(ar->module->ldb, msg, msg, "uSNCreated", seq_num);
759 if (ret != LDB_SUCCESS) {
760 return replmd_replicated_request_error(ar, ret);
763 ret = samdb_msg_add_uint64(ar->module->ldb, msg, msg, "uSNChanged", seq_num);
764 if (ret != LDB_SUCCESS) {
765 return replmd_replicated_request_error(ar, ret);
769 * the meta data array is already sorted by the caller
771 for (i=0; i < md->ctr.ctr1.count; i++) {
772 md->ctr.ctr1.array[i].local_usn = seq_num;
774 nt_status = ndr_push_struct_blob(&md_value, msg, md,
775 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
776 if (!NT_STATUS_IS_OK(nt_status)) {
777 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
779 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
780 if (ret != LDB_SUCCESS) {
781 return replmd_replicated_request_error(ar, ret);
784 replmd_ldb_message_sort(msg, ar->schema);
786 ret = ldb_build_add_req(&ar->sub.change_req,
787 ar->module->ldb,
788 ar->sub.mem_ctx,
789 msg,
790 NULL,
792 replmd_replicated_apply_add_callback);
793 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
795 #ifdef REPLMD_FULL_ASYNC /* TODO: active this code when ldb support full async code */
796 return ldb_next_request(ar->module, ar->sub.change_req);
797 #else
798 ret = ldb_next_request(ar->module, ar->sub.change_req);
799 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
801 ar->sub.change_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
802 if (ar->sub.change_ret != LDB_SUCCESS) {
803 return replmd_replicated_request_error(ar, ar->sub.change_ret);
806 talloc_free(ar->sub.mem_ctx);
807 ZERO_STRUCT(ar->sub);
809 ar->index_current++;
811 return LDB_SUCCESS;
812 #endif
815 static int replmd_replPropertyMetaData1_conflict_compare(struct replPropertyMetaData1 *m1,
816 struct replPropertyMetaData1 *m2)
818 int ret;
820 if (m1->version != m2->version) {
821 return m1->version - m2->version;
824 if (m1->orginating_time != m2->orginating_time) {
825 return m1->orginating_time - m2->orginating_time;
828 ret = GUID_compare(&m1->orginating_invocation_id, &m2->orginating_invocation_id);
829 if (ret != 0) {
830 return ret;
833 return m1->orginating_usn - m2->orginating_usn;
836 static int replmd_replicated_apply_merge_callback(struct ldb_context *ldb,
837 void *private_data,
838 struct ldb_reply *ares)
840 #ifdef REPLMD_FULL_ASYNC /* TODO: active this code when ldb support full async code */
841 struct replmd_replicated_request *ar = talloc_get_type(private_data,
842 struct replmd_replicated_request);
844 ret = ldb_next_request(ar->module, ar->sub.change_req);
845 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
847 ar->sub.change_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
848 if (ar->sub.change_ret != LDB_SUCCESS) {
849 return replmd_replicated_request_error(ar, ar->sub.change_ret);
852 talloc_free(ar->sub.mem_ctx);
853 ZERO_STRUCT(ar->sub);
855 ar->index_current++;
857 return LDB_SUCCESS;
858 #else
859 return LDB_SUCCESS;
860 #endif
863 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
865 NTSTATUS nt_status;
866 struct ldb_message *msg;
867 struct replPropertyMetaDataBlob *rmd;
868 struct replPropertyMetaDataBlob omd;
869 const struct ldb_val *omd_value;
870 struct replPropertyMetaDataBlob nmd;
871 struct ldb_val nmd_value;
872 uint32_t i,j,ni=0;
873 uint32_t removed_attrs = 0;
874 uint64_t seq_num;
875 int ret;
877 msg = ar->objs->objects[ar->index_current].msg;
878 rmd = ar->objs->objects[ar->index_current].meta_data;
879 ZERO_STRUCT(omd);
880 omd.version = 1;
883 * TODO: add rename conflict handling
885 if (ldb_dn_compare(msg->dn, ar->sub.search_msg->dn) != 0) {
886 ldb_debug_set(ar->module->ldb, LDB_DEBUG_FATAL, "replmd_replicated_apply_merge[%u]: rename not supported",
887 ar->index_current);
888 ldb_debug(ar->module->ldb, LDB_DEBUG_FATAL, "%s => %s\n",
889 ldb_dn_get_linearized(ar->sub.search_msg->dn),
890 ldb_dn_get_linearized(msg->dn));
891 return replmd_replicated_request_werror(ar, WERR_NOT_SUPPORTED);
894 ret = ldb_sequence_number(ar->module->ldb, LDB_SEQ_NEXT, &seq_num);
895 if (ret != LDB_SUCCESS) {
896 return replmd_replicated_request_error(ar, ret);
899 /* find existing meta data */
900 omd_value = ldb_msg_find_ldb_val(ar->sub.search_msg, "replPropertyMetaData");
901 if (omd_value) {
902 nt_status = ndr_pull_struct_blob(omd_value, ar->sub.mem_ctx, &omd,
903 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
904 if (!NT_STATUS_IS_OK(nt_status)) {
905 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
908 if (omd.version != 1) {
909 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
913 ZERO_STRUCT(nmd);
914 nmd.version = 1;
915 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
916 nmd.ctr.ctr1.array = talloc_array(ar->sub.mem_ctx,
917 struct replPropertyMetaData1,
918 nmd.ctr.ctr1.count);
919 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOMEM);
921 /* first copy the old meta data */
922 for (i=0; i < omd.ctr.ctr1.count; i++) {
923 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
924 ni++;
927 /* now merge in the new meta data */
928 for (i=0; i < rmd->ctr.ctr1.count; i++) {
929 bool found = false;
931 rmd->ctr.ctr1.array[i].local_usn = seq_num;
933 for (j=0; j < ni; j++) {
934 int cmp;
936 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
937 continue;
940 cmp = replmd_replPropertyMetaData1_conflict_compare(&rmd->ctr.ctr1.array[i],
941 &nmd.ctr.ctr1.array[j]);
942 if (cmp > 0) {
943 /* replace the entry */
944 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
945 found = true;
946 break;
949 /* we don't want to apply this change so remove the attribute */
950 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
951 removed_attrs++;
953 found = true;
954 break;
957 if (found) continue;
959 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
960 ni++;
964 * finally correct the size of the meta_data array
966 nmd.ctr.ctr1.count = ni;
969 * the rdn attribute (the alias for the name attribute),
970 * 'cn' for most objects is the last entry in the meta data array
971 * we have stored
973 * sort the new meta data array
976 struct replPropertyMetaData1 *rdn_p;
977 uint32_t rdn_idx = omd.ctr.ctr1.count - 1;
979 rdn_p = &nmd.ctr.ctr1.array[rdn_idx];
980 replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, &rdn_p->attid);
983 /* create the meta data value */
984 nt_status = ndr_push_struct_blob(&nmd_value, msg, &nmd,
985 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
986 if (!NT_STATUS_IS_OK(nt_status)) {
987 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
991 * check if some replicated attributes left, otherwise skip the ldb_modify() call
993 if (msg->num_elements == 0) {
994 ldb_debug(ar->module->ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
995 ar->index_current);
996 goto next_object;
999 ldb_debug(ar->module->ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
1000 ar->index_current, msg->num_elements);
1003 * when we now that we'll modify the record, add the whenChanged, uSNChanged
1004 * and replPopertyMetaData attributes
1006 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
1007 if (ret != LDB_SUCCESS) {
1008 return replmd_replicated_request_error(ar, ret);
1010 ret = samdb_msg_add_uint64(ar->module->ldb, msg, msg, "uSNChanged", seq_num);
1011 if (ret != LDB_SUCCESS) {
1012 return replmd_replicated_request_error(ar, ret);
1014 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
1015 if (ret != LDB_SUCCESS) {
1016 return replmd_replicated_request_error(ar, ret);
1019 replmd_ldb_message_sort(msg, ar->schema);
1021 /* we want to replace the old values */
1022 for (i=0; i < msg->num_elements; i++) {
1023 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
1026 ret = ldb_build_mod_req(&ar->sub.change_req,
1027 ar->module->ldb,
1028 ar->sub.mem_ctx,
1029 msg,
1030 NULL,
1032 replmd_replicated_apply_merge_callback);
1033 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1035 #ifdef REPLMD_FULL_ASYNC /* TODO: active this code when ldb support full async code */
1036 return ldb_next_request(ar->module, ar->sub.change_req);
1037 #else
1038 ret = ldb_next_request(ar->module, ar->sub.change_req);
1039 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1041 ar->sub.change_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
1042 if (ar->sub.change_ret != LDB_SUCCESS) {
1043 return replmd_replicated_request_error(ar, ar->sub.change_ret);
1046 next_object:
1047 talloc_free(ar->sub.mem_ctx);
1048 ZERO_STRUCT(ar->sub);
1050 ar->index_current++;
1052 return LDB_SUCCESS;
1053 #endif
1056 static int replmd_replicated_apply_search_callback(struct ldb_context *ldb,
1057 void *private_data,
1058 struct ldb_reply *ares)
1060 struct replmd_replicated_request *ar = talloc_get_type(private_data,
1061 struct replmd_replicated_request);
1062 bool is_done = false;
1064 switch (ares->type) {
1065 case LDB_REPLY_ENTRY:
1066 ar->sub.search_msg = talloc_steal(ar->sub.mem_ctx, ares->message);
1067 break;
1068 case LDB_REPLY_REFERRAL:
1069 /* we ignore referrals */
1070 break;
1071 case LDB_REPLY_EXTENDED:
1072 case LDB_REPLY_DONE:
1073 is_done = true;
1076 talloc_free(ares);
1078 #ifdef REPLMD_FULL_ASYNC /* TODO: active this code when ldb support full async code */
1079 if (is_done) {
1080 ar->sub.search_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
1081 if (ar->sub.search_ret != LDB_SUCCESS) {
1082 return replmd_replicated_request_error(ar, ar->sub.search_ret);
1084 if (ar->sub.search_msg) {
1085 return replmd_replicated_apply_merge(ar);
1087 return replmd_replicated_apply_add(ar);
1089 #endif
1090 return LDB_SUCCESS;
1093 static int replmd_replicated_apply_search(struct replmd_replicated_request *ar)
1095 int ret;
1096 char *tmp_str;
1097 char *filter;
1099 tmp_str = ldb_binary_encode(ar->sub.mem_ctx, ar->objs->objects[ar->index_current].guid_value);
1100 if (!tmp_str) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1102 filter = talloc_asprintf(ar->sub.mem_ctx, "(objectGUID=%s)", tmp_str);
1103 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1104 talloc_free(tmp_str);
1106 ret = ldb_build_search_req(&ar->sub.search_req,
1107 ar->module->ldb,
1108 ar->sub.mem_ctx,
1109 ar->objs->partition_dn,
1110 LDB_SCOPE_SUBTREE,
1111 filter,
1112 NULL,
1113 NULL,
1115 replmd_replicated_apply_search_callback);
1116 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1118 #ifdef REPLMD_FULL_ASYNC /* TODO: active this code when ldb support full async code */
1119 return ldb_next_request(ar->module, ar->sub.search_req);
1120 #else
1121 ret = ldb_next_request(ar->module, ar->sub.search_req);
1122 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1124 ar->sub.search_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
1125 if (ar->sub.search_ret != LDB_SUCCESS) {
1126 return replmd_replicated_request_error(ar, ar->sub.search_ret);
1128 if (ar->sub.search_msg) {
1129 return replmd_replicated_apply_merge(ar);
1132 return replmd_replicated_apply_add(ar);
1133 #endif
1136 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
1138 #ifdef REPLMD_FULL_ASYNC /* TODO: active this code when ldb support full async code */
1139 if (ar->index_current >= ar->objs->num_objects) {
1140 return replmd_replicated_uptodate_vector(ar);
1142 #endif
1144 ar->sub.mem_ctx = talloc_new(ar);
1145 if (!ar->sub.mem_ctx) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1147 return replmd_replicated_apply_search(ar);
1150 static int replmd_replicated_uptodate_modify_callback(struct ldb_context *ldb,
1151 void *private_data,
1152 struct ldb_reply *ares)
1154 #ifdef REPLMD_FULL_ASYNC /* TODO: active this code when ldb support full async code */
1155 struct replmd_replicated_request *ar = talloc_get_type(private_data,
1156 struct replmd_replicated_request);
1158 ar->sub.change_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
1159 if (ar->sub.change_ret != LDB_SUCCESS) {
1160 return replmd_replicated_request_error(ar, ar->sub.change_ret);
1163 talloc_free(ar->sub.mem_ctx);
1164 ZERO_STRUCT(ar->sub);
1166 return replmd_replicated_request_done(ar);
1167 #else
1168 return LDB_SUCCESS;
1169 #endif
1172 static int replmd_drsuapi_DsReplicaCursor2_compare(const struct drsuapi_DsReplicaCursor2 *c1,
1173 const struct drsuapi_DsReplicaCursor2 *c2)
1175 return GUID_compare(&c1->source_dsa_invocation_id, &c2->source_dsa_invocation_id);
1178 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
1180 NTSTATUS nt_status;
1181 struct ldb_message *msg;
1182 struct replUpToDateVectorBlob ouv;
1183 const struct ldb_val *ouv_value;
1184 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
1185 struct replUpToDateVectorBlob nuv;
1186 struct ldb_val nuv_value;
1187 struct ldb_message_element *nuv_el = NULL;
1188 const struct GUID *our_invocation_id;
1189 struct ldb_message_element *orf_el = NULL;
1190 struct repsFromToBlob nrf;
1191 struct ldb_val *nrf_value = NULL;
1192 struct ldb_message_element *nrf_el = NULL;
1193 uint32_t i,j,ni=0;
1194 uint64_t seq_num;
1195 bool found = false;
1196 time_t t = time(NULL);
1197 NTTIME now;
1198 int ret;
1200 ruv = ar->objs->uptodateness_vector;
1201 ZERO_STRUCT(ouv);
1202 ouv.version = 2;
1203 ZERO_STRUCT(nuv);
1204 nuv.version = 2;
1206 unix_to_nt_time(&now, t);
1209 * we use the next sequence number for our own highest_usn
1210 * because we will do a modify request and this will increment
1211 * our highest_usn
1213 ret = ldb_sequence_number(ar->module->ldb, LDB_SEQ_NEXT, &seq_num);
1214 if (ret != LDB_SUCCESS) {
1215 return replmd_replicated_request_error(ar, ret);
1219 * first create the new replUpToDateVector
1221 ouv_value = ldb_msg_find_ldb_val(ar->sub.search_msg, "replUpToDateVector");
1222 if (ouv_value) {
1223 nt_status = ndr_pull_struct_blob(ouv_value, ar->sub.mem_ctx, &ouv,
1224 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
1225 if (!NT_STATUS_IS_OK(nt_status)) {
1226 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
1229 if (ouv.version != 2) {
1230 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
1235 * the new uptodateness vector will at least
1236 * contain 1 entry, one for the source_dsa
1238 * plus optional values from our old vector and the one from the source_dsa
1240 nuv.ctr.ctr2.count = 1 + ouv.ctr.ctr2.count;
1241 if (ruv) nuv.ctr.ctr2.count += ruv->count;
1242 nuv.ctr.ctr2.cursors = talloc_array(ar->sub.mem_ctx,
1243 struct drsuapi_DsReplicaCursor2,
1244 nuv.ctr.ctr2.count);
1245 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1247 /* first copy the old vector */
1248 for (i=0; i < ouv.ctr.ctr2.count; i++) {
1249 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
1250 ni++;
1253 /* get our invocation_id if we have one already attached to the ldb */
1254 our_invocation_id = samdb_ntds_invocation_id(ar->module->ldb);
1256 /* merge in the source_dsa vector is available */
1257 for (i=0; (ruv && i < ruv->count); i++) {
1258 found = false;
1260 if (our_invocation_id &&
1261 GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
1262 our_invocation_id)) {
1263 continue;
1266 for (j=0; j < ni; j++) {
1267 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
1268 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
1269 continue;
1272 found = true;
1275 * we update only the highest_usn and not the latest_sync_success time,
1276 * because the last success stands for direct replication
1278 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
1279 nuv.ctr.ctr2.cursors[j].highest_usn = ruv->cursors[i].highest_usn;
1281 break;
1284 if (found) continue;
1286 /* if it's not there yet, add it */
1287 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
1288 ni++;
1292 * merge in the current highwatermark for the source_dsa
1294 found = false;
1295 for (j=0; j < ni; j++) {
1296 if (!GUID_equal(&ar->objs->source_dsa->source_dsa_invocation_id,
1297 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
1298 continue;
1301 found = true;
1304 * here we update the highest_usn and last_sync_success time
1305 * because we're directly replicating from the source_dsa
1307 * and use the tmp_highest_usn because this is what we have just applied
1308 * to our ldb
1310 nuv.ctr.ctr2.cursors[j].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
1311 nuv.ctr.ctr2.cursors[j].last_sync_success = now;
1312 break;
1314 if (!found) {
1316 * here we update the highest_usn and last_sync_success time
1317 * because we're directly replicating from the source_dsa
1319 * and use the tmp_highest_usn because this is what we have just applied
1320 * to our ldb
1322 nuv.ctr.ctr2.cursors[ni].source_dsa_invocation_id= ar->objs->source_dsa->source_dsa_invocation_id;
1323 nuv.ctr.ctr2.cursors[ni].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
1324 nuv.ctr.ctr2.cursors[ni].last_sync_success = now;
1325 ni++;
1329 * finally correct the size of the cursors array
1331 nuv.ctr.ctr2.count = ni;
1334 * sort the cursors
1336 qsort(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count,
1337 sizeof(struct drsuapi_DsReplicaCursor2),
1338 (comparison_fn_t)replmd_drsuapi_DsReplicaCursor2_compare);
1341 * create the change ldb_message
1343 msg = ldb_msg_new(ar->sub.mem_ctx);
1344 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1345 msg->dn = ar->sub.search_msg->dn;
1347 nt_status = ndr_push_struct_blob(&nuv_value, msg, &nuv,
1348 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
1349 if (!NT_STATUS_IS_OK(nt_status)) {
1350 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
1352 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
1353 if (ret != LDB_SUCCESS) {
1354 return replmd_replicated_request_error(ar, ret);
1356 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
1359 * now create the new repsFrom value from the given repsFromTo1 structure
1361 ZERO_STRUCT(nrf);
1362 nrf.version = 1;
1363 nrf.ctr.ctr1 = *ar->objs->source_dsa;
1364 /* and fix some values... */
1365 nrf.ctr.ctr1.consecutive_sync_failures = 0;
1366 nrf.ctr.ctr1.last_success = now;
1367 nrf.ctr.ctr1.last_attempt = now;
1368 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
1369 nrf.ctr.ctr1.highwatermark.highest_usn = nrf.ctr.ctr1.highwatermark.tmp_highest_usn;
1372 * first see if we already have a repsFrom value for the current source dsa
1373 * if so we'll later replace this value
1375 orf_el = ldb_msg_find_element(ar->sub.search_msg, "repsFrom");
1376 if (orf_el) {
1377 for (i=0; i < orf_el->num_values; i++) {
1378 struct repsFromToBlob *trf;
1380 trf = talloc(ar->sub.mem_ctx, struct repsFromToBlob);
1381 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1383 nt_status = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
1384 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
1385 if (!NT_STATUS_IS_OK(nt_status)) {
1386 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
1389 if (trf->version != 1) {
1390 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
1394 * we compare the source dsa objectGUID not the invocation_id
1395 * because we want only one repsFrom value per source dsa
1396 * and when the invocation_id of the source dsa has changed we don't need
1397 * the old repsFrom with the old invocation_id
1399 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
1400 &ar->objs->source_dsa->source_dsa_obj_guid)) {
1401 talloc_free(trf);
1402 continue;
1405 talloc_free(trf);
1406 nrf_value = &orf_el->values[i];
1407 break;
1411 * copy over all old values to the new ldb_message
1413 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
1414 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1415 *nrf_el = *orf_el;
1419 * if we haven't found an old repsFrom value for the current source dsa
1420 * we'll add a new value
1422 if (!nrf_value) {
1423 struct ldb_val zero_value;
1424 ZERO_STRUCT(zero_value);
1425 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
1426 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1428 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
1431 /* we now fill the value which is already attached to ldb_message */
1432 nt_status = ndr_push_struct_blob(nrf_value, msg, &nrf,
1433 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
1434 if (!NT_STATUS_IS_OK(nt_status)) {
1435 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
1439 * the ldb_message_element for the attribute, has all the old values and the new one
1440 * so we'll replace the whole attribute with all values
1442 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
1444 /* prepare the ldb_modify() request */
1445 ret = ldb_build_mod_req(&ar->sub.change_req,
1446 ar->module->ldb,
1447 ar->sub.mem_ctx,
1448 msg,
1449 NULL,
1451 replmd_replicated_uptodate_modify_callback);
1452 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1454 #ifdef REPLMD_FULL_ASYNC /* TODO: active this code when ldb support full async code */
1455 return ldb_next_request(ar->module, ar->sub.change_req);
1456 #else
1457 ret = ldb_next_request(ar->module, ar->sub.change_req);
1458 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1460 ar->sub.change_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
1461 if (ar->sub.change_ret != LDB_SUCCESS) {
1462 return replmd_replicated_request_error(ar, ar->sub.change_ret);
1465 talloc_free(ar->sub.mem_ctx);
1466 ZERO_STRUCT(ar->sub);
1468 return replmd_replicated_request_done(ar);
1469 #endif
1472 static int replmd_replicated_uptodate_search_callback(struct ldb_context *ldb,
1473 void *private_data,
1474 struct ldb_reply *ares)
1476 struct replmd_replicated_request *ar = talloc_get_type(private_data,
1477 struct replmd_replicated_request);
1478 bool is_done = false;
1480 switch (ares->type) {
1481 case LDB_REPLY_ENTRY:
1482 ar->sub.search_msg = talloc_steal(ar->sub.mem_ctx, ares->message);
1483 break;
1484 case LDB_REPLY_REFERRAL:
1485 /* we ignore referrals */
1486 break;
1487 case LDB_REPLY_EXTENDED:
1488 case LDB_REPLY_DONE:
1489 is_done = true;
1492 talloc_free(ares);
1494 #ifdef REPLMD_FULL_ASYNC /* TODO: active this code when ldb support full async code */
1495 if (is_done) {
1496 ar->sub.search_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
1497 if (ar->sub.search_ret != LDB_SUCCESS) {
1498 return replmd_replicated_request_error(ar, ar->sub.search_ret);
1500 if (!ar->sub.search_msg) {
1501 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
1504 return replmd_replicated_uptodate_modify(ar);
1506 #endif
1507 return LDB_SUCCESS;
1510 static int replmd_replicated_uptodate_search(struct replmd_replicated_request *ar)
1512 int ret;
1513 static const char *attrs[] = {
1514 "replUpToDateVector",
1515 "repsFrom",
1516 NULL
1519 ret = ldb_build_search_req(&ar->sub.search_req,
1520 ar->module->ldb,
1521 ar->sub.mem_ctx,
1522 ar->objs->partition_dn,
1523 LDB_SCOPE_BASE,
1524 "(objectClass=*)",
1525 attrs,
1526 NULL,
1528 replmd_replicated_uptodate_search_callback);
1529 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1531 #ifdef REPLMD_FULL_ASYNC /* TODO: active this code when ldb support full async code */
1532 return ldb_next_request(ar->module, ar->sub.search_req);
1533 #else
1534 ret = ldb_next_request(ar->module, ar->sub.search_req);
1535 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1537 ar->sub.search_ret = ldb_wait(ar->sub.search_req->handle, LDB_WAIT_ALL);
1538 if (ar->sub.search_ret != LDB_SUCCESS) {
1539 return replmd_replicated_request_error(ar, ar->sub.search_ret);
1541 if (!ar->sub.search_msg) {
1542 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
1545 return replmd_replicated_uptodate_modify(ar);
1546 #endif
1549 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
1551 ar->sub.mem_ctx = talloc_new(ar);
1552 if (!ar->sub.mem_ctx) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1554 return replmd_replicated_uptodate_search(ar);
1557 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
1559 struct dsdb_extended_replicated_objects *objs;
1560 struct replmd_replicated_request *ar;
1562 ldb_debug(module->ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
1564 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
1565 if (!objs) {
1566 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
1567 return LDB_ERR_PROTOCOL_ERROR;
1570 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
1571 ldb_debug(module->ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
1572 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
1573 return LDB_ERR_PROTOCOL_ERROR;
1576 ar = replmd_replicated_init_handle(module, req, objs);
1577 if (!ar) {
1578 return LDB_ERR_OPERATIONS_ERROR;
1581 #ifdef REPLMD_FULL_ASYNC /* TODO: active this code when ldb support full async code */
1582 return replmd_replicated_apply_next(ar);
1583 #else
1584 while (ar->index_current < ar->objs->num_objects &&
1585 req->handle->state != LDB_ASYNC_DONE) {
1586 replmd_replicated_apply_next(ar);
1589 if (req->handle->state != LDB_ASYNC_DONE) {
1590 replmd_replicated_uptodate_vector(ar);
1593 return LDB_SUCCESS;
1594 #endif
1597 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
1599 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
1600 return replmd_extended_replicated_objects(module, req);
1603 return ldb_next_request(module, req);
1606 static int replmd_wait_none(struct ldb_handle *handle) {
1607 struct replmd_replicated_request *ar;
1609 if (!handle || !handle->private_data) {
1610 return LDB_ERR_OPERATIONS_ERROR;
1613 ar = talloc_get_type(handle->private_data, struct replmd_replicated_request);
1614 if (!ar) {
1615 return LDB_ERR_OPERATIONS_ERROR;
1618 /* we do only sync calls */
1619 if (handle->state != LDB_ASYNC_DONE) {
1620 return LDB_ERR_OPERATIONS_ERROR;
1623 return handle->status;
1626 static int replmd_wait_all(struct ldb_handle *handle) {
1628 int ret;
1630 while (handle->state != LDB_ASYNC_DONE) {
1631 ret = replmd_wait_none(handle);
1632 if (ret != LDB_SUCCESS) {
1633 return ret;
1637 return handle->status;
1640 static int replmd_wait(struct ldb_handle *handle, enum ldb_wait_type type)
1642 if (type == LDB_WAIT_ALL) {
1643 return replmd_wait_all(handle);
1644 } else {
1645 return replmd_wait_none(handle);
1649 static const struct ldb_module_ops replmd_ops = {
1650 .name = "repl_meta_data",
1651 .add = replmd_add,
1652 .modify = replmd_modify,
1653 .extended = replmd_extended,
1654 .wait = replmd_wait
1657 int repl_meta_data_module_init(void)
1659 return ldb_register_module(&replmd_ops);