s4:dsdb Pass down the exact error code on failure in repl_meta_data
[Samba.git] / source4 / dsdb / samdb / ldb_modules / repl_meta_data.c
blob59dc2ec928690a27758996ead46486b28848b69b
1 /*
2 ldb database library
4 Copyright (C) Simo Sorce 2004-2008
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 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 * Name: ldb
26 * Component: ldb repl_meta_data module
28 * Description: - add a unique objectGUID onto every new record,
29 * - handle whenCreated, whenChanged timestamps
30 * - handle uSNCreated, uSNChanged numbers
31 * - handle replPropertyMetaData attribute
33 * Author: Simo Sorce
34 * Author: Stefan Metzmacher
37 #include "includes.h"
38 #include "ldb_module.h"
39 #include "dsdb/samdb/samdb.h"
40 #include "dsdb/common/proto.h"
41 #include "../libds/common/flags.h"
42 #include "librpc/gen_ndr/ndr_misc.h"
43 #include "librpc/gen_ndr/ndr_drsuapi.h"
44 #include "librpc/gen_ndr/ndr_drsblobs.h"
45 #include "param/param.h"
46 #include "libcli/security/dom_sid.h"
47 #include "lib/util/dlinklist.h"
49 struct replmd_private {
50 TALLOC_CTX *la_ctx;
51 struct la_entry *la_list;
52 uint32_t num_ncs;
53 struct nc_entry {
54 struct ldb_dn *dn;
55 struct GUID guid;
56 uint64_t mod_usn;
57 } *ncs;
60 struct la_entry {
61 struct la_entry *next, *prev;
62 struct drsuapi_DsReplicaLinkedAttribute *la;
65 struct replmd_replicated_request {
66 struct ldb_module *module;
67 struct ldb_request *req;
69 const struct dsdb_schema *schema;
71 struct dsdb_extended_replicated_objects *objs;
73 /* the controls we pass down */
74 struct ldb_control **controls;
76 uint32_t index_current;
78 struct ldb_message *search_msg;
83 initialise the module
84 allocate the private structure and build the list
85 of partition DNs for use by replmd_notify()
87 static int replmd_init(struct ldb_module *module)
89 struct replmd_private *replmd_private;
90 struct ldb_context *ldb = ldb_module_get_ctx(module);
92 replmd_private = talloc_zero(module, struct replmd_private);
93 if (replmd_private == NULL) {
94 ldb_oom(ldb);
95 return LDB_ERR_OPERATIONS_ERROR;
97 ldb_module_set_private(module, replmd_private);
99 return ldb_next_init(module);
103 static int nc_compare(struct nc_entry *n1, struct nc_entry *n2)
105 return ldb_dn_compare(n1->dn, n2->dn);
109 build the list of partition DNs for use by replmd_notify()
111 static int replmd_load_NCs(struct ldb_module *module)
113 const char *attrs[] = { "namingContexts", NULL };
114 struct ldb_result *res = NULL;
115 int i, ret;
116 TALLOC_CTX *tmp_ctx;
117 struct ldb_context *ldb;
118 struct ldb_message_element *el;
119 struct replmd_private *replmd_private =
120 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
122 if (replmd_private->ncs != NULL) {
123 return LDB_SUCCESS;
126 ldb = ldb_module_get_ctx(module);
127 tmp_ctx = talloc_new(module);
129 /* load the list of naming contexts */
130 ret = ldb_search(ldb, tmp_ctx, &res, ldb_dn_new(tmp_ctx, ldb, ""),
131 LDB_SCOPE_BASE, attrs, NULL);
132 if (ret != LDB_SUCCESS ||
133 res->count != 1) {
134 DEBUG(0,(__location__ ": Failed to load rootDSE\n"));
135 return LDB_ERR_OPERATIONS_ERROR;
138 el = ldb_msg_find_element(res->msgs[0], "namingContexts");
139 if (el == NULL) {
140 DEBUG(0,(__location__ ": Failed to load namingContexts\n"));
141 return LDB_ERR_OPERATIONS_ERROR;
144 replmd_private->num_ncs = el->num_values;
145 replmd_private->ncs = talloc_array(replmd_private, struct nc_entry,
146 replmd_private->num_ncs);
147 if (replmd_private->ncs == NULL) {
148 ldb_oom(ldb);
149 return LDB_ERR_OPERATIONS_ERROR;
152 for (i=0; i<replmd_private->num_ncs; i++) {
153 replmd_private->ncs[i].dn =
154 ldb_dn_from_ldb_val(replmd_private->ncs,
155 ldb, &el->values[i]);
156 replmd_private->ncs[i].mod_usn = 0;
159 talloc_free(res);
161 /* now find the GUIDs of each of those DNs */
162 for (i=0; i<replmd_private->num_ncs; i++) {
163 const char *attrs2[] = { "objectGUID", NULL };
164 ret = ldb_search(ldb, tmp_ctx, &res, replmd_private->ncs[i].dn,
165 LDB_SCOPE_BASE, attrs2, NULL);
166 if (ret != LDB_SUCCESS ||
167 res->count != 1) {
168 /* this happens when the schema is first being
169 setup */
170 talloc_free(replmd_private->ncs);
171 replmd_private->ncs = NULL;
172 replmd_private->num_ncs = 0;
173 talloc_free(tmp_ctx);
174 return LDB_SUCCESS;
176 replmd_private->ncs[i].guid =
177 samdb_result_guid(res->msgs[0], "objectGUID");
178 talloc_free(res);
181 /* sort the NCs into order, most to least specific */
182 qsort(replmd_private->ncs, replmd_private->num_ncs,
183 sizeof(replmd_private->ncs[0]), QSORT_CAST nc_compare);
186 talloc_free(tmp_ctx);
188 return LDB_SUCCESS;
193 * notify the repl task that a object has changed. The notifies are
194 * gathered up in the replmd_private structure then written to the
195 * @REPLCHANGED object in each partition during the prepare_commit
197 static int replmd_notify(struct ldb_module *module, struct ldb_dn *dn, uint64_t uSN)
199 int ret, i;
200 struct replmd_private *replmd_private =
201 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
203 ret = replmd_load_NCs(module);
204 if (ret != LDB_SUCCESS) {
205 return ret;
207 if (replmd_private->num_ncs == 0) {
208 return LDB_SUCCESS;
211 for (i=0; i<replmd_private->num_ncs; i++) {
212 if (ldb_dn_compare_base(replmd_private->ncs[i].dn, dn) == 0) {
213 break;
216 if (i == replmd_private->num_ncs) {
217 DEBUG(0,(__location__ ": DN not within known NCs '%s'\n",
218 ldb_dn_get_linearized(dn)));
219 return LDB_ERR_OPERATIONS_ERROR;
222 if (uSN > replmd_private->ncs[i].mod_usn) {
223 replmd_private->ncs[i].mod_usn = uSN;
226 return LDB_SUCCESS;
231 * update a @REPLCHANGED record in each partition if there have been
232 * any writes of replicated data in the partition
234 static int replmd_notify_store(struct ldb_module *module)
236 int i;
237 struct replmd_private *replmd_private =
238 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
239 struct ldb_context *ldb = ldb_module_get_ctx(module);
241 for (i=0; i<replmd_private->num_ncs; i++) {
242 int ret;
244 if (replmd_private->ncs[i].mod_usn == 0) {
245 /* this partition has not changed in this
246 transaction */
247 continue;
250 ret = dsdb_save_partition_usn(ldb, replmd_private->ncs[i].dn,
251 replmd_private->ncs[i].mod_usn);
252 if (ret != LDB_SUCCESS) {
253 DEBUG(0,(__location__ ": Failed to save partition uSN for %s\n",
254 ldb_dn_get_linearized(replmd_private->ncs[i].dn)));
255 return ret;
259 return LDB_SUCCESS;
264 created a replmd_replicated_request context
266 static struct replmd_replicated_request *replmd_ctx_init(struct ldb_module *module,
267 struct ldb_request *req)
269 struct ldb_context *ldb;
270 struct replmd_replicated_request *ac;
272 ldb = ldb_module_get_ctx(module);
274 ac = talloc_zero(req, struct replmd_replicated_request);
275 if (ac == NULL) {
276 ldb_oom(ldb);
277 return NULL;
280 ac->module = module;
281 ac->req = req;
282 return ac;
286 add a time element to a record
288 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
290 struct ldb_message_element *el;
291 char *s;
293 if (ldb_msg_find_element(msg, attr) != NULL) {
294 return LDB_SUCCESS;
297 s = ldb_timestring(msg, t);
298 if (s == NULL) {
299 return LDB_ERR_OPERATIONS_ERROR;
302 if (ldb_msg_add_string(msg, attr, s) != LDB_SUCCESS) {
303 return LDB_ERR_OPERATIONS_ERROR;
306 el = ldb_msg_find_element(msg, attr);
307 /* always set as replace. This works because on add ops, the flag
308 is ignored */
309 el->flags = LDB_FLAG_MOD_REPLACE;
311 return LDB_SUCCESS;
315 add a uint64_t element to a record
317 static int add_uint64_element(struct ldb_message *msg, const char *attr, uint64_t v)
319 struct ldb_message_element *el;
321 if (ldb_msg_find_element(msg, attr) != NULL) {
322 return LDB_SUCCESS;
325 if (ldb_msg_add_fmt(msg, attr, "%llu", (unsigned long long)v) != LDB_SUCCESS) {
326 return LDB_ERR_OPERATIONS_ERROR;
329 el = ldb_msg_find_element(msg, attr);
330 /* always set as replace. This works because on add ops, the flag
331 is ignored */
332 el->flags = LDB_FLAG_MOD_REPLACE;
334 return LDB_SUCCESS;
337 static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1 *m1,
338 const struct replPropertyMetaData1 *m2,
339 const uint32_t *rdn_attid)
341 if (m1->attid == m2->attid) {
342 return 0;
346 * the rdn attribute should be at the end!
347 * so we need to return a value greater than zero
348 * which means m1 is greater than m2
350 if (m1->attid == *rdn_attid) {
351 return 1;
355 * the rdn attribute should be at the end!
356 * so we need to return a value less than zero
357 * which means m2 is greater than m1
359 if (m2->attid == *rdn_attid) {
360 return -1;
363 return m1->attid - m2->attid;
366 static int replmd_replPropertyMetaDataCtr1_sort(struct replPropertyMetaDataCtr1 *ctr1,
367 const struct dsdb_schema *schema,
368 struct ldb_dn *dn)
370 const char *rdn_name;
371 const struct dsdb_attribute *rdn_sa;
373 rdn_name = ldb_dn_get_rdn_name(dn);
374 if (!rdn_name) {
375 DEBUG(0,(__location__ ": No rDN for %s?\n", ldb_dn_get_linearized(dn)));
376 return LDB_ERR_OPERATIONS_ERROR;
379 rdn_sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
380 if (rdn_sa == NULL) {
381 DEBUG(0,(__location__ ": No sa found for rDN %s for %s\n", rdn_name, ldb_dn_get_linearized(dn)));
382 return LDB_ERR_OPERATIONS_ERROR;
385 DEBUG(6,("Sorting rpmd with attid exception %u rDN=%s DN=%s\n",
386 rdn_sa->attributeID_id, rdn_name, ldb_dn_get_linearized(dn)));
388 ldb_qsort(ctr1->array, ctr1->count, sizeof(struct replPropertyMetaData1),
389 discard_const_p(void, &rdn_sa->attributeID_id),
390 (ldb_qsort_cmp_fn_t)replmd_replPropertyMetaData1_attid_sort);
392 return LDB_SUCCESS;
395 static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1,
396 const struct ldb_message_element *e2,
397 const struct dsdb_schema *schema)
399 const struct dsdb_attribute *a1;
400 const struct dsdb_attribute *a2;
403 * TODO: make this faster by caching the dsdb_attribute pointer
404 * on the ldb_messag_element
407 a1 = dsdb_attribute_by_lDAPDisplayName(schema, e1->name);
408 a2 = dsdb_attribute_by_lDAPDisplayName(schema, e2->name);
411 * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
412 * in the schema
414 if (!a1 || !a2) {
415 return strcasecmp(e1->name, e2->name);
418 return a1->attributeID_id - a2->attributeID_id;
421 static void replmd_ldb_message_sort(struct ldb_message *msg,
422 const struct dsdb_schema *schema)
424 ldb_qsort(msg->elements, msg->num_elements, sizeof(struct ldb_message_element),
425 discard_const_p(void, schema), (ldb_qsort_cmp_fn_t)replmd_ldb_message_element_attid_sort);
428 static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
430 struct ldb_context *ldb;
431 struct replmd_replicated_request *ac;
433 ac = talloc_get_type(req->context, struct replmd_replicated_request);
434 ldb = ldb_module_get_ctx(ac->module);
436 if (!ares) {
437 return ldb_module_done(ac->req, NULL, NULL,
438 LDB_ERR_OPERATIONS_ERROR);
440 if (ares->error != LDB_SUCCESS) {
441 return ldb_module_done(ac->req, ares->controls,
442 ares->response, ares->error);
445 if (ares->type != LDB_REPLY_DONE) {
446 ldb_set_errstring(ldb,
447 "invalid ldb_reply_type in callback");
448 talloc_free(ares);
449 return ldb_module_done(ac->req, NULL, NULL,
450 LDB_ERR_OPERATIONS_ERROR);
453 return ldb_module_done(ac->req, ares->controls,
454 ares->response, LDB_SUCCESS);
457 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
459 struct ldb_context *ldb;
460 struct ldb_control *control;
461 struct ldb_control **saved_controls;
462 struct replmd_replicated_request *ac;
463 const struct dsdb_schema *schema;
464 enum ndr_err_code ndr_err;
465 struct ldb_request *down_req;
466 struct ldb_message *msg;
467 const DATA_BLOB *guid_blob;
468 struct GUID guid;
469 struct ldb_val guid_value;
470 struct replPropertyMetaDataBlob nmd;
471 struct ldb_val nmd_value;
472 uint64_t seq_num;
473 const struct GUID *our_invocation_id;
474 time_t t = time(NULL);
475 NTTIME now;
476 char *time_str;
477 int ret;
478 uint32_t i, ni=0;
479 int allow_add_guid=0;
480 int remove_current_guid=0;
482 /* check if there's a show deleted control */
483 control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID);
484 if (control) {
485 allow_add_guid = 1;
488 /* do not manipulate our control entries */
489 if (ldb_dn_is_special(req->op.add.message->dn)) {
490 return ldb_next_request(module, req);
493 ldb = ldb_module_get_ctx(module);
495 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_add\n");
497 schema = dsdb_get_schema(ldb);
498 if (!schema) {
499 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
500 "replmd_add: no dsdb_schema loaded");
501 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
502 return LDB_ERR_CONSTRAINT_VIOLATION;
505 ac = replmd_ctx_init(module, req);
506 if (!ac) {
507 return LDB_ERR_OPERATIONS_ERROR;
510 ac->schema = schema;
512 guid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectGUID");
513 if ( guid_blob != NULL ) {
514 if( !allow_add_guid ) {
515 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
516 "replmd_add: it's not allowed to add an object with objectGUID\n");
517 return LDB_ERR_UNWILLING_TO_PERFORM;
518 } else {
519 NTSTATUS status = GUID_from_data_blob(guid_blob,&guid);
520 if ( !NT_STATUS_IS_OK(status)) {
521 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
522 "replmd_add: Unable to parse as a GUID the attribute objectGUID\n");
523 return LDB_ERR_UNWILLING_TO_PERFORM;
525 /* we remove this attribute as it can be a string and will not be treated
526 correctly and then we will readd it latter on in the good format*/
527 remove_current_guid = 1;
529 } else {
530 /* a new GUID */
531 guid = GUID_random();
534 /* Get a sequence number from the backend */
535 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
536 if (ret != LDB_SUCCESS) {
537 return ret;
541 /* get our invocationId */
542 our_invocation_id = samdb_ntds_invocation_id(ldb);
543 if (!our_invocation_id) {
544 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
545 "replmd_add: unable to find invocationId\n");
546 return LDB_ERR_OPERATIONS_ERROR;
549 /* we have to copy the message as the caller might have it as a const */
550 msg = ldb_msg_copy_shallow(ac, req->op.add.message);
551 if (msg == NULL) {
552 ldb_oom(ldb);
553 return LDB_ERR_OPERATIONS_ERROR;
556 /* generated times */
557 unix_to_nt_time(&now, t);
558 time_str = ldb_timestring(msg, t);
559 if (!time_str) {
560 return LDB_ERR_OPERATIONS_ERROR;
562 if (remove_current_guid) {
563 ldb_msg_remove_attr(msg,"objectGUID");
567 * remove autogenerated attributes
569 ldb_msg_remove_attr(msg, "whenCreated");
570 ldb_msg_remove_attr(msg, "whenChanged");
571 ldb_msg_remove_attr(msg, "uSNCreated");
572 ldb_msg_remove_attr(msg, "uSNChanged");
573 ldb_msg_remove_attr(msg, "replPropertyMetaData");
575 if (!ldb_msg_find_element(req->op.add.message, "instanceType")) {
576 ret = ldb_msg_add_fmt(msg, "instanceType", "%u", INSTANCE_TYPE_WRITE);
577 if (ret != LDB_SUCCESS) {
578 ldb_oom(ldb);
579 return LDB_ERR_OPERATIONS_ERROR;
584 * readd replicated attributes
586 ret = ldb_msg_add_string(msg, "whenCreated", time_str);
587 if (ret != LDB_SUCCESS) {
588 ldb_oom(ldb);
589 return LDB_ERR_OPERATIONS_ERROR;
592 /* build the replication meta_data */
593 ZERO_STRUCT(nmd);
594 nmd.version = 1;
595 nmd.ctr.ctr1.count = msg->num_elements;
596 nmd.ctr.ctr1.array = talloc_array(msg,
597 struct replPropertyMetaData1,
598 nmd.ctr.ctr1.count);
599 if (!nmd.ctr.ctr1.array) {
600 ldb_oom(ldb);
601 return LDB_ERR_OPERATIONS_ERROR;
604 for (i=0; i < msg->num_elements; i++) {
605 struct ldb_message_element *e = &msg->elements[i];
606 struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
607 const struct dsdb_attribute *sa;
609 if (e->name[0] == '@') continue;
611 sa = dsdb_attribute_by_lDAPDisplayName(schema, e->name);
612 if (!sa) {
613 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
614 "replmd_add: attribute '%s' not defined in schema\n",
615 e->name);
616 return LDB_ERR_NO_SUCH_ATTRIBUTE;
619 if ((sa->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (sa->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
620 /* if the attribute is not replicated (0x00000001)
621 * or constructed (0x00000004) it has no metadata
623 continue;
626 m->attid = sa->attributeID_id;
627 m->version = 1;
628 m->originating_change_time = now;
629 m->originating_invocation_id = *our_invocation_id;
630 m->originating_usn = seq_num;
631 m->local_usn = seq_num;
632 ni++;
635 /* fix meta data count */
636 nmd.ctr.ctr1.count = ni;
639 * sort meta data array, and move the rdn attribute entry to the end
641 ret = replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, schema, msg->dn);
642 if (ret != LDB_SUCCESS) {
643 return ret;
646 /* generated NDR encoded values */
647 ndr_err = ndr_push_struct_blob(&guid_value, msg,
648 NULL,
649 &guid,
650 (ndr_push_flags_fn_t)ndr_push_GUID);
651 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
652 ldb_oom(ldb);
653 return LDB_ERR_OPERATIONS_ERROR;
655 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
656 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
657 &nmd,
658 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
659 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
660 ldb_oom(ldb);
661 return LDB_ERR_OPERATIONS_ERROR;
665 * add the autogenerated values
667 ret = ldb_msg_add_value(msg, "objectGUID", &guid_value, NULL);
668 if (ret != LDB_SUCCESS) {
669 ldb_oom(ldb);
670 return ret;
672 ret = ldb_msg_add_string(msg, "whenChanged", time_str);
673 if (ret != LDB_SUCCESS) {
674 ldb_oom(ldb);
675 return ret;
677 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", seq_num);
678 if (ret != LDB_SUCCESS) {
679 ldb_oom(ldb);
680 return ret;
682 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", seq_num);
683 if (ret != LDB_SUCCESS) {
684 ldb_oom(ldb);
685 return ret;
687 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
688 if (ret != LDB_SUCCESS) {
689 ldb_oom(ldb);
690 return ret;
694 * sort the attributes by attid before storing the object
696 replmd_ldb_message_sort(msg, schema);
698 ret = ldb_build_add_req(&down_req, ldb, ac,
699 msg,
700 req->controls,
701 ac, replmd_op_callback,
702 req);
703 if (ret != LDB_SUCCESS) {
704 return ret;
707 ret = replmd_notify(module, msg->dn, seq_num);
708 if (ret != LDB_SUCCESS) {
709 return ret;
712 /* if a control is there remove if from the modified request */
713 if (control && !save_controls(control, down_req, &saved_controls)) {
714 return LDB_ERR_OPERATIONS_ERROR;
717 /* go on with the call chain */
718 return ldb_next_request(module, down_req);
723 * update the replPropertyMetaData for one element
725 static int replmd_update_rpmd_element(struct ldb_context *ldb,
726 struct ldb_message *msg,
727 struct ldb_message_element *el,
728 struct replPropertyMetaDataBlob *omd,
729 struct dsdb_schema *schema,
730 uint64_t *seq_num,
731 const struct GUID *our_invocation_id,
732 NTTIME now)
734 int i;
735 const struct dsdb_attribute *a;
736 struct replPropertyMetaData1 *md1;
738 a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
739 if (a == NULL) {
740 DEBUG(0,(__location__ ": Unable to find attribute %s in schema\n",
741 el->name));
742 return LDB_ERR_OPERATIONS_ERROR;
745 if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
746 return LDB_SUCCESS;
749 for (i=0; i<omd->ctr.ctr1.count; i++) {
750 if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) break;
752 if (i == omd->ctr.ctr1.count) {
753 /* we need to add a new one */
754 omd->ctr.ctr1.array = talloc_realloc(msg, omd->ctr.ctr1.array,
755 struct replPropertyMetaData1, omd->ctr.ctr1.count+1);
756 if (omd->ctr.ctr1.array == NULL) {
757 ldb_oom(ldb);
758 return LDB_ERR_OPERATIONS_ERROR;
760 omd->ctr.ctr1.count++;
761 ZERO_STRUCT(omd->ctr.ctr1.array[i]);
764 /* Get a new sequence number from the backend. We only do this
765 * if we have a change that requires a new
766 * replPropertyMetaData element
768 if (*seq_num == 0) {
769 int ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num);
770 if (ret != LDB_SUCCESS) {
771 return LDB_ERR_OPERATIONS_ERROR;
775 md1 = &omd->ctr.ctr1.array[i];
776 md1->version++;
777 md1->attid = a->attributeID_id;
778 md1->originating_change_time = now;
779 md1->originating_invocation_id = *our_invocation_id;
780 md1->originating_usn = *seq_num;
781 md1->local_usn = *seq_num;
783 return LDB_SUCCESS;
787 * update the replPropertyMetaData object each time we modify an
788 * object. This is needed for DRS replication, as the merge on the
789 * client is based on this object
791 static int replmd_update_rpmd(struct ldb_module *module,
792 struct ldb_message *msg, uint64_t *seq_num)
794 const struct ldb_val *omd_value;
795 enum ndr_err_code ndr_err;
796 struct replPropertyMetaDataBlob omd;
797 int i;
798 struct dsdb_schema *schema;
799 time_t t = time(NULL);
800 NTTIME now;
801 const struct GUID *our_invocation_id;
802 int ret;
803 const char *attrs[] = { "replPropertyMetaData" , NULL };
804 struct ldb_result *res;
805 struct ldb_context *ldb;
807 ldb = ldb_module_get_ctx(module);
809 our_invocation_id = samdb_ntds_invocation_id(ldb);
810 if (!our_invocation_id) {
811 /* this happens during an initial vampire while
812 updating the schema */
813 DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
814 return LDB_SUCCESS;
817 unix_to_nt_time(&now, t);
819 /* search for the existing replPropertyMetaDataBlob */
820 ret = dsdb_search_dn_with_deleted(ldb, msg, &res, msg->dn, attrs);
821 if (ret != LDB_SUCCESS || res->count < 1) {
822 DEBUG(0,(__location__ ": Object %s failed to find replPropertyMetaData\n",
823 ldb_dn_get_linearized(msg->dn)));
824 return LDB_ERR_OPERATIONS_ERROR;
828 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
829 if (!omd_value) {
830 DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
831 ldb_dn_get_linearized(msg->dn)));
832 return LDB_ERR_OPERATIONS_ERROR;
835 ndr_err = ndr_pull_struct_blob(omd_value, msg,
836 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), &omd,
837 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
838 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
839 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
840 ldb_dn_get_linearized(msg->dn)));
841 return LDB_ERR_OPERATIONS_ERROR;
844 if (omd.version != 1) {
845 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
846 omd.version, ldb_dn_get_linearized(msg->dn)));
847 return LDB_ERR_OPERATIONS_ERROR;
850 schema = dsdb_get_schema(ldb);
852 for (i=0; i<msg->num_elements; i++) {
853 ret = replmd_update_rpmd_element(ldb, msg, &msg->elements[i], &omd, schema, seq_num,
854 our_invocation_id, now);
855 if (ret != LDB_SUCCESS) {
856 return ret;
861 * replmd_update_rpmd_element has done an update if the
862 * seq_num is set
864 if (*seq_num != 0) {
865 struct ldb_val *md_value;
866 struct ldb_message_element *el;
868 md_value = talloc(msg, struct ldb_val);
869 if (md_value == NULL) {
870 ldb_oom(ldb);
871 return LDB_ERR_OPERATIONS_ERROR;
874 ret = replmd_replPropertyMetaDataCtr1_sort(&omd.ctr.ctr1, schema, msg->dn);
875 if (ret != LDB_SUCCESS) {
876 return ret;
879 ndr_err = ndr_push_struct_blob(md_value, msg,
880 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
881 &omd,
882 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
883 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
884 DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
885 ldb_dn_get_linearized(msg->dn)));
886 return LDB_ERR_OPERATIONS_ERROR;
889 ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
890 if (ret != LDB_SUCCESS) {
891 DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
892 ldb_dn_get_linearized(msg->dn)));
893 return ret;
896 ret = replmd_notify(module, msg->dn, *seq_num);
897 if (ret != LDB_SUCCESS) {
898 return ret;
901 el->num_values = 1;
902 el->values = md_value;
905 return LDB_SUCCESS;
909 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
911 struct ldb_context *ldb;
912 struct replmd_replicated_request *ac;
913 const struct dsdb_schema *schema;
914 struct ldb_request *down_req;
915 struct ldb_message *msg;
916 int ret;
917 time_t t = time(NULL);
918 uint64_t seq_num = 0;
920 /* do not manipulate our control entries */
921 if (ldb_dn_is_special(req->op.mod.message->dn)) {
922 return ldb_next_request(module, req);
925 ldb = ldb_module_get_ctx(module);
927 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
929 schema = dsdb_get_schema(ldb);
930 if (!schema) {
931 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
932 "replmd_modify: no dsdb_schema loaded");
933 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
934 return LDB_ERR_CONSTRAINT_VIOLATION;
937 ac = replmd_ctx_init(module, req);
938 if (!ac) {
939 return LDB_ERR_OPERATIONS_ERROR;
942 ac->schema = schema;
944 /* we have to copy the message as the caller might have it as a const */
945 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
946 if (msg == NULL) {
947 talloc_free(ac);
948 return LDB_ERR_OPERATIONS_ERROR;
951 /* TODO:
952 * - get the whole old object
953 * - if the old object doesn't exist report an error
954 * - give an error when a readonly attribute should
955 * be modified
956 * - merge the changed into the old object
957 * if the caller set values to the same value
958 * ignore the attribute, return success when no
959 * attribute was changed
962 ret = replmd_update_rpmd(module, msg, &seq_num);
963 if (ret != LDB_SUCCESS) {
964 return ret;
967 /* TODO:
968 * - replace the old object with the newly constructed one
971 ret = ldb_build_mod_req(&down_req, ldb, ac,
972 msg,
973 req->controls,
974 ac, replmd_op_callback,
975 req);
976 if (ret != LDB_SUCCESS) {
977 return ret;
979 talloc_steal(down_req, msg);
981 /* we only change whenChanged and uSNChanged if the seq_num
982 has changed */
983 if (seq_num != 0) {
984 if (add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) {
985 talloc_free(ac);
986 return LDB_ERR_OPERATIONS_ERROR;
989 if (add_uint64_element(msg, "uSNChanged", seq_num) != LDB_SUCCESS) {
990 talloc_free(ac);
991 return LDB_ERR_OPERATIONS_ERROR;
995 /* go on with the call chain */
996 return ldb_next_request(module, down_req);
1001 handle a rename request
1003 On a rename we need to do an extra ldb_modify which sets the
1004 whenChanged and uSNChanged attributes
1006 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
1008 struct ldb_context *ldb;
1009 int ret, i;
1010 time_t t = time(NULL);
1011 uint64_t seq_num = 0;
1012 struct ldb_message *msg;
1013 struct replmd_private *replmd_private =
1014 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
1016 /* do not manipulate our control entries */
1017 if (ldb_dn_is_special(req->op.mod.message->dn)) {
1018 return ldb_next_request(module, req);
1021 ldb = ldb_module_get_ctx(module);
1023 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
1025 /* Get a sequence number from the backend */
1026 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
1027 if (ret != LDB_SUCCESS) {
1028 return ret;
1031 msg = ldb_msg_new(req);
1032 if (msg == NULL) {
1033 ldb_oom(ldb);
1034 return LDB_ERR_OPERATIONS_ERROR;
1037 msg->dn = req->op.rename.olddn;
1039 if (add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) {
1040 talloc_free(msg);
1041 return LDB_ERR_OPERATIONS_ERROR;
1043 msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
1045 if (add_uint64_element(msg, "uSNChanged", seq_num) != LDB_SUCCESS) {
1046 talloc_free(msg);
1047 return LDB_ERR_OPERATIONS_ERROR;
1049 msg->elements[1].flags = LDB_FLAG_MOD_REPLACE;
1051 ret = ldb_modify(ldb, msg);
1052 talloc_free(msg);
1053 if (ret != LDB_SUCCESS) {
1054 return ret;
1057 ret = replmd_load_NCs(module);
1058 if (ret != 0) {
1059 return ret;
1062 /* now update the highest uSNs of the partitions that are
1063 affected. Note that two partitions could be changing */
1064 for (i=0; i<replmd_private->num_ncs; i++) {
1065 if (ldb_dn_compare_base(replmd_private->ncs[i].dn,
1066 req->op.rename.olddn) == 0) {
1067 break;
1070 if (i == replmd_private->num_ncs) {
1071 DEBUG(0,(__location__ ": rename olddn outside tree? %s\n",
1072 ldb_dn_get_linearized(req->op.rename.olddn)));
1073 return LDB_ERR_OPERATIONS_ERROR;
1075 replmd_private->ncs[i].mod_usn = seq_num;
1077 for (i=0; i<replmd_private->num_ncs; i++) {
1078 if (ldb_dn_compare_base(replmd_private->ncs[i].dn,
1079 req->op.rename.newdn) == 0) {
1080 break;
1083 if (i == replmd_private->num_ncs) {
1084 DEBUG(0,(__location__ ": rename newdn outside tree? %s\n",
1085 ldb_dn_get_linearized(req->op.rename.newdn)));
1086 return LDB_ERR_OPERATIONS_ERROR;
1088 replmd_private->ncs[i].mod_usn = seq_num;
1090 /* go on with the call chain */
1091 return ldb_next_request(module, req);
1095 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
1097 return ret;
1100 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
1102 int ret = LDB_ERR_OTHER;
1103 /* TODO: do some error mapping */
1104 return ret;
1107 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar);
1109 static int replmd_replicated_apply_add_callback(struct ldb_request *req,
1110 struct ldb_reply *ares)
1112 struct ldb_context *ldb;
1113 struct replmd_replicated_request *ar = talloc_get_type(req->context,
1114 struct replmd_replicated_request);
1115 int ret;
1117 ldb = ldb_module_get_ctx(ar->module);
1119 if (!ares) {
1120 return ldb_module_done(ar->req, NULL, NULL,
1121 LDB_ERR_OPERATIONS_ERROR);
1123 if (ares->error != LDB_SUCCESS) {
1124 return ldb_module_done(ar->req, ares->controls,
1125 ares->response, ares->error);
1128 if (ares->type != LDB_REPLY_DONE) {
1129 ldb_set_errstring(ldb, "Invalid reply type\n!");
1130 return ldb_module_done(ar->req, NULL, NULL,
1131 LDB_ERR_OPERATIONS_ERROR);
1134 talloc_free(ares);
1135 ar->index_current++;
1137 ret = replmd_replicated_apply_next(ar);
1138 if (ret != LDB_SUCCESS) {
1139 return ldb_module_done(ar->req, NULL, NULL, ret);
1142 return LDB_SUCCESS;
1145 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
1147 struct ldb_context *ldb;
1148 struct ldb_request *change_req;
1149 enum ndr_err_code ndr_err;
1150 struct ldb_message *msg;
1151 struct replPropertyMetaDataBlob *md;
1152 struct ldb_val md_value;
1153 uint32_t i;
1154 uint64_t seq_num;
1155 int ret;
1158 * TODO: check if the parent object exist
1162 * TODO: handle the conflict case where an object with the
1163 * same name exist
1166 ldb = ldb_module_get_ctx(ar->module);
1167 msg = ar->objs->objects[ar->index_current].msg;
1168 md = ar->objs->objects[ar->index_current].meta_data;
1170 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
1171 if (ret != LDB_SUCCESS) {
1172 return replmd_replicated_request_error(ar, ret);
1175 ret = ldb_msg_add_value(msg, "objectGUID", &ar->objs->objects[ar->index_current].guid_value, NULL);
1176 if (ret != LDB_SUCCESS) {
1177 return replmd_replicated_request_error(ar, ret);
1180 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
1181 if (ret != LDB_SUCCESS) {
1182 return replmd_replicated_request_error(ar, ret);
1185 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", seq_num);
1186 if (ret != LDB_SUCCESS) {
1187 return replmd_replicated_request_error(ar, ret);
1190 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", seq_num);
1191 if (ret != LDB_SUCCESS) {
1192 return replmd_replicated_request_error(ar, ret);
1195 ret = replmd_notify(ar->module, msg->dn, seq_num);
1196 if (ret != LDB_SUCCESS) {
1197 return replmd_replicated_request_error(ar, ret);
1200 /* remove any message elements that have zero values */
1201 for (i=0; i<msg->num_elements; i++) {
1202 if (msg->elements[i].num_values == 0) {
1203 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
1204 msg->elements[i].name));
1205 memmove(&msg->elements[i],
1206 &msg->elements[i+1],
1207 sizeof(msg->elements[i])*(msg->num_elements - (i+1)));
1208 msg->num_elements--;
1209 i--;
1214 * the meta data array is already sorted by the caller
1216 for (i=0; i < md->ctr.ctr1.count; i++) {
1217 md->ctr.ctr1.array[i].local_usn = seq_num;
1219 ndr_err = ndr_push_struct_blob(&md_value, msg,
1220 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1222 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1223 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1224 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
1225 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
1227 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
1228 if (ret != LDB_SUCCESS) {
1229 return replmd_replicated_request_error(ar, ret);
1232 replmd_ldb_message_sort(msg, ar->schema);
1234 if (DEBUGLVL(4)) {
1235 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_ADD, msg);
1236 DEBUG(4, ("DRS replication add message:\n%s\n", s));
1237 talloc_free(s);
1240 ret = ldb_build_add_req(&change_req,
1241 ldb,
1243 msg,
1244 ar->controls,
1246 replmd_replicated_apply_add_callback,
1247 ar->req);
1248 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1250 return ldb_next_request(ar->module, change_req);
1253 static int replmd_replPropertyMetaData1_conflict_compare(struct replPropertyMetaData1 *m1,
1254 struct replPropertyMetaData1 *m2)
1256 int ret;
1258 if (m1->version != m2->version) {
1259 return m1->version - m2->version;
1262 if (m1->originating_change_time != m2->originating_change_time) {
1263 return m1->originating_change_time - m2->originating_change_time;
1266 ret = GUID_compare(&m1->originating_invocation_id, &m2->originating_invocation_id);
1267 if (ret != 0) {
1268 return ret;
1271 return m1->originating_usn - m2->originating_usn;
1274 static int replmd_replicated_apply_merge_callback(struct ldb_request *req,
1275 struct ldb_reply *ares)
1277 struct ldb_context *ldb;
1278 struct replmd_replicated_request *ar = talloc_get_type(req->context,
1279 struct replmd_replicated_request);
1280 int ret;
1282 ldb = ldb_module_get_ctx(ar->module);
1284 if (!ares) {
1285 return ldb_module_done(ar->req, NULL, NULL,
1286 LDB_ERR_OPERATIONS_ERROR);
1288 if (ares->error != LDB_SUCCESS) {
1289 return ldb_module_done(ar->req, ares->controls,
1290 ares->response, ares->error);
1293 if (ares->type != LDB_REPLY_DONE) {
1294 ldb_set_errstring(ldb, "Invalid reply type\n!");
1295 return ldb_module_done(ar->req, NULL, NULL,
1296 LDB_ERR_OPERATIONS_ERROR);
1299 talloc_free(ares);
1300 ar->index_current++;
1302 ret = replmd_replicated_apply_next(ar);
1303 if (ret != LDB_SUCCESS) {
1304 return ldb_module_done(ar->req, NULL, NULL, ret);
1307 return LDB_SUCCESS;
1310 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
1312 struct ldb_context *ldb;
1313 struct ldb_request *change_req;
1314 enum ndr_err_code ndr_err;
1315 struct ldb_message *msg;
1316 struct replPropertyMetaDataBlob *rmd;
1317 struct replPropertyMetaDataBlob omd;
1318 const struct ldb_val *omd_value;
1319 struct replPropertyMetaDataBlob nmd;
1320 struct ldb_val nmd_value;
1321 uint32_t i,j,ni=0;
1322 uint32_t removed_attrs = 0;
1323 uint64_t seq_num;
1324 int ret;
1326 ldb = ldb_module_get_ctx(ar->module);
1327 msg = ar->objs->objects[ar->index_current].msg;
1328 rmd = ar->objs->objects[ar->index_current].meta_data;
1329 ZERO_STRUCT(omd);
1330 omd.version = 1;
1333 * TODO: check repl data is correct after a rename
1335 if (ldb_dn_compare(msg->dn, ar->search_msg->dn) != 0) {
1336 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_request rename %s => %s\n",
1337 ldb_dn_get_linearized(ar->search_msg->dn),
1338 ldb_dn_get_linearized(msg->dn));
1339 if (ldb_rename(ldb, ar->search_msg->dn, msg->dn) != LDB_SUCCESS) {
1340 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_replicated_request rename %s => %s failed - %s\n",
1341 ldb_dn_get_linearized(ar->search_msg->dn),
1342 ldb_dn_get_linearized(msg->dn),
1343 ldb_errstring(ldb));
1344 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
1348 /* find existing meta data */
1349 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
1350 if (omd_value) {
1351 ndr_err = ndr_pull_struct_blob(omd_value, ar,
1352 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), &omd,
1353 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1354 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1355 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
1356 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
1359 if (omd.version != 1) {
1360 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
1364 ZERO_STRUCT(nmd);
1365 nmd.version = 1;
1366 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
1367 nmd.ctr.ctr1.array = talloc_array(ar,
1368 struct replPropertyMetaData1,
1369 nmd.ctr.ctr1.count);
1370 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1372 /* first copy the old meta data */
1373 for (i=0; i < omd.ctr.ctr1.count; i++) {
1374 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
1375 ni++;
1378 /* now merge in the new meta data */
1379 for (i=0; i < rmd->ctr.ctr1.count; i++) {
1380 bool found = false;
1382 for (j=0; j < ni; j++) {
1383 int cmp;
1385 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
1386 continue;
1389 cmp = replmd_replPropertyMetaData1_conflict_compare(&rmd->ctr.ctr1.array[i],
1390 &nmd.ctr.ctr1.array[j]);
1391 if (cmp > 0) {
1392 /* replace the entry */
1393 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
1394 found = true;
1395 break;
1398 /* we don't want to apply this change so remove the attribute */
1399 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
1400 removed_attrs++;
1402 found = true;
1403 break;
1406 if (found) continue;
1408 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
1409 ni++;
1413 * finally correct the size of the meta_data array
1415 nmd.ctr.ctr1.count = ni;
1418 * the rdn attribute (the alias for the name attribute),
1419 * 'cn' for most objects is the last entry in the meta data array
1420 * we have stored
1422 * sort the new meta data array
1424 ret = replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, ar->schema, msg->dn);
1425 if (ret != LDB_SUCCESS) {
1426 return ret;
1430 * check if some replicated attributes left, otherwise skip the ldb_modify() call
1432 if (msg->num_elements == 0) {
1433 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
1434 ar->index_current);
1436 ar->index_current++;
1437 return replmd_replicated_apply_next(ar);
1440 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
1441 ar->index_current, msg->num_elements);
1443 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
1444 if (ret != LDB_SUCCESS) {
1445 return replmd_replicated_request_error(ar, ret);
1448 for (i=0; i<ni; i++) {
1449 nmd.ctr.ctr1.array[i].local_usn = seq_num;
1452 /* create the meta data value */
1453 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
1454 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1455 &nmd,
1456 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1457 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1458 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
1459 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
1463 * when we know that we'll modify the record, add the whenChanged, uSNChanged
1464 * and replPopertyMetaData attributes
1466 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
1467 if (ret != LDB_SUCCESS) {
1468 return replmd_replicated_request_error(ar, ret);
1470 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", seq_num);
1471 if (ret != LDB_SUCCESS) {
1472 return replmd_replicated_request_error(ar, ret);
1474 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
1475 if (ret != LDB_SUCCESS) {
1476 return replmd_replicated_request_error(ar, ret);
1479 replmd_ldb_message_sort(msg, ar->schema);
1481 /* we want to replace the old values */
1482 for (i=0; i < msg->num_elements; i++) {
1483 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
1486 ret = replmd_notify(ar->module, msg->dn, seq_num);
1487 if (ret != LDB_SUCCESS) {
1488 return replmd_replicated_request_error(ar, ret);
1491 if (DEBUGLVL(4)) {
1492 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
1493 DEBUG(4, ("DRS replication modify message:\n%s\n", s));
1494 talloc_free(s);
1497 ret = ldb_build_mod_req(&change_req,
1498 ldb,
1500 msg,
1501 ar->controls,
1503 replmd_replicated_apply_merge_callback,
1504 ar->req);
1505 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1507 return ldb_next_request(ar->module, change_req);
1510 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
1511 struct ldb_reply *ares)
1513 struct replmd_replicated_request *ar = talloc_get_type(req->context,
1514 struct replmd_replicated_request);
1515 int ret;
1517 if (!ares) {
1518 return ldb_module_done(ar->req, NULL, NULL,
1519 LDB_ERR_OPERATIONS_ERROR);
1521 if (ares->error != LDB_SUCCESS &&
1522 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
1523 return ldb_module_done(ar->req, ares->controls,
1524 ares->response, ares->error);
1527 switch (ares->type) {
1528 case LDB_REPLY_ENTRY:
1529 ar->search_msg = talloc_steal(ar, ares->message);
1530 break;
1532 case LDB_REPLY_REFERRAL:
1533 /* we ignore referrals */
1534 break;
1536 case LDB_REPLY_DONE:
1537 if (ar->search_msg != NULL) {
1538 ret = replmd_replicated_apply_merge(ar);
1539 } else {
1540 ret = replmd_replicated_apply_add(ar);
1542 if (ret != LDB_SUCCESS) {
1543 return ldb_module_done(ar->req, NULL, NULL, ret);
1547 talloc_free(ares);
1548 return LDB_SUCCESS;
1551 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
1553 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
1555 struct ldb_context *ldb;
1556 int ret;
1557 char *tmp_str;
1558 char *filter;
1559 struct ldb_request *search_req;
1561 if (ar->index_current >= ar->objs->num_objects) {
1562 /* done with it, go to next stage */
1563 return replmd_replicated_uptodate_vector(ar);
1566 ldb = ldb_module_get_ctx(ar->module);
1567 ar->search_msg = NULL;
1569 tmp_str = ldb_binary_encode(ar, ar->objs->objects[ar->index_current].guid_value);
1570 if (!tmp_str) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1572 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
1573 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1574 talloc_free(tmp_str);
1576 ret = ldb_build_search_req(&search_req,
1577 ldb,
1579 ar->objs->partition_dn,
1580 LDB_SCOPE_SUBTREE,
1581 filter,
1582 NULL,
1583 NULL,
1585 replmd_replicated_apply_search_callback,
1586 ar->req);
1588 ret = ldb_request_add_control(search_req, LDB_CONTROL_SHOW_DELETED_OID, true, NULL);
1589 if (ret != LDB_SUCCESS) {
1590 return ret;
1594 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1596 return ldb_next_request(ar->module, search_req);
1599 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
1600 struct ldb_reply *ares)
1602 struct ldb_context *ldb;
1603 struct replmd_replicated_request *ar = talloc_get_type(req->context,
1604 struct replmd_replicated_request);
1605 ldb = ldb_module_get_ctx(ar->module);
1607 if (!ares) {
1608 return ldb_module_done(ar->req, NULL, NULL,
1609 LDB_ERR_OPERATIONS_ERROR);
1611 if (ares->error != LDB_SUCCESS) {
1612 return ldb_module_done(ar->req, ares->controls,
1613 ares->response, ares->error);
1616 if (ares->type != LDB_REPLY_DONE) {
1617 ldb_set_errstring(ldb, "Invalid reply type\n!");
1618 return ldb_module_done(ar->req, NULL, NULL,
1619 LDB_ERR_OPERATIONS_ERROR);
1622 talloc_free(ares);
1624 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
1627 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
1629 struct ldb_context *ldb;
1630 struct ldb_request *change_req;
1631 enum ndr_err_code ndr_err;
1632 struct ldb_message *msg;
1633 struct replUpToDateVectorBlob ouv;
1634 const struct ldb_val *ouv_value;
1635 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
1636 struct replUpToDateVectorBlob nuv;
1637 struct ldb_val nuv_value;
1638 struct ldb_message_element *nuv_el = NULL;
1639 const struct GUID *our_invocation_id;
1640 struct ldb_message_element *orf_el = NULL;
1641 struct repsFromToBlob nrf;
1642 struct ldb_val *nrf_value = NULL;
1643 struct ldb_message_element *nrf_el = NULL;
1644 uint32_t i,j,ni=0;
1645 bool found = false;
1646 time_t t = time(NULL);
1647 NTTIME now;
1648 int ret;
1650 ldb = ldb_module_get_ctx(ar->module);
1651 ruv = ar->objs->uptodateness_vector;
1652 ZERO_STRUCT(ouv);
1653 ouv.version = 2;
1654 ZERO_STRUCT(nuv);
1655 nuv.version = 2;
1657 unix_to_nt_time(&now, t);
1660 * first create the new replUpToDateVector
1662 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
1663 if (ouv_value) {
1664 ndr_err = ndr_pull_struct_blob(ouv_value, ar,
1665 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), &ouv,
1666 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
1667 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1668 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
1669 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
1672 if (ouv.version != 2) {
1673 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
1678 * the new uptodateness vector will at least
1679 * contain 1 entry, one for the source_dsa
1681 * plus optional values from our old vector and the one from the source_dsa
1683 nuv.ctr.ctr2.count = 1 + ouv.ctr.ctr2.count;
1684 if (ruv) nuv.ctr.ctr2.count += ruv->count;
1685 nuv.ctr.ctr2.cursors = talloc_array(ar,
1686 struct drsuapi_DsReplicaCursor2,
1687 nuv.ctr.ctr2.count);
1688 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1690 /* first copy the old vector */
1691 for (i=0; i < ouv.ctr.ctr2.count; i++) {
1692 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
1693 ni++;
1696 /* get our invocation_id if we have one already attached to the ldb */
1697 our_invocation_id = samdb_ntds_invocation_id(ldb);
1699 /* merge in the source_dsa vector is available */
1700 for (i=0; (ruv && i < ruv->count); i++) {
1701 found = false;
1703 if (our_invocation_id &&
1704 GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
1705 our_invocation_id)) {
1706 continue;
1709 for (j=0; j < ni; j++) {
1710 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
1711 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
1712 continue;
1715 found = true;
1718 * we update only the highest_usn and not the latest_sync_success time,
1719 * because the last success stands for direct replication
1721 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
1722 nuv.ctr.ctr2.cursors[j].highest_usn = ruv->cursors[i].highest_usn;
1724 break;
1727 if (found) continue;
1729 /* if it's not there yet, add it */
1730 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
1731 ni++;
1735 * merge in the current highwatermark for the source_dsa
1737 found = false;
1738 for (j=0; j < ni; j++) {
1739 if (!GUID_equal(&ar->objs->source_dsa->source_dsa_invocation_id,
1740 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
1741 continue;
1744 found = true;
1747 * here we update the highest_usn and last_sync_success time
1748 * because we're directly replicating from the source_dsa
1750 * and use the tmp_highest_usn because this is what we have just applied
1751 * to our ldb
1753 nuv.ctr.ctr2.cursors[j].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
1754 nuv.ctr.ctr2.cursors[j].last_sync_success = now;
1755 break;
1757 if (!found) {
1759 * here we update the highest_usn and last_sync_success time
1760 * because we're directly replicating from the source_dsa
1762 * and use the tmp_highest_usn because this is what we have just applied
1763 * to our ldb
1765 nuv.ctr.ctr2.cursors[ni].source_dsa_invocation_id= ar->objs->source_dsa->source_dsa_invocation_id;
1766 nuv.ctr.ctr2.cursors[ni].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
1767 nuv.ctr.ctr2.cursors[ni].last_sync_success = now;
1768 ni++;
1772 * finally correct the size of the cursors array
1774 nuv.ctr.ctr2.count = ni;
1777 * sort the cursors
1779 qsort(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count,
1780 sizeof(struct drsuapi_DsReplicaCursor2),
1781 (comparison_fn_t)drsuapi_DsReplicaCursor2_compare);
1784 * create the change ldb_message
1786 msg = ldb_msg_new(ar);
1787 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1788 msg->dn = ar->search_msg->dn;
1790 ndr_err = ndr_push_struct_blob(&nuv_value, msg,
1791 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1792 &nuv,
1793 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
1794 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1795 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
1796 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
1798 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
1799 if (ret != LDB_SUCCESS) {
1800 return replmd_replicated_request_error(ar, ret);
1802 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
1805 * now create the new repsFrom value from the given repsFromTo1 structure
1807 ZERO_STRUCT(nrf);
1808 nrf.version = 1;
1809 nrf.ctr.ctr1 = *ar->objs->source_dsa;
1810 /* and fix some values... */
1811 nrf.ctr.ctr1.consecutive_sync_failures = 0;
1812 nrf.ctr.ctr1.last_success = now;
1813 nrf.ctr.ctr1.last_attempt = now;
1814 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
1815 nrf.ctr.ctr1.highwatermark.highest_usn = nrf.ctr.ctr1.highwatermark.tmp_highest_usn;
1818 * first see if we already have a repsFrom value for the current source dsa
1819 * if so we'll later replace this value
1821 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
1822 if (orf_el) {
1823 for (i=0; i < orf_el->num_values; i++) {
1824 struct repsFromToBlob *trf;
1826 trf = talloc(ar, struct repsFromToBlob);
1827 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1829 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), trf,
1830 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
1831 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1832 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
1833 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
1836 if (trf->version != 1) {
1837 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
1841 * we compare the source dsa objectGUID not the invocation_id
1842 * because we want only one repsFrom value per source dsa
1843 * and when the invocation_id of the source dsa has changed we don't need
1844 * the old repsFrom with the old invocation_id
1846 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
1847 &ar->objs->source_dsa->source_dsa_obj_guid)) {
1848 talloc_free(trf);
1849 continue;
1852 talloc_free(trf);
1853 nrf_value = &orf_el->values[i];
1854 break;
1858 * copy over all old values to the new ldb_message
1860 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
1861 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1862 *nrf_el = *orf_el;
1866 * if we haven't found an old repsFrom value for the current source dsa
1867 * we'll add a new value
1869 if (!nrf_value) {
1870 struct ldb_val zero_value;
1871 ZERO_STRUCT(zero_value);
1872 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
1873 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1875 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
1878 /* we now fill the value which is already attached to ldb_message */
1879 ndr_err = ndr_push_struct_blob(nrf_value, msg,
1880 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1881 &nrf,
1882 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
1883 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1884 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
1885 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
1889 * the ldb_message_element for the attribute, has all the old values and the new one
1890 * so we'll replace the whole attribute with all values
1892 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
1894 if (DEBUGLVL(4)) {
1895 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
1896 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
1897 talloc_free(s);
1900 /* prepare the ldb_modify() request */
1901 ret = ldb_build_mod_req(&change_req,
1902 ldb,
1904 msg,
1905 ar->controls,
1907 replmd_replicated_uptodate_modify_callback,
1908 ar->req);
1909 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1911 return ldb_next_request(ar->module, change_req);
1914 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
1915 struct ldb_reply *ares)
1917 struct replmd_replicated_request *ar = talloc_get_type(req->context,
1918 struct replmd_replicated_request);
1919 int ret;
1921 if (!ares) {
1922 return ldb_module_done(ar->req, NULL, NULL,
1923 LDB_ERR_OPERATIONS_ERROR);
1925 if (ares->error != LDB_SUCCESS &&
1926 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
1927 return ldb_module_done(ar->req, ares->controls,
1928 ares->response, ares->error);
1931 switch (ares->type) {
1932 case LDB_REPLY_ENTRY:
1933 ar->search_msg = talloc_steal(ar, ares->message);
1934 break;
1936 case LDB_REPLY_REFERRAL:
1937 /* we ignore referrals */
1938 break;
1940 case LDB_REPLY_DONE:
1941 if (ar->search_msg == NULL) {
1942 ret = replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
1943 } else {
1944 ret = replmd_replicated_uptodate_modify(ar);
1946 if (ret != LDB_SUCCESS) {
1947 return ldb_module_done(ar->req, NULL, NULL, ret);
1951 talloc_free(ares);
1952 return LDB_SUCCESS;
1956 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
1958 struct ldb_context *ldb;
1959 int ret;
1960 static const char *attrs[] = {
1961 "replUpToDateVector",
1962 "repsFrom",
1963 NULL
1965 struct ldb_request *search_req;
1967 ldb = ldb_module_get_ctx(ar->module);
1968 ar->search_msg = NULL;
1970 ret = ldb_build_search_req(&search_req,
1971 ldb,
1973 ar->objs->partition_dn,
1974 LDB_SCOPE_BASE,
1975 "(objectClass=*)",
1976 attrs,
1977 NULL,
1979 replmd_replicated_uptodate_search_callback,
1980 ar->req);
1981 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1983 return ldb_next_request(ar->module, search_req);
1988 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
1990 struct ldb_context *ldb;
1991 struct dsdb_extended_replicated_objects *objs;
1992 struct replmd_replicated_request *ar;
1993 struct ldb_control **ctrls;
1994 int ret, i;
1995 struct dsdb_control_current_partition *partition_ctrl;
1996 struct replmd_private *replmd_private =
1997 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
1999 ldb = ldb_module_get_ctx(module);
2001 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
2003 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
2004 if (!objs) {
2005 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
2006 return LDB_ERR_PROTOCOL_ERROR;
2009 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
2010 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
2011 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
2012 return LDB_ERR_PROTOCOL_ERROR;
2015 ar = replmd_ctx_init(module, req);
2016 if (!ar)
2017 return LDB_ERR_OPERATIONS_ERROR;
2019 ar->objs = objs;
2020 ar->schema = dsdb_get_schema(ldb);
2021 if (!ar->schema) {
2022 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
2023 talloc_free(ar);
2024 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
2025 return LDB_ERR_CONSTRAINT_VIOLATION;
2028 ctrls = req->controls;
2030 if (req->controls) {
2031 req->controls = talloc_memdup(ar, req->controls,
2032 talloc_get_size(req->controls));
2033 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOMEM);
2036 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, NULL);
2037 if (ret != LDB_SUCCESS) {
2038 return ret;
2042 add the DSDB_CONTROL_CURRENT_PARTITION_OID control. This
2043 tells the partition module which partition this request is
2044 directed at. That is important as the partition roots appear
2045 twice in the directory, once as mount points in the top
2046 level store, and once as the roots of each partition. The
2047 replication code wants to operate on the root of the
2048 partitions, not the top level mount points
2050 partition_ctrl = talloc(req, struct dsdb_control_current_partition);
2051 if (partition_ctrl == NULL) {
2052 if (!partition_ctrl) return replmd_replicated_request_werror(ar, WERR_NOMEM);
2054 partition_ctrl->version = DSDB_CONTROL_CURRENT_PARTITION_VERSION;
2055 partition_ctrl->dn = objs->partition_dn;
2057 ret = ldb_request_add_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID, false, partition_ctrl);
2058 if (ret != LDB_SUCCESS) {
2059 return ret;
2062 ar->controls = req->controls;
2063 req->controls = ctrls;
2065 DEBUG(4,("linked_attributes_count=%u\n", objs->linked_attributes_count));
2067 /* save away the linked attributes for the end of the
2068 transaction */
2069 for (i=0; i<ar->objs->linked_attributes_count; i++) {
2070 struct la_entry *la_entry;
2072 if (replmd_private->la_ctx == NULL) {
2073 replmd_private->la_ctx = talloc_new(replmd_private);
2075 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
2076 if (la_entry == NULL) {
2077 ldb_oom(ldb);
2078 return LDB_ERR_OPERATIONS_ERROR;
2080 la_entry->la = talloc(la_entry, struct drsuapi_DsReplicaLinkedAttribute);
2081 if (la_entry->la == NULL) {
2082 talloc_free(la_entry);
2083 ldb_oom(ldb);
2084 return LDB_ERR_OPERATIONS_ERROR;
2086 *la_entry->la = ar->objs->linked_attributes[i];
2088 /* we need to steal the non-scalars so they stay
2089 around until the end of the transaction */
2090 talloc_steal(la_entry->la, la_entry->la->identifier);
2091 talloc_steal(la_entry->la, la_entry->la->value.blob);
2093 DLIST_ADD(replmd_private->la_list, la_entry);
2096 return replmd_replicated_apply_next(ar);
2100 process one linked attribute structure
2102 static int replmd_process_linked_attribute(struct ldb_module *module,
2103 struct la_entry *la_entry)
2105 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
2106 struct ldb_context *ldb = ldb_module_get_ctx(module);
2107 struct drsuapi_DsReplicaObjectIdentifier3 target;
2108 struct ldb_message *msg;
2109 struct ldb_message_element *ret_el;
2110 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
2111 enum ndr_err_code ndr_err;
2112 struct ldb_request *mod_req;
2113 int ret;
2114 const struct dsdb_attribute *attr;
2115 struct ldb_dn *target_dn;
2116 uint64_t seq_num = 0;
2119 linked_attributes[0]:
2120 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
2121 identifier : *
2122 identifier: struct drsuapi_DsReplicaObjectIdentifier
2123 __ndr_size : 0x0000003a (58)
2124 __ndr_size_sid : 0x00000000 (0)
2125 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
2126 sid : S-0-0
2127 __ndr_size_dn : 0x00000000 (0)
2128 dn : ''
2129 attid : DRSUAPI_ATTRIBUTE_member (0x1F)
2130 value: struct drsuapi_DsAttributeValue
2131 __ndr_size : 0x0000007e (126)
2132 blob : *
2133 blob : DATA_BLOB length=126
2134 flags : 0x00000001 (1)
2135 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
2136 originating_add_time : Wed Sep 2 22:20:01 2009 EST
2137 meta_data: struct drsuapi_DsReplicaMetaData
2138 version : 0x00000015 (21)
2139 originating_change_time : Wed Sep 2 23:39:07 2009 EST
2140 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
2141 originating_usn : 0x000000000001e19c (123292)
2142 &target: struct drsuapi_DsReplicaObjectIdentifier3
2143 __ndr_size : 0x0000007e (126)
2144 __ndr_size_sid : 0x0000001c (28)
2145 guid : 7639e594-db75-4086-b0d4-67890ae46031
2146 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
2147 __ndr_size_dn : 0x00000022 (34)
2148 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
2150 if (DEBUGLVL(4)) {
2151 NDR_PRINT_DEBUG(drsuapi_DsReplicaLinkedAttribute, la);
2154 /* decode the target of the link */
2155 ndr_err = ndr_pull_struct_blob(la->value.blob,
2156 tmp_ctx, lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
2157 &target,
2158 (ndr_pull_flags_fn_t)ndr_pull_drsuapi_DsReplicaObjectIdentifier3);
2159 if (ndr_err != NDR_ERR_SUCCESS) {
2160 DEBUG(0,("Unable to decode linked_attribute target\n"));
2161 dump_data(4, la->value.blob->data, la->value.blob->length);
2162 talloc_free(tmp_ctx);
2163 return LDB_ERR_OPERATIONS_ERROR;
2165 if (DEBUGLVL(4)) {
2166 NDR_PRINT_DEBUG(drsuapi_DsReplicaObjectIdentifier3, &target);
2169 /* construct a modify request for this attribute change */
2170 msg = ldb_msg_new(tmp_ctx);
2171 if (!msg) {
2172 ldb_oom(ldb);
2173 talloc_free(tmp_ctx);
2174 return LDB_ERR_OPERATIONS_ERROR;
2177 ret = dsdb_find_dn_by_guid(ldb, tmp_ctx,
2178 GUID_string(tmp_ctx, &la->identifier->guid), &msg->dn);
2179 if (ret != LDB_SUCCESS) {
2180 talloc_free(tmp_ctx);
2181 return ret;
2184 /* find the attribute being modified */
2185 attr = dsdb_attribute_by_attributeID_id(dsdb_get_schema(ldb), la->attid);
2186 if (attr == NULL) {
2187 DEBUG(0, (__location__ ": Unable to find attributeID 0x%x\n", la->attid));
2188 talloc_free(tmp_ctx);
2189 return LDB_ERR_OPERATIONS_ERROR;
2192 if (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE) {
2193 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName,
2194 LDB_FLAG_MOD_ADD, &ret_el);
2195 } else {
2196 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName,
2197 LDB_FLAG_MOD_DELETE, &ret_el);
2199 if (ret != LDB_SUCCESS) {
2200 talloc_free(tmp_ctx);
2201 return ret;
2203 /* we allocate two entries here, in case we need a remove/add
2204 pair */
2205 ret_el->values = talloc_array(msg, struct ldb_val, 2);
2206 if (!ret_el->values) {
2207 ldb_oom(ldb);
2208 talloc_free(tmp_ctx);
2209 return LDB_ERR_OPERATIONS_ERROR;
2211 ret_el->num_values = 1;
2213 ret = dsdb_find_dn_by_guid(ldb, tmp_ctx, GUID_string(tmp_ctx, &target.guid), &target_dn);
2214 if (ret != LDB_SUCCESS) {
2215 DEBUG(0,(__location__ ": Failed to map GUID %s to DN\n", GUID_string(tmp_ctx, &target.guid)));
2216 talloc_free(tmp_ctx);
2217 return LDB_ERR_OPERATIONS_ERROR;
2220 ret_el->values[0].data = (uint8_t *)ldb_dn_get_extended_linearized(tmp_ctx, target_dn, 1);
2221 ret_el->values[0].length = strlen((char *)ret_el->values[0].data);
2223 ret = replmd_update_rpmd(module, msg, &seq_num);
2224 if (ret != LDB_SUCCESS) {
2225 talloc_free(tmp_ctx);
2226 return ret;
2229 /* we only change whenChanged and uSNChanged if the seq_num
2230 has changed */
2231 if (seq_num != 0) {
2232 time_t t = time(NULL);
2234 if (add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) {
2235 talloc_free(tmp_ctx);
2236 return LDB_ERR_OPERATIONS_ERROR;
2239 if (add_uint64_element(msg, "uSNChanged", seq_num) != LDB_SUCCESS) {
2240 talloc_free(tmp_ctx);
2241 return LDB_ERR_OPERATIONS_ERROR;
2245 ret = ldb_build_mod_req(&mod_req, ldb, tmp_ctx,
2246 msg,
2247 NULL,
2248 NULL,
2249 ldb_op_default_callback,
2250 NULL);
2251 if (ret != LDB_SUCCESS) {
2252 talloc_free(tmp_ctx);
2253 return ret;
2255 talloc_steal(mod_req, msg);
2257 if (DEBUGLVL(4)) {
2258 DEBUG(4,("Applying DRS linked attribute change:\n%s\n",
2259 ldb_ldif_message_string(ldb, tmp_ctx, LDB_CHANGETYPE_MODIFY, msg)));
2262 /* Run the new request */
2263 ret = ldb_next_request(module, mod_req);
2265 /* we need to wait for this to finish, as we are being called
2266 from the synchronous end_transaction hook of this module */
2267 if (ret == LDB_SUCCESS) {
2268 ret = ldb_wait(mod_req->handle, LDB_WAIT_ALL);
2271 if (ret == LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS) {
2272 /* the link destination exists, we need to update it
2273 * by deleting the old one for the same DN then adding
2274 * the new one */
2275 msg->elements = talloc_realloc(msg, msg->elements,
2276 struct ldb_message_element,
2277 msg->num_elements+1);
2278 if (msg->elements == NULL) {
2279 ldb_oom(ldb);
2280 talloc_free(tmp_ctx);
2281 return LDB_ERR_OPERATIONS_ERROR;
2283 /* this relies on the backend matching the old entry
2284 only by the DN portion of the extended DN */
2285 msg->elements[1] = msg->elements[0];
2286 msg->elements[0].flags = LDB_FLAG_MOD_DELETE;
2287 msg->num_elements++;
2289 ret = ldb_build_mod_req(&mod_req, ldb, tmp_ctx,
2290 msg,
2291 NULL,
2292 NULL,
2293 ldb_op_default_callback,
2294 NULL);
2295 if (ret != LDB_SUCCESS) {
2296 talloc_free(tmp_ctx);
2297 return ret;
2300 /* Run the new request */
2301 ret = ldb_next_request(module, mod_req);
2303 if (ret == LDB_SUCCESS) {
2304 ret = ldb_wait(mod_req->handle, LDB_WAIT_ALL);
2308 if (ret != LDB_SUCCESS) {
2309 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s' %s\n",
2310 ldb_errstring(ldb),
2311 ldb_ldif_message_string(ldb, tmp_ctx, LDB_CHANGETYPE_MODIFY, msg));
2312 ret = LDB_SUCCESS;
2315 talloc_free(tmp_ctx);
2317 return ret;
2320 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
2322 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
2323 return replmd_extended_replicated_objects(module, req);
2326 return ldb_next_request(module, req);
2331 we hook into the transaction operations to allow us to
2332 perform the linked attribute updates at the end of the whole
2333 transaction. This allows a forward linked attribute to be created
2334 before the object is created. During a vampire, w2k8 sends us linked
2335 attributes before the objects they are part of.
2337 static int replmd_start_transaction(struct ldb_module *module)
2339 /* create our private structure for this transaction */
2340 int i;
2341 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
2342 struct replmd_private);
2343 talloc_free(replmd_private->la_ctx);
2344 replmd_private->la_list = NULL;
2345 replmd_private->la_ctx = NULL;
2347 for (i=0; i<replmd_private->num_ncs; i++) {
2348 replmd_private->ncs[i].mod_usn = 0;
2351 return ldb_next_start_trans(module);
2355 on prepare commit we loop over our queued la_context structures and
2356 apply each of them
2358 static int replmd_prepare_commit(struct ldb_module *module)
2360 struct replmd_private *replmd_private =
2361 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
2362 struct la_entry *la, *prev;
2363 int ret;
2365 /* walk the list backwards, to do the first entry first, as we
2366 * added the entries with DLIST_ADD() which puts them at the
2367 * start of the list */
2368 for (la = replmd_private->la_list; la && la->next; la=la->next) ;
2370 for (; la; la=prev) {
2371 prev = la->prev;
2372 DLIST_REMOVE(replmd_private->la_list, la);
2373 ret = replmd_process_linked_attribute(module, la);
2374 if (ret != LDB_SUCCESS) {
2375 talloc_free(replmd_private->la_ctx);
2376 replmd_private->la_list = NULL;
2377 replmd_private->la_ctx = NULL;
2378 return ret;
2382 talloc_free(replmd_private->la_ctx);
2383 replmd_private->la_list = NULL;
2384 replmd_private->la_ctx = NULL;
2386 /* possibly change @REPLCHANGED */
2387 ret = replmd_notify_store(module);
2388 if (ret != LDB_SUCCESS) {
2389 return ret;
2392 return ldb_next_prepare_commit(module);
2395 static int replmd_del_transaction(struct ldb_module *module)
2397 struct replmd_private *replmd_private =
2398 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
2399 talloc_free(replmd_private->la_ctx);
2400 replmd_private->la_list = NULL;
2401 replmd_private->la_ctx = NULL;
2402 return ldb_next_del_trans(module);
2406 _PUBLIC_ const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
2407 .name = "repl_meta_data",
2408 .init_context = replmd_init,
2409 .add = replmd_add,
2410 .modify = replmd_modify,
2411 .rename = replmd_rename,
2412 .extended = replmd_extended,
2413 .start_transaction = replmd_start_transaction,
2414 .prepare_commit = replmd_prepare_commit,
2415 .del_transaction = replmd_del_transaction,