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/>.
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
34 * Author: Stefan Metzmacher
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 {
51 struct la_entry *la_list;
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;
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) {
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;
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) {
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 ||
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");
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) {
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;
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 ||
168 /* this happens when the schema is first being
170 talloc_free(replmd_private->ncs);
171 replmd_private->ncs = NULL;
172 replmd_private->num_ncs = 0;
173 talloc_free(tmp_ctx);
176 replmd_private->ncs[i].guid =
177 samdb_result_guid(res->msgs[0], "objectGUID");
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);
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)
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) {
207 if (replmd_private->num_ncs == 0) {
211 for (i=0; i<replmd_private->num_ncs; i++) {
212 if (ldb_dn_compare_base(replmd_private->ncs[i].dn, dn) == 0) {
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;
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)
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++) {
244 if (replmd_private->ncs[i].mod_usn == 0) {
245 /* this partition has not changed in this
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)));
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);
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;
293 if (ldb_msg_find_element(msg, attr) != NULL) {
297 s = ldb_timestring(msg, t);
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
309 el->flags = LDB_FLAG_MOD_REPLACE;
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) {
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
332 el->flags = LDB_FLAG_MOD_REPLACE;
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) {
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) {
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) {
363 return m1->attid - m2->attid;
366 static int replmd_replPropertyMetaDataCtr1_sort(struct replPropertyMetaDataCtr1 *ctr1,
367 const struct dsdb_schema *schema,
370 const char *rdn_name;
371 const struct dsdb_attribute *rdn_sa;
373 rdn_name = ldb_dn_get_rdn_name(dn);
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);
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
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);
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");
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 replmd_replicated_request *ac;
461 const struct dsdb_schema *schema;
462 enum ndr_err_code ndr_err;
463 struct ldb_request *down_req;
464 struct ldb_message *msg;
466 struct ldb_val guid_value;
467 struct replPropertyMetaDataBlob nmd;
468 struct ldb_val nmd_value;
470 const struct GUID *our_invocation_id;
471 time_t t = time(NULL);
477 /* do not manipulate our control entries */
478 if (ldb_dn_is_special(req->op.add.message->dn)) {
479 return ldb_next_request(module, req);
482 ldb = ldb_module_get_ctx(module);
484 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_add\n");
486 schema = dsdb_get_schema(ldb);
488 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
489 "replmd_add: no dsdb_schema loaded");
490 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
491 return LDB_ERR_CONSTRAINT_VIOLATION;
494 ac = replmd_ctx_init(module, req);
496 return LDB_ERR_OPERATIONS_ERROR;
501 if (ldb_msg_find_element(req->op.add.message, "objectGUID") != NULL) {
502 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
503 "replmd_add: it's not allowed to add an object with objectGUID\n");
504 return LDB_ERR_UNWILLING_TO_PERFORM;
507 /* Get a sequence number from the backend */
508 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
509 if (ret != LDB_SUCCESS) {
514 guid = GUID_random();
516 /* get our invocationId */
517 our_invocation_id = samdb_ntds_invocation_id(ldb);
518 if (!our_invocation_id) {
519 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
520 "replmd_add: unable to find invocationId\n");
521 return LDB_ERR_OPERATIONS_ERROR;
524 /* we have to copy the message as the caller might have it as a const */
525 msg = ldb_msg_copy_shallow(ac, req->op.add.message);
528 return LDB_ERR_OPERATIONS_ERROR;
531 /* generated times */
532 unix_to_nt_time(&now, t);
533 time_str = ldb_timestring(msg, t);
535 return LDB_ERR_OPERATIONS_ERROR;
539 * remove autogenerated attributes
541 ldb_msg_remove_attr(msg, "whenCreated");
542 ldb_msg_remove_attr(msg, "whenChanged");
543 ldb_msg_remove_attr(msg, "uSNCreated");
544 ldb_msg_remove_attr(msg, "uSNChanged");
545 ldb_msg_remove_attr(msg, "replPropertyMetaData");
547 if (!ldb_msg_find_element(req->op.add.message, "instanceType")) {
548 ret = ldb_msg_add_fmt(msg, "instanceType", "%u", INSTANCE_TYPE_WRITE);
549 if (ret != LDB_SUCCESS) {
551 return LDB_ERR_OPERATIONS_ERROR;
556 * readd replicated attributes
558 ret = ldb_msg_add_string(msg, "whenCreated", time_str);
559 if (ret != LDB_SUCCESS) {
561 return LDB_ERR_OPERATIONS_ERROR;
564 /* build the replication meta_data */
567 nmd.ctr.ctr1.count = msg->num_elements;
568 nmd.ctr.ctr1.array = talloc_array(msg,
569 struct replPropertyMetaData1,
571 if (!nmd.ctr.ctr1.array) {
573 return LDB_ERR_OPERATIONS_ERROR;
576 for (i=0; i < msg->num_elements; i++) {
577 struct ldb_message_element *e = &msg->elements[i];
578 struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
579 const struct dsdb_attribute *sa;
581 if (e->name[0] == '@') continue;
583 sa = dsdb_attribute_by_lDAPDisplayName(schema, e->name);
585 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
586 "replmd_add: attribute '%s' not defined in schema\n",
588 return LDB_ERR_NO_SUCH_ATTRIBUTE;
591 if ((sa->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (sa->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
592 /* if the attribute is not replicated (0x00000001)
593 * or constructed (0x00000004) it has no metadata
598 m->attid = sa->attributeID_id;
600 m->originating_change_time = now;
601 m->originating_invocation_id = *our_invocation_id;
602 m->originating_usn = seq_num;
603 m->local_usn = seq_num;
607 /* fix meta data count */
608 nmd.ctr.ctr1.count = ni;
611 * sort meta data array, and move the rdn attribute entry to the end
613 ret = replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, schema, msg->dn);
614 if (ret != LDB_SUCCESS) {
618 /* generated NDR encoded values */
619 ndr_err = ndr_push_struct_blob(&guid_value, msg,
622 (ndr_push_flags_fn_t)ndr_push_GUID);
623 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
625 return LDB_ERR_OPERATIONS_ERROR;
627 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
628 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
630 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
631 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
633 return LDB_ERR_OPERATIONS_ERROR;
637 * add the autogenerated values
639 ret = ldb_msg_add_value(msg, "objectGUID", &guid_value, NULL);
640 if (ret != LDB_SUCCESS) {
642 return LDB_ERR_OPERATIONS_ERROR;
644 ret = ldb_msg_add_string(msg, "whenChanged", time_str);
645 if (ret != LDB_SUCCESS) {
647 return LDB_ERR_OPERATIONS_ERROR;
649 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", seq_num);
650 if (ret != LDB_SUCCESS) {
652 return LDB_ERR_OPERATIONS_ERROR;
654 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", seq_num);
655 if (ret != LDB_SUCCESS) {
657 return LDB_ERR_OPERATIONS_ERROR;
659 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
660 if (ret != LDB_SUCCESS) {
662 return LDB_ERR_OPERATIONS_ERROR;
666 * sort the attributes by attid before storing the object
668 replmd_ldb_message_sort(msg, schema);
670 ret = ldb_build_add_req(&down_req, ldb, ac,
673 ac, replmd_op_callback,
675 if (ret != LDB_SUCCESS) {
679 ret = replmd_notify(module, msg->dn, seq_num);
680 if (ret != LDB_SUCCESS) {
684 /* go on with the call chain */
685 return ldb_next_request(module, down_req);
690 * update the replPropertyMetaData for one element
692 static int replmd_update_rpmd_element(struct ldb_context *ldb,
693 struct ldb_message *msg,
694 struct ldb_message_element *el,
695 struct replPropertyMetaDataBlob *omd,
696 struct dsdb_schema *schema,
698 const struct GUID *our_invocation_id,
702 const struct dsdb_attribute *a;
703 struct replPropertyMetaData1 *md1;
705 a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
707 DEBUG(0,(__location__ ": Unable to find attribute %s in schema\n",
709 return LDB_ERR_OPERATIONS_ERROR;
712 if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
716 for (i=0; i<omd->ctr.ctr1.count; i++) {
717 if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) break;
719 if (i == omd->ctr.ctr1.count) {
720 /* we need to add a new one */
721 omd->ctr.ctr1.array = talloc_realloc(msg, omd->ctr.ctr1.array,
722 struct replPropertyMetaData1, omd->ctr.ctr1.count+1);
723 if (omd->ctr.ctr1.array == NULL) {
725 return LDB_ERR_OPERATIONS_ERROR;
727 omd->ctr.ctr1.count++;
728 ZERO_STRUCT(omd->ctr.ctr1.array[i]);
731 /* Get a new sequence number from the backend. We only do this
732 * if we have a change that requires a new
733 * replPropertyMetaData element
736 int ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num);
737 if (ret != LDB_SUCCESS) {
738 return LDB_ERR_OPERATIONS_ERROR;
742 md1 = &omd->ctr.ctr1.array[i];
744 md1->attid = a->attributeID_id;
745 md1->originating_change_time = now;
746 md1->originating_invocation_id = *our_invocation_id;
747 md1->originating_usn = *seq_num;
748 md1->local_usn = *seq_num;
754 * update the replPropertyMetaData object each time we modify an
755 * object. This is needed for DRS replication, as the merge on the
756 * client is based on this object
758 static int replmd_update_rpmd(struct ldb_module *module,
759 struct ldb_message *msg, uint64_t *seq_num)
761 const struct ldb_val *omd_value;
762 enum ndr_err_code ndr_err;
763 struct replPropertyMetaDataBlob omd;
765 struct dsdb_schema *schema;
766 time_t t = time(NULL);
768 const struct GUID *our_invocation_id;
770 const char *attrs[] = { "replPropertyMetaData" , NULL };
771 struct ldb_result *res;
772 struct ldb_context *ldb;
774 ldb = ldb_module_get_ctx(module);
776 our_invocation_id = samdb_ntds_invocation_id(ldb);
777 if (!our_invocation_id) {
778 /* this happens during an initial vampire while
779 updating the schema */
780 DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
784 unix_to_nt_time(&now, t);
786 /* search for the existing replPropertyMetaDataBlob */
787 ret = dsdb_search_dn_with_deleted(ldb, msg, &res, msg->dn, attrs);
788 if (ret != LDB_SUCCESS || res->count < 1) {
789 DEBUG(0,(__location__ ": Object %s failed to find replPropertyMetaData\n",
790 ldb_dn_get_linearized(msg->dn)));
791 return LDB_ERR_OPERATIONS_ERROR;
795 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
797 DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
798 ldb_dn_get_linearized(msg->dn)));
799 return LDB_ERR_OPERATIONS_ERROR;
802 ndr_err = ndr_pull_struct_blob(omd_value, msg,
803 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), &omd,
804 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
805 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
806 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
807 ldb_dn_get_linearized(msg->dn)));
808 return LDB_ERR_OPERATIONS_ERROR;
811 if (omd.version != 1) {
812 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
813 omd.version, ldb_dn_get_linearized(msg->dn)));
814 return LDB_ERR_OPERATIONS_ERROR;
817 schema = dsdb_get_schema(ldb);
819 for (i=0; i<msg->num_elements; i++) {
820 ret = replmd_update_rpmd_element(ldb, msg, &msg->elements[i], &omd, schema, seq_num,
821 our_invocation_id, now);
822 if (ret != LDB_SUCCESS) {
828 * replmd_update_rpmd_element has done an update if the
832 struct ldb_val *md_value;
833 struct ldb_message_element *el;
835 md_value = talloc(msg, struct ldb_val);
836 if (md_value == NULL) {
838 return LDB_ERR_OPERATIONS_ERROR;
841 ret = replmd_replPropertyMetaDataCtr1_sort(&omd.ctr.ctr1, schema, msg->dn);
842 if (ret != LDB_SUCCESS) {
846 ndr_err = ndr_push_struct_blob(md_value, msg,
847 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
849 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
850 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
851 DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
852 ldb_dn_get_linearized(msg->dn)));
853 return LDB_ERR_OPERATIONS_ERROR;
856 ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
857 if (ret != LDB_SUCCESS) {
858 DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
859 ldb_dn_get_linearized(msg->dn)));
863 ret = replmd_notify(module, msg->dn, *seq_num);
864 if (ret != LDB_SUCCESS) {
869 el->values = md_value;
876 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
878 struct ldb_context *ldb;
879 struct replmd_replicated_request *ac;
880 const struct dsdb_schema *schema;
881 struct ldb_request *down_req;
882 struct ldb_message *msg;
884 time_t t = time(NULL);
885 uint64_t seq_num = 0;
887 /* do not manipulate our control entries */
888 if (ldb_dn_is_special(req->op.mod.message->dn)) {
889 return ldb_next_request(module, req);
892 ldb = ldb_module_get_ctx(module);
894 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
896 schema = dsdb_get_schema(ldb);
898 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
899 "replmd_modify: no dsdb_schema loaded");
900 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
901 return LDB_ERR_CONSTRAINT_VIOLATION;
904 ac = replmd_ctx_init(module, req);
906 return LDB_ERR_OPERATIONS_ERROR;
911 /* we have to copy the message as the caller might have it as a const */
912 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
915 return LDB_ERR_OPERATIONS_ERROR;
919 * - get the whole old object
920 * - if the old object doesn't exist report an error
921 * - give an error when a readonly attribute should
923 * - merge the changed into the old object
924 * if the caller set values to the same value
925 * ignore the attribute, return success when no
926 * attribute was changed
929 ret = replmd_update_rpmd(module, msg, &seq_num);
930 if (ret != LDB_SUCCESS) {
935 * - replace the old object with the newly constructed one
938 ret = ldb_build_mod_req(&down_req, ldb, ac,
941 ac, replmd_op_callback,
943 if (ret != LDB_SUCCESS) {
946 talloc_steal(down_req, msg);
948 /* we only change whenChanged and uSNChanged if the seq_num
951 if (add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) {
953 return LDB_ERR_OPERATIONS_ERROR;
956 if (add_uint64_element(msg, "uSNChanged", seq_num) != LDB_SUCCESS) {
958 return LDB_ERR_OPERATIONS_ERROR;
962 /* go on with the call chain */
963 return ldb_next_request(module, down_req);
968 handle a rename request
970 On a rename we need to do an extra ldb_modify which sets the
971 whenChanged and uSNChanged attributes
973 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
975 struct ldb_context *ldb;
977 time_t t = time(NULL);
978 uint64_t seq_num = 0;
979 struct ldb_message *msg;
980 struct replmd_private *replmd_private =
981 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
983 /* do not manipulate our control entries */
984 if (ldb_dn_is_special(req->op.mod.message->dn)) {
985 return ldb_next_request(module, req);
988 ldb = ldb_module_get_ctx(module);
990 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
992 /* Get a sequence number from the backend */
993 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
994 if (ret != LDB_SUCCESS) {
998 msg = ldb_msg_new(req);
1001 return LDB_ERR_OPERATIONS_ERROR;
1004 msg->dn = req->op.rename.olddn;
1006 if (add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) {
1008 return LDB_ERR_OPERATIONS_ERROR;
1010 msg->elements[0].flags = LDB_FLAG_MOD_REPLACE;
1012 if (add_uint64_element(msg, "uSNChanged", seq_num) != LDB_SUCCESS) {
1014 return LDB_ERR_OPERATIONS_ERROR;
1016 msg->elements[1].flags = LDB_FLAG_MOD_REPLACE;
1018 ret = ldb_modify(ldb, msg);
1020 if (ret != LDB_SUCCESS) {
1024 ret = replmd_load_NCs(module);
1029 /* now update the highest uSNs of the partitions that are
1030 affected. Note that two partitions could be changing */
1031 for (i=0; i<replmd_private->num_ncs; i++) {
1032 if (ldb_dn_compare_base(replmd_private->ncs[i].dn,
1033 req->op.rename.olddn) == 0) {
1037 if (i == replmd_private->num_ncs) {
1038 DEBUG(0,(__location__ ": rename olddn outside tree? %s\n",
1039 ldb_dn_get_linearized(req->op.rename.olddn)));
1040 return LDB_ERR_OPERATIONS_ERROR;
1042 replmd_private->ncs[i].mod_usn = seq_num;
1044 for (i=0; i<replmd_private->num_ncs; i++) {
1045 if (ldb_dn_compare_base(replmd_private->ncs[i].dn,
1046 req->op.rename.newdn) == 0) {
1050 if (i == replmd_private->num_ncs) {
1051 DEBUG(0,(__location__ ": rename newdn outside tree? %s\n",
1052 ldb_dn_get_linearized(req->op.rename.newdn)));
1053 return LDB_ERR_OPERATIONS_ERROR;
1055 replmd_private->ncs[i].mod_usn = seq_num;
1057 /* go on with the call chain */
1058 return ldb_next_request(module, req);
1062 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
1067 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
1069 int ret = LDB_ERR_OTHER;
1070 /* TODO: do some error mapping */
1074 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar);
1076 static int replmd_replicated_apply_add_callback(struct ldb_request *req,
1077 struct ldb_reply *ares)
1079 struct ldb_context *ldb;
1080 struct replmd_replicated_request *ar = talloc_get_type(req->context,
1081 struct replmd_replicated_request);
1084 ldb = ldb_module_get_ctx(ar->module);
1087 return ldb_module_done(ar->req, NULL, NULL,
1088 LDB_ERR_OPERATIONS_ERROR);
1090 if (ares->error != LDB_SUCCESS) {
1091 return ldb_module_done(ar->req, ares->controls,
1092 ares->response, ares->error);
1095 if (ares->type != LDB_REPLY_DONE) {
1096 ldb_set_errstring(ldb, "Invalid reply type\n!");
1097 return ldb_module_done(ar->req, NULL, NULL,
1098 LDB_ERR_OPERATIONS_ERROR);
1102 ar->index_current++;
1104 ret = replmd_replicated_apply_next(ar);
1105 if (ret != LDB_SUCCESS) {
1106 return ldb_module_done(ar->req, NULL, NULL, ret);
1112 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
1114 struct ldb_context *ldb;
1115 struct ldb_request *change_req;
1116 enum ndr_err_code ndr_err;
1117 struct ldb_message *msg;
1118 struct replPropertyMetaDataBlob *md;
1119 struct ldb_val md_value;
1125 * TODO: check if the parent object exist
1129 * TODO: handle the conflict case where an object with the
1133 ldb = ldb_module_get_ctx(ar->module);
1134 msg = ar->objs->objects[ar->index_current].msg;
1135 md = ar->objs->objects[ar->index_current].meta_data;
1137 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
1138 if (ret != LDB_SUCCESS) {
1139 return replmd_replicated_request_error(ar, ret);
1142 ret = ldb_msg_add_value(msg, "objectGUID", &ar->objs->objects[ar->index_current].guid_value, NULL);
1143 if (ret != LDB_SUCCESS) {
1144 return replmd_replicated_request_error(ar, ret);
1147 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
1148 if (ret != LDB_SUCCESS) {
1149 return replmd_replicated_request_error(ar, ret);
1152 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", seq_num);
1153 if (ret != LDB_SUCCESS) {
1154 return replmd_replicated_request_error(ar, ret);
1157 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", seq_num);
1158 if (ret != LDB_SUCCESS) {
1159 return replmd_replicated_request_error(ar, ret);
1162 ret = replmd_notify(ar->module, msg->dn, seq_num);
1163 if (ret != LDB_SUCCESS) {
1164 return replmd_replicated_request_error(ar, ret);
1167 /* remove any message elements that have zero values */
1168 for (i=0; i<msg->num_elements; i++) {
1169 if (msg->elements[i].num_values == 0) {
1170 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
1171 msg->elements[i].name));
1172 memmove(&msg->elements[i],
1173 &msg->elements[i+1],
1174 sizeof(msg->elements[i])*(msg->num_elements - (i+1)));
1175 msg->num_elements--;
1181 * the meta data array is already sorted by the caller
1183 for (i=0; i < md->ctr.ctr1.count; i++) {
1184 md->ctr.ctr1.array[i].local_usn = seq_num;
1186 ndr_err = ndr_push_struct_blob(&md_value, msg,
1187 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1189 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1190 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1191 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
1192 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
1194 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
1195 if (ret != LDB_SUCCESS) {
1196 return replmd_replicated_request_error(ar, ret);
1199 replmd_ldb_message_sort(msg, ar->schema);
1202 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_ADD, msg);
1203 DEBUG(4, ("DRS replication add message:\n%s\n", s));
1207 ret = ldb_build_add_req(&change_req,
1213 replmd_replicated_apply_add_callback,
1215 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1217 return ldb_next_request(ar->module, change_req);
1220 static int replmd_replPropertyMetaData1_conflict_compare(struct replPropertyMetaData1 *m1,
1221 struct replPropertyMetaData1 *m2)
1225 if (m1->version != m2->version) {
1226 return m1->version - m2->version;
1229 if (m1->originating_change_time != m2->originating_change_time) {
1230 return m1->originating_change_time - m2->originating_change_time;
1233 ret = GUID_compare(&m1->originating_invocation_id, &m2->originating_invocation_id);
1238 return m1->originating_usn - m2->originating_usn;
1241 static int replmd_replicated_apply_merge_callback(struct ldb_request *req,
1242 struct ldb_reply *ares)
1244 struct ldb_context *ldb;
1245 struct replmd_replicated_request *ar = talloc_get_type(req->context,
1246 struct replmd_replicated_request);
1249 ldb = ldb_module_get_ctx(ar->module);
1252 return ldb_module_done(ar->req, NULL, NULL,
1253 LDB_ERR_OPERATIONS_ERROR);
1255 if (ares->error != LDB_SUCCESS) {
1256 return ldb_module_done(ar->req, ares->controls,
1257 ares->response, ares->error);
1260 if (ares->type != LDB_REPLY_DONE) {
1261 ldb_set_errstring(ldb, "Invalid reply type\n!");
1262 return ldb_module_done(ar->req, NULL, NULL,
1263 LDB_ERR_OPERATIONS_ERROR);
1267 ar->index_current++;
1269 ret = replmd_replicated_apply_next(ar);
1270 if (ret != LDB_SUCCESS) {
1271 return ldb_module_done(ar->req, NULL, NULL, ret);
1277 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
1279 struct ldb_context *ldb;
1280 struct ldb_request *change_req;
1281 enum ndr_err_code ndr_err;
1282 struct ldb_message *msg;
1283 struct replPropertyMetaDataBlob *rmd;
1284 struct replPropertyMetaDataBlob omd;
1285 const struct ldb_val *omd_value;
1286 struct replPropertyMetaDataBlob nmd;
1287 struct ldb_val nmd_value;
1289 uint32_t removed_attrs = 0;
1293 ldb = ldb_module_get_ctx(ar->module);
1294 msg = ar->objs->objects[ar->index_current].msg;
1295 rmd = ar->objs->objects[ar->index_current].meta_data;
1300 * TODO: check repl data is correct after a rename
1302 if (ldb_dn_compare(msg->dn, ar->search_msg->dn) != 0) {
1303 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_request rename %s => %s\n",
1304 ldb_dn_get_linearized(ar->search_msg->dn),
1305 ldb_dn_get_linearized(msg->dn));
1306 if (ldb_rename(ldb, ar->search_msg->dn, msg->dn) != LDB_SUCCESS) {
1307 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_replicated_request rename %s => %s failed - %s\n",
1308 ldb_dn_get_linearized(ar->search_msg->dn),
1309 ldb_dn_get_linearized(msg->dn),
1310 ldb_errstring(ldb));
1311 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
1315 /* find existing meta data */
1316 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
1318 ndr_err = ndr_pull_struct_blob(omd_value, ar,
1319 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), &omd,
1320 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1321 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1322 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
1323 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
1326 if (omd.version != 1) {
1327 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
1333 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
1334 nmd.ctr.ctr1.array = talloc_array(ar,
1335 struct replPropertyMetaData1,
1336 nmd.ctr.ctr1.count);
1337 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1339 /* first copy the old meta data */
1340 for (i=0; i < omd.ctr.ctr1.count; i++) {
1341 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
1345 /* now merge in the new meta data */
1346 for (i=0; i < rmd->ctr.ctr1.count; i++) {
1349 for (j=0; j < ni; j++) {
1352 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
1356 cmp = replmd_replPropertyMetaData1_conflict_compare(&rmd->ctr.ctr1.array[i],
1357 &nmd.ctr.ctr1.array[j]);
1359 /* replace the entry */
1360 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
1365 /* we don't want to apply this change so remove the attribute */
1366 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
1373 if (found) continue;
1375 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
1380 * finally correct the size of the meta_data array
1382 nmd.ctr.ctr1.count = ni;
1385 * the rdn attribute (the alias for the name attribute),
1386 * 'cn' for most objects is the last entry in the meta data array
1389 * sort the new meta data array
1391 ret = replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, ar->schema, msg->dn);
1392 if (ret != LDB_SUCCESS) {
1397 * check if some replicated attributes left, otherwise skip the ldb_modify() call
1399 if (msg->num_elements == 0) {
1400 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
1403 ar->index_current++;
1404 return replmd_replicated_apply_next(ar);
1407 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
1408 ar->index_current, msg->num_elements);
1410 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
1411 if (ret != LDB_SUCCESS) {
1412 return replmd_replicated_request_error(ar, ret);
1415 for (i=0; i<ni; i++) {
1416 nmd.ctr.ctr1.array[i].local_usn = seq_num;
1419 /* create the meta data value */
1420 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
1421 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1423 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1424 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1425 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
1426 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
1430 * when we know that we'll modify the record, add the whenChanged, uSNChanged
1431 * and replPopertyMetaData attributes
1433 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
1434 if (ret != LDB_SUCCESS) {
1435 return replmd_replicated_request_error(ar, ret);
1437 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", seq_num);
1438 if (ret != LDB_SUCCESS) {
1439 return replmd_replicated_request_error(ar, ret);
1441 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
1442 if (ret != LDB_SUCCESS) {
1443 return replmd_replicated_request_error(ar, ret);
1446 replmd_ldb_message_sort(msg, ar->schema);
1448 /* we want to replace the old values */
1449 for (i=0; i < msg->num_elements; i++) {
1450 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
1453 ret = replmd_notify(ar->module, msg->dn, seq_num);
1454 if (ret != LDB_SUCCESS) {
1455 return replmd_replicated_request_error(ar, ret);
1459 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
1460 DEBUG(4, ("DRS replication modify message:\n%s\n", s));
1464 ret = ldb_build_mod_req(&change_req,
1470 replmd_replicated_apply_merge_callback,
1472 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1474 return ldb_next_request(ar->module, change_req);
1477 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
1478 struct ldb_reply *ares)
1480 struct replmd_replicated_request *ar = talloc_get_type(req->context,
1481 struct replmd_replicated_request);
1485 return ldb_module_done(ar->req, NULL, NULL,
1486 LDB_ERR_OPERATIONS_ERROR);
1488 if (ares->error != LDB_SUCCESS &&
1489 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
1490 return ldb_module_done(ar->req, ares->controls,
1491 ares->response, ares->error);
1494 switch (ares->type) {
1495 case LDB_REPLY_ENTRY:
1496 ar->search_msg = talloc_steal(ar, ares->message);
1499 case LDB_REPLY_REFERRAL:
1500 /* we ignore referrals */
1503 case LDB_REPLY_DONE:
1504 if (ar->search_msg != NULL) {
1505 ret = replmd_replicated_apply_merge(ar);
1507 ret = replmd_replicated_apply_add(ar);
1509 if (ret != LDB_SUCCESS) {
1510 return ldb_module_done(ar->req, NULL, NULL, ret);
1518 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
1520 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
1522 struct ldb_context *ldb;
1526 struct ldb_request *search_req;
1528 if (ar->index_current >= ar->objs->num_objects) {
1529 /* done with it, go to next stage */
1530 return replmd_replicated_uptodate_vector(ar);
1533 ldb = ldb_module_get_ctx(ar->module);
1534 ar->search_msg = NULL;
1536 tmp_str = ldb_binary_encode(ar, ar->objs->objects[ar->index_current].guid_value);
1537 if (!tmp_str) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1539 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
1540 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1541 talloc_free(tmp_str);
1543 ret = ldb_build_search_req(&search_req,
1546 ar->objs->partition_dn,
1552 replmd_replicated_apply_search_callback,
1555 ret = ldb_request_add_control(search_req, LDB_CONTROL_SHOW_DELETED_OID, true, NULL);
1556 if (ret != LDB_SUCCESS) {
1561 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1563 return ldb_next_request(ar->module, search_req);
1566 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
1567 struct ldb_reply *ares)
1569 struct ldb_context *ldb;
1570 struct replmd_replicated_request *ar = talloc_get_type(req->context,
1571 struct replmd_replicated_request);
1572 ldb = ldb_module_get_ctx(ar->module);
1575 return ldb_module_done(ar->req, NULL, NULL,
1576 LDB_ERR_OPERATIONS_ERROR);
1578 if (ares->error != LDB_SUCCESS) {
1579 return ldb_module_done(ar->req, ares->controls,
1580 ares->response, ares->error);
1583 if (ares->type != LDB_REPLY_DONE) {
1584 ldb_set_errstring(ldb, "Invalid reply type\n!");
1585 return ldb_module_done(ar->req, NULL, NULL,
1586 LDB_ERR_OPERATIONS_ERROR);
1591 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
1594 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
1596 struct ldb_context *ldb;
1597 struct ldb_request *change_req;
1598 enum ndr_err_code ndr_err;
1599 struct ldb_message *msg;
1600 struct replUpToDateVectorBlob ouv;
1601 const struct ldb_val *ouv_value;
1602 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
1603 struct replUpToDateVectorBlob nuv;
1604 struct ldb_val nuv_value;
1605 struct ldb_message_element *nuv_el = NULL;
1606 const struct GUID *our_invocation_id;
1607 struct ldb_message_element *orf_el = NULL;
1608 struct repsFromToBlob nrf;
1609 struct ldb_val *nrf_value = NULL;
1610 struct ldb_message_element *nrf_el = NULL;
1613 time_t t = time(NULL);
1617 ldb = ldb_module_get_ctx(ar->module);
1618 ruv = ar->objs->uptodateness_vector;
1624 unix_to_nt_time(&now, t);
1627 * first create the new replUpToDateVector
1629 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
1631 ndr_err = ndr_pull_struct_blob(ouv_value, ar,
1632 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), &ouv,
1633 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
1634 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1635 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
1636 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
1639 if (ouv.version != 2) {
1640 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
1645 * the new uptodateness vector will at least
1646 * contain 1 entry, one for the source_dsa
1648 * plus optional values from our old vector and the one from the source_dsa
1650 nuv.ctr.ctr2.count = 1 + ouv.ctr.ctr2.count;
1651 if (ruv) nuv.ctr.ctr2.count += ruv->count;
1652 nuv.ctr.ctr2.cursors = talloc_array(ar,
1653 struct drsuapi_DsReplicaCursor2,
1654 nuv.ctr.ctr2.count);
1655 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1657 /* first copy the old vector */
1658 for (i=0; i < ouv.ctr.ctr2.count; i++) {
1659 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
1663 /* get our invocation_id if we have one already attached to the ldb */
1664 our_invocation_id = samdb_ntds_invocation_id(ldb);
1666 /* merge in the source_dsa vector is available */
1667 for (i=0; (ruv && i < ruv->count); i++) {
1670 if (our_invocation_id &&
1671 GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
1672 our_invocation_id)) {
1676 for (j=0; j < ni; j++) {
1677 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
1678 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
1685 * we update only the highest_usn and not the latest_sync_success time,
1686 * because the last success stands for direct replication
1688 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
1689 nuv.ctr.ctr2.cursors[j].highest_usn = ruv->cursors[i].highest_usn;
1694 if (found) continue;
1696 /* if it's not there yet, add it */
1697 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
1702 * merge in the current highwatermark for the source_dsa
1705 for (j=0; j < ni; j++) {
1706 if (!GUID_equal(&ar->objs->source_dsa->source_dsa_invocation_id,
1707 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
1714 * here we update the highest_usn and last_sync_success time
1715 * because we're directly replicating from the source_dsa
1717 * and use the tmp_highest_usn because this is what we have just applied
1720 nuv.ctr.ctr2.cursors[j].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
1721 nuv.ctr.ctr2.cursors[j].last_sync_success = now;
1726 * here we update the highest_usn and last_sync_success time
1727 * because we're directly replicating from the source_dsa
1729 * and use the tmp_highest_usn because this is what we have just applied
1732 nuv.ctr.ctr2.cursors[ni].source_dsa_invocation_id= ar->objs->source_dsa->source_dsa_invocation_id;
1733 nuv.ctr.ctr2.cursors[ni].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
1734 nuv.ctr.ctr2.cursors[ni].last_sync_success = now;
1739 * finally correct the size of the cursors array
1741 nuv.ctr.ctr2.count = ni;
1746 qsort(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count,
1747 sizeof(struct drsuapi_DsReplicaCursor2),
1748 (comparison_fn_t)drsuapi_DsReplicaCursor2_compare);
1751 * create the change ldb_message
1753 msg = ldb_msg_new(ar);
1754 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1755 msg->dn = ar->search_msg->dn;
1757 ndr_err = ndr_push_struct_blob(&nuv_value, msg,
1758 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1760 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
1761 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1762 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
1763 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
1765 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
1766 if (ret != LDB_SUCCESS) {
1767 return replmd_replicated_request_error(ar, ret);
1769 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
1772 * now create the new repsFrom value from the given repsFromTo1 structure
1776 nrf.ctr.ctr1 = *ar->objs->source_dsa;
1777 /* and fix some values... */
1778 nrf.ctr.ctr1.consecutive_sync_failures = 0;
1779 nrf.ctr.ctr1.last_success = now;
1780 nrf.ctr.ctr1.last_attempt = now;
1781 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
1782 nrf.ctr.ctr1.highwatermark.highest_usn = nrf.ctr.ctr1.highwatermark.tmp_highest_usn;
1785 * first see if we already have a repsFrom value for the current source dsa
1786 * if so we'll later replace this value
1788 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
1790 for (i=0; i < orf_el->num_values; i++) {
1791 struct repsFromToBlob *trf;
1793 trf = talloc(ar, struct repsFromToBlob);
1794 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOMEM);
1796 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), trf,
1797 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
1798 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1799 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
1800 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
1803 if (trf->version != 1) {
1804 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
1808 * we compare the source dsa objectGUID not the invocation_id
1809 * because we want only one repsFrom value per source dsa
1810 * and when the invocation_id of the source dsa has changed we don't need
1811 * the old repsFrom with the old invocation_id
1813 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
1814 &ar->objs->source_dsa->source_dsa_obj_guid)) {
1820 nrf_value = &orf_el->values[i];
1825 * copy over all old values to the new ldb_message
1827 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
1828 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1833 * if we haven't found an old repsFrom value for the current source dsa
1834 * we'll add a new value
1837 struct ldb_val zero_value;
1838 ZERO_STRUCT(zero_value);
1839 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
1840 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1842 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
1845 /* we now fill the value which is already attached to ldb_message */
1846 ndr_err = ndr_push_struct_blob(nrf_value, msg,
1847 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1849 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
1850 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1851 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
1852 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
1856 * the ldb_message_element for the attribute, has all the old values and the new one
1857 * so we'll replace the whole attribute with all values
1859 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
1862 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
1863 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
1867 /* prepare the ldb_modify() request */
1868 ret = ldb_build_mod_req(&change_req,
1874 replmd_replicated_uptodate_modify_callback,
1876 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1878 return ldb_next_request(ar->module, change_req);
1881 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
1882 struct ldb_reply *ares)
1884 struct replmd_replicated_request *ar = talloc_get_type(req->context,
1885 struct replmd_replicated_request);
1889 return ldb_module_done(ar->req, NULL, NULL,
1890 LDB_ERR_OPERATIONS_ERROR);
1892 if (ares->error != LDB_SUCCESS &&
1893 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
1894 return ldb_module_done(ar->req, ares->controls,
1895 ares->response, ares->error);
1898 switch (ares->type) {
1899 case LDB_REPLY_ENTRY:
1900 ar->search_msg = talloc_steal(ar, ares->message);
1903 case LDB_REPLY_REFERRAL:
1904 /* we ignore referrals */
1907 case LDB_REPLY_DONE:
1908 if (ar->search_msg == NULL) {
1909 ret = replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
1911 ret = replmd_replicated_uptodate_modify(ar);
1913 if (ret != LDB_SUCCESS) {
1914 return ldb_module_done(ar->req, NULL, NULL, ret);
1923 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
1925 struct ldb_context *ldb;
1927 static const char *attrs[] = {
1928 "replUpToDateVector",
1932 struct ldb_request *search_req;
1934 ldb = ldb_module_get_ctx(ar->module);
1935 ar->search_msg = NULL;
1937 ret = ldb_build_search_req(&search_req,
1940 ar->objs->partition_dn,
1946 replmd_replicated_uptodate_search_callback,
1948 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
1950 return ldb_next_request(ar->module, search_req);
1955 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
1957 struct ldb_context *ldb;
1958 struct dsdb_extended_replicated_objects *objs;
1959 struct replmd_replicated_request *ar;
1960 struct ldb_control **ctrls;
1962 struct dsdb_control_current_partition *partition_ctrl;
1963 struct replmd_private *replmd_private =
1964 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
1966 ldb = ldb_module_get_ctx(module);
1968 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
1970 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
1972 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
1973 return LDB_ERR_PROTOCOL_ERROR;
1976 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
1977 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
1978 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
1979 return LDB_ERR_PROTOCOL_ERROR;
1982 ar = replmd_ctx_init(module, req);
1984 return LDB_ERR_OPERATIONS_ERROR;
1987 ar->schema = dsdb_get_schema(ldb);
1989 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
1991 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
1992 return LDB_ERR_CONSTRAINT_VIOLATION;
1995 ctrls = req->controls;
1997 if (req->controls) {
1998 req->controls = talloc_memdup(ar, req->controls,
1999 talloc_get_size(req->controls));
2000 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOMEM);
2003 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, NULL);
2004 if (ret != LDB_SUCCESS) {
2009 add the DSDB_CONTROL_CURRENT_PARTITION_OID control. This
2010 tells the partition module which partition this request is
2011 directed at. That is important as the partition roots appear
2012 twice in the directory, once as mount points in the top
2013 level store, and once as the roots of each partition. The
2014 replication code wants to operate on the root of the
2015 partitions, not the top level mount points
2017 partition_ctrl = talloc(req, struct dsdb_control_current_partition);
2018 if (partition_ctrl == NULL) {
2019 if (!partition_ctrl) return replmd_replicated_request_werror(ar, WERR_NOMEM);
2021 partition_ctrl->version = DSDB_CONTROL_CURRENT_PARTITION_VERSION;
2022 partition_ctrl->dn = objs->partition_dn;
2024 ret = ldb_request_add_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID, false, partition_ctrl);
2025 if (ret != LDB_SUCCESS) {
2029 ar->controls = req->controls;
2030 req->controls = ctrls;
2032 DEBUG(4,("linked_attributes_count=%u\n", objs->linked_attributes_count));
2034 /* save away the linked attributes for the end of the
2036 for (i=0; i<ar->objs->linked_attributes_count; i++) {
2037 struct la_entry *la_entry;
2039 if (replmd_private->la_ctx == NULL) {
2040 replmd_private->la_ctx = talloc_new(replmd_private);
2042 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
2043 if (la_entry == NULL) {
2045 return LDB_ERR_OPERATIONS_ERROR;
2047 la_entry->la = talloc(la_entry, struct drsuapi_DsReplicaLinkedAttribute);
2048 if (la_entry->la == NULL) {
2049 talloc_free(la_entry);
2051 return LDB_ERR_OPERATIONS_ERROR;
2053 *la_entry->la = ar->objs->linked_attributes[i];
2055 /* we need to steal the non-scalars so they stay
2056 around until the end of the transaction */
2057 talloc_steal(la_entry->la, la_entry->la->identifier);
2058 talloc_steal(la_entry->la, la_entry->la->value.blob);
2060 DLIST_ADD(replmd_private->la_list, la_entry);
2063 return replmd_replicated_apply_next(ar);
2067 process one linked attribute structure
2069 static int replmd_process_linked_attribute(struct ldb_module *module,
2070 struct la_entry *la_entry)
2072 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
2073 struct ldb_context *ldb = ldb_module_get_ctx(module);
2074 struct drsuapi_DsReplicaObjectIdentifier3 target;
2075 struct ldb_message *msg;
2076 struct ldb_message_element *ret_el;
2077 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
2078 enum ndr_err_code ndr_err;
2079 struct ldb_request *mod_req;
2081 const struct dsdb_attribute *attr;
2082 struct ldb_dn *target_dn;
2085 linked_attributes[0]:
2086 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
2088 identifier: struct drsuapi_DsReplicaObjectIdentifier
2089 __ndr_size : 0x0000003a (58)
2090 __ndr_size_sid : 0x00000000 (0)
2091 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
2093 __ndr_size_dn : 0x00000000 (0)
2095 attid : DRSUAPI_ATTRIBUTE_member (0x1F)
2096 value: struct drsuapi_DsAttributeValue
2097 __ndr_size : 0x0000007e (126)
2099 blob : DATA_BLOB length=126
2100 flags : 0x00000001 (1)
2101 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
2102 originating_add_time : Wed Sep 2 22:20:01 2009 EST
2103 meta_data: struct drsuapi_DsReplicaMetaData
2104 version : 0x00000015 (21)
2105 originating_change_time : Wed Sep 2 23:39:07 2009 EST
2106 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
2107 originating_usn : 0x000000000001e19c (123292)
2108 &target: struct drsuapi_DsReplicaObjectIdentifier3
2109 __ndr_size : 0x0000007e (126)
2110 __ndr_size_sid : 0x0000001c (28)
2111 guid : 7639e594-db75-4086-b0d4-67890ae46031
2112 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
2113 __ndr_size_dn : 0x00000022 (34)
2114 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
2117 NDR_PRINT_DEBUG(drsuapi_DsReplicaLinkedAttribute, la);
2120 /* decode the target of the link */
2121 ndr_err = ndr_pull_struct_blob(la->value.blob,
2122 tmp_ctx, lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
2124 (ndr_pull_flags_fn_t)ndr_pull_drsuapi_DsReplicaObjectIdentifier3);
2125 if (ndr_err != NDR_ERR_SUCCESS) {
2126 DEBUG(0,("Unable to decode linked_attribute target\n"));
2127 dump_data(4, la->value.blob->data, la->value.blob->length);
2128 talloc_free(tmp_ctx);
2129 return LDB_ERR_OPERATIONS_ERROR;
2132 NDR_PRINT_DEBUG(drsuapi_DsReplicaObjectIdentifier3, &target);
2135 /* construct a modify request for this attribute change */
2136 msg = ldb_msg_new(tmp_ctx);
2139 talloc_free(tmp_ctx);
2140 return LDB_ERR_OPERATIONS_ERROR;
2143 ret = dsdb_find_dn_by_guid(ldb, tmp_ctx,
2144 GUID_string(tmp_ctx, &la->identifier->guid), &msg->dn);
2145 if (ret != LDB_SUCCESS) {
2146 talloc_free(tmp_ctx);
2150 /* find the attribute being modified */
2151 attr = dsdb_attribute_by_attributeID_id(dsdb_get_schema(ldb), la->attid);
2153 DEBUG(0, (__location__ ": Unable to find attributeID 0x%x\n", la->attid));
2154 talloc_free(tmp_ctx);
2155 return LDB_ERR_OPERATIONS_ERROR;
2158 if (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE) {
2159 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName,
2160 LDB_FLAG_MOD_ADD, &ret_el);
2162 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName,
2163 LDB_FLAG_MOD_DELETE, &ret_el);
2165 if (ret != LDB_SUCCESS) {
2166 talloc_free(tmp_ctx);
2169 /* we allocate two entries here, in case we need a remove/add
2171 ret_el->values = talloc_array(msg, struct ldb_val, 2);
2172 if (!ret_el->values) {
2174 talloc_free(tmp_ctx);
2175 return LDB_ERR_OPERATIONS_ERROR;
2177 ret_el->num_values = 1;
2179 ret = dsdb_find_dn_by_guid(ldb, tmp_ctx, GUID_string(tmp_ctx, &target.guid), &target_dn);
2180 if (ret != LDB_SUCCESS) {
2181 DEBUG(0,(__location__ ": Failed to map GUID %s to DN\n", GUID_string(tmp_ctx, &target.guid)));
2182 talloc_free(tmp_ctx);
2183 return LDB_ERR_OPERATIONS_ERROR;
2186 ret_el->values[0].data = (uint8_t *)ldb_dn_get_extended_linearized(tmp_ctx, target_dn, 1);
2187 ret_el->values[0].length = strlen((char *)ret_el->values[0].data);
2189 ret = ldb_build_mod_req(&mod_req, ldb, tmp_ctx,
2193 ldb_op_default_callback,
2195 if (ret != LDB_SUCCESS) {
2196 talloc_free(tmp_ctx);
2199 talloc_steal(mod_req, msg);
2202 DEBUG(4,("Applying DRS linked attribute change:\n%s\n",
2203 ldb_ldif_message_string(ldb, tmp_ctx, LDB_CHANGETYPE_MODIFY, msg)));
2206 /* Run the new request */
2207 ret = ldb_next_request(module, mod_req);
2209 /* we need to wait for this to finish, as we are being called
2210 from the synchronous end_transaction hook of this module */
2211 if (ret == LDB_SUCCESS) {
2212 ret = ldb_wait(mod_req->handle, LDB_WAIT_ALL);
2215 if (ret == LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS) {
2216 /* the link destination exists, we need to update it
2217 * by deleting the old one for the same DN then adding
2219 msg->elements = talloc_realloc(msg, msg->elements,
2220 struct ldb_message_element,
2221 msg->num_elements+1);
2222 if (msg->elements == NULL) {
2224 talloc_free(tmp_ctx);
2225 return LDB_ERR_OPERATIONS_ERROR;
2227 /* this relies on the backend matching the old entry
2228 only by the DN portion of the extended DN */
2229 msg->elements[1] = msg->elements[0];
2230 msg->elements[0].flags = LDB_FLAG_MOD_DELETE;
2231 msg->num_elements++;
2233 ret = ldb_build_mod_req(&mod_req, ldb, tmp_ctx,
2237 ldb_op_default_callback,
2239 if (ret != LDB_SUCCESS) {
2240 talloc_free(tmp_ctx);
2244 /* Run the new request */
2245 ret = ldb_next_request(module, mod_req);
2247 if (ret == LDB_SUCCESS) {
2248 ret = ldb_wait(mod_req->handle, LDB_WAIT_ALL);
2252 if (ret != LDB_SUCCESS) {
2253 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s' %s\n",
2255 ldb_ldif_message_string(ldb, tmp_ctx, LDB_CHANGETYPE_MODIFY, msg));
2259 talloc_free(tmp_ctx);
2264 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
2266 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
2267 return replmd_extended_replicated_objects(module, req);
2270 return ldb_next_request(module, req);
2275 we hook into the transaction operations to allow us to
2276 perform the linked attribute updates at the end of the whole
2277 transaction. This allows a forward linked attribute to be created
2278 before the object is created. During a vampire, w2k8 sends us linked
2279 attributes before the objects they are part of.
2281 static int replmd_start_transaction(struct ldb_module *module)
2283 /* create our private structure for this transaction */
2285 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
2286 struct replmd_private);
2287 talloc_free(replmd_private->la_ctx);
2288 replmd_private->la_list = NULL;
2289 replmd_private->la_ctx = NULL;
2291 for (i=0; i<replmd_private->num_ncs; i++) {
2292 replmd_private->ncs[i].mod_usn = 0;
2295 return ldb_next_start_trans(module);
2299 on prepare commit we loop over our queued la_context structures and
2302 static int replmd_prepare_commit(struct ldb_module *module)
2304 struct replmd_private *replmd_private =
2305 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
2306 struct la_entry *la, *prev;
2309 /* walk the list backwards, to do the first entry first, as we
2310 * added the entries with DLIST_ADD() which puts them at the
2311 * start of the list */
2312 for (la = replmd_private->la_list; la && la->next; la=la->next) ;
2314 for (; la; la=prev) {
2316 DLIST_REMOVE(replmd_private->la_list, la);
2317 ret = replmd_process_linked_attribute(module, la);
2318 if (ret != LDB_SUCCESS) {
2319 talloc_free(replmd_private->la_ctx);
2320 replmd_private->la_list = NULL;
2321 replmd_private->la_ctx = NULL;
2326 talloc_free(replmd_private->la_ctx);
2327 replmd_private->la_list = NULL;
2328 replmd_private->la_ctx = NULL;
2330 /* possibly change @REPLCHANGED */
2331 ret = replmd_notify_store(module);
2332 if (ret != LDB_SUCCESS) {
2336 return ldb_next_prepare_commit(module);
2339 static int replmd_del_transaction(struct ldb_module *module)
2341 struct replmd_private *replmd_private =
2342 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
2343 talloc_free(replmd_private->la_ctx);
2344 replmd_private->la_list = NULL;
2345 replmd_private->la_ctx = NULL;
2346 return ldb_next_del_trans(module);
2350 _PUBLIC_ const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
2351 .name = "repl_meta_data",
2352 .init_context = replmd_init,
2354 .modify = replmd_modify,
2355 .rename = replmd_rename,
2356 .extended = replmd_extended,
2357 .start_transaction = replmd_start_transaction,
2358 .prepare_commit = replmd_prepare_commit,
2359 .del_transaction = replmd_del_transaction,