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"
48 #include "dsdb/samdb/ldb_modules/util.h"
49 #include "lib/util/binsearch.h"
51 #define W2K3_LINKED_ATTRIBUTES 1
53 struct replmd_private {
55 struct la_entry *la_list;
57 struct la_backlink *la_backlinks;
59 struct nc_entry *prev, *next;
66 struct la_entry *next, *prev;
67 struct drsuapi_DsReplicaLinkedAttribute *la;
70 struct replmd_replicated_request {
71 struct ldb_module *module;
72 struct ldb_request *req;
74 const struct dsdb_schema *schema;
76 /* the controls we pass down */
77 struct ldb_control **controls;
79 /* details for the mode where we apply a bunch of inbound replication meessages */
81 uint32_t index_current;
82 struct dsdb_extended_replicated_objects *objs;
84 struct ldb_message *search_msg;
90 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar);
95 allocate the private structure and build the list
96 of partition DNs for use by replmd_notify()
98 static int replmd_init(struct ldb_module *module)
100 struct replmd_private *replmd_private;
101 struct ldb_context *ldb = ldb_module_get_ctx(module);
103 replmd_private = talloc_zero(module, struct replmd_private);
104 if (replmd_private == NULL) {
106 return LDB_ERR_OPERATIONS_ERROR;
108 ldb_module_set_private(module, replmd_private);
110 return ldb_next_init(module);
114 cleanup our per-transaction contexts
116 static void replmd_txn_cleanup(struct replmd_private *replmd_private)
118 talloc_free(replmd_private->la_ctx);
119 replmd_private->la_list = NULL;
120 replmd_private->la_ctx = NULL;
122 talloc_free(replmd_private->bl_ctx);
123 replmd_private->la_backlinks = NULL;
124 replmd_private->bl_ctx = NULL;
129 struct la_backlink *next, *prev;
130 const char *attr_name;
131 struct GUID forward_guid, target_guid;
136 process a backlinks we accumulated during a transaction, adding and
137 deleting the backlinks from the target objects
139 static int replmd_process_backlink(struct ldb_module *module, struct la_backlink *bl)
141 struct ldb_dn *target_dn, *source_dn;
143 struct ldb_context *ldb = ldb_module_get_ctx(module);
144 struct ldb_message *msg;
145 TALLOC_CTX *tmp_ctx = talloc_new(bl);
151 - construct ldb_message
152 - either an add or a delete
154 ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->target_guid, &target_dn);
155 if (ret != LDB_SUCCESS) {
156 ldb_asprintf_errstring(ldb, "Failed to find target DN for linked attribute with GUID %s\n",
157 GUID_string(bl, &bl->target_guid));
158 talloc_free(tmp_ctx);
162 ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->forward_guid, &source_dn);
163 if (ret != LDB_SUCCESS) {
164 ldb_asprintf_errstring(ldb, "Failed to find source DN for linked attribute with GUID %s\n",
165 GUID_string(bl, &bl->forward_guid));
166 talloc_free(tmp_ctx);
170 msg = ldb_msg_new(tmp_ctx);
172 ldb_module_oom(module);
173 talloc_free(tmp_ctx);
174 return LDB_ERR_OPERATIONS_ERROR;
177 /* construct a ldb_message for adding/deleting the backlink */
179 dn_string = ldb_dn_get_extended_linearized(tmp_ctx, source_dn, 1);
181 ldb_module_oom(module);
182 talloc_free(tmp_ctx);
183 return LDB_ERR_OPERATIONS_ERROR;
185 ret = ldb_msg_add_steal_string(msg, bl->attr_name, dn_string);
186 if (ret != LDB_SUCCESS) {
187 talloc_free(tmp_ctx);
190 msg->elements[0].flags = bl->active?LDB_FLAG_MOD_ADD:LDB_FLAG_MOD_DELETE;
192 ret = dsdb_module_modify(module, msg, 0);
193 if (ret != LDB_SUCCESS) {
194 ldb_asprintf_errstring(ldb, "Failed to %s backlink from %s to %s - %s",
195 bl->active?"add":"remove",
196 ldb_dn_get_linearized(source_dn),
197 ldb_dn_get_linearized(target_dn),
199 talloc_free(tmp_ctx);
202 talloc_free(tmp_ctx);
207 add a backlink to the list of backlinks to add/delete in the prepare
210 static int replmd_add_backlink(struct ldb_module *module, const struct dsdb_schema *schema,
211 struct GUID *forward_guid, struct GUID *target_guid,
212 bool active, const struct dsdb_attribute *schema_attr, bool immediate)
214 const struct dsdb_attribute *target_attr;
215 struct la_backlink *bl;
216 struct replmd_private *replmd_private =
217 talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
219 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
222 * windows 2003 has a broken schema where the
223 * definition of msDS-IsDomainFor is missing (which is
224 * supposed to be the backlink of the
225 * msDS-HasDomainNCs attribute
230 /* see if its already in the list */
231 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
232 if (GUID_equal(forward_guid, &bl->forward_guid) &&
233 GUID_equal(target_guid, &bl->target_guid) &&
234 (target_attr->lDAPDisplayName == bl->attr_name ||
235 strcmp(target_attr->lDAPDisplayName, bl->attr_name) == 0)) {
241 /* we found an existing one */
242 if (bl->active == active) {
245 DLIST_REMOVE(replmd_private->la_backlinks, bl);
250 if (replmd_private->bl_ctx == NULL) {
251 replmd_private->bl_ctx = talloc_new(replmd_private);
252 if (replmd_private->bl_ctx == NULL) {
253 ldb_module_oom(module);
254 return LDB_ERR_OPERATIONS_ERROR;
259 bl = talloc(replmd_private->bl_ctx, struct la_backlink);
261 ldb_module_oom(module);
262 return LDB_ERR_OPERATIONS_ERROR;
265 bl->attr_name = target_attr->lDAPDisplayName;
266 bl->forward_guid = *forward_guid;
267 bl->target_guid = *target_guid;
270 /* the caller may ask for this backlink to be processed
273 int ret = replmd_process_backlink(module, bl);
278 DLIST_ADD(replmd_private->la_backlinks, bl);
285 * Callback for most write operations in this module:
287 * notify the repl task that a object has changed. The notifies are
288 * gathered up in the replmd_private structure then written to the
289 * @REPLCHANGED object in each partition during the prepare_commit
291 static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
294 struct replmd_replicated_request *ac =
295 talloc_get_type_abort(req->context, struct replmd_replicated_request);
296 struct replmd_private *replmd_private =
297 talloc_get_type_abort(ldb_module_get_private(ac->module), struct replmd_private);
298 struct nc_entry *modified_partition;
299 struct ldb_control *partition_ctrl;
300 const struct dsdb_control_current_partition *partition;
302 struct ldb_control **controls;
304 partition_ctrl = ldb_reply_get_control(ares, DSDB_CONTROL_CURRENT_PARTITION_OID);
306 /* Remove the 'partition' control from what we pass up the chain */
307 controls = controls_except_specified(ares->controls, ares, partition_ctrl);
309 if (ares->error != LDB_SUCCESS) {
310 return ldb_module_done(ac->req, controls,
311 ares->response, ares->error);
314 if (ares->type != LDB_REPLY_DONE) {
315 ldb_set_errstring(ldb_module_get_ctx(ac->module), "Invalid reply type for notify\n!");
316 return ldb_module_done(ac->req, NULL,
317 NULL, LDB_ERR_OPERATIONS_ERROR);
320 if (!partition_ctrl) {
321 ldb_set_errstring(ldb_module_get_ctx(ac->module),"No partition control on reply");
322 return ldb_module_done(ac->req, NULL,
323 NULL, LDB_ERR_OPERATIONS_ERROR);
326 partition = talloc_get_type_abort(partition_ctrl->data,
327 struct dsdb_control_current_partition);
329 if (ac->seq_num > 0) {
330 for (modified_partition = replmd_private->ncs; modified_partition;
331 modified_partition = modified_partition->next) {
332 if (ldb_dn_compare(modified_partition->dn, partition->dn) == 0) {
337 if (modified_partition == NULL) {
338 modified_partition = talloc_zero(replmd_private, struct nc_entry);
339 if (!modified_partition) {
340 ldb_oom(ldb_module_get_ctx(ac->module));
341 return ldb_module_done(ac->req, NULL,
342 NULL, LDB_ERR_OPERATIONS_ERROR);
344 modified_partition->dn = ldb_dn_copy(modified_partition, partition->dn);
345 if (!modified_partition->dn) {
346 ldb_oom(ldb_module_get_ctx(ac->module));
347 return ldb_module_done(ac->req, NULL,
348 NULL, LDB_ERR_OPERATIONS_ERROR);
350 DLIST_ADD(replmd_private->ncs, modified_partition);
353 if (ac->seq_num > modified_partition->mod_usn) {
354 modified_partition->mod_usn = ac->seq_num;
358 if (ac->apply_mode) {
362 ret = replmd_replicated_apply_next(ac);
363 if (ret != LDB_SUCCESS) {
364 return ldb_module_done(ac->req, NULL, NULL, ret);
368 /* free the partition control container here, for the
369 * common path. Other cases will have it cleaned up
370 * eventually with the ares */
371 talloc_free(partition_ctrl);
372 return ldb_module_done(ac->req,
373 controls_except_specified(controls, ares, partition_ctrl),
374 ares->response, LDB_SUCCESS);
380 * update a @REPLCHANGED record in each partition if there have been
381 * any writes of replicated data in the partition
383 static int replmd_notify_store(struct ldb_module *module)
385 struct replmd_private *replmd_private =
386 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
387 struct ldb_context *ldb = ldb_module_get_ctx(module);
389 while (replmd_private->ncs) {
391 struct nc_entry *modified_partition = replmd_private->ncs;
393 ret = dsdb_save_partition_usn(ldb, modified_partition->dn, modified_partition->mod_usn);
394 if (ret != LDB_SUCCESS) {
395 DEBUG(0,(__location__ ": Failed to save partition uSN for %s\n",
396 ldb_dn_get_linearized(modified_partition->dn)));
399 DLIST_REMOVE(replmd_private->ncs, modified_partition);
400 talloc_free(modified_partition);
408 created a replmd_replicated_request context
410 static struct replmd_replicated_request *replmd_ctx_init(struct ldb_module *module,
411 struct ldb_request *req)
413 struct ldb_context *ldb;
414 struct replmd_replicated_request *ac;
416 ldb = ldb_module_get_ctx(module);
418 ac = talloc_zero(req, struct replmd_replicated_request);
427 ac->schema = dsdb_get_schema(ldb);
429 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
430 "replmd_modify: no dsdb_schema loaded");
431 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
439 add a time element to a record
441 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
443 struct ldb_message_element *el;
446 if (ldb_msg_find_element(msg, attr) != NULL) {
450 s = ldb_timestring(msg, t);
452 return LDB_ERR_OPERATIONS_ERROR;
455 if (ldb_msg_add_string(msg, attr, s) != LDB_SUCCESS) {
456 return LDB_ERR_OPERATIONS_ERROR;
459 el = ldb_msg_find_element(msg, attr);
460 /* always set as replace. This works because on add ops, the flag
462 el->flags = LDB_FLAG_MOD_REPLACE;
468 add a uint64_t element to a record
470 static int add_uint64_element(struct ldb_message *msg, const char *attr, uint64_t v)
472 struct ldb_message_element *el;
474 if (ldb_msg_find_element(msg, attr) != NULL) {
478 if (ldb_msg_add_fmt(msg, attr, "%llu", (unsigned long long)v) != LDB_SUCCESS) {
479 return LDB_ERR_OPERATIONS_ERROR;
482 el = ldb_msg_find_element(msg, attr);
483 /* always set as replace. This works because on add ops, the flag
485 el->flags = LDB_FLAG_MOD_REPLACE;
490 static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1 *m1,
491 const struct replPropertyMetaData1 *m2,
492 const uint32_t *rdn_attid)
494 if (m1->attid == m2->attid) {
499 * the rdn attribute should be at the end!
500 * so we need to return a value greater than zero
501 * which means m1 is greater than m2
503 if (m1->attid == *rdn_attid) {
508 * the rdn attribute should be at the end!
509 * so we need to return a value less than zero
510 * which means m2 is greater than m1
512 if (m2->attid == *rdn_attid) {
516 return m1->attid > m2->attid ? 1 : -1;
519 static int replmd_replPropertyMetaDataCtr1_sort(struct replPropertyMetaDataCtr1 *ctr1,
520 const struct dsdb_schema *schema,
523 const char *rdn_name;
524 const struct dsdb_attribute *rdn_sa;
526 rdn_name = ldb_dn_get_rdn_name(dn);
528 DEBUG(0,(__location__ ": No rDN for %s?\n", ldb_dn_get_linearized(dn)));
529 return LDB_ERR_OPERATIONS_ERROR;
532 rdn_sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
533 if (rdn_sa == NULL) {
534 DEBUG(0,(__location__ ": No sa found for rDN %s for %s\n", rdn_name, ldb_dn_get_linearized(dn)));
535 return LDB_ERR_OPERATIONS_ERROR;
538 DEBUG(6,("Sorting rpmd with attid exception %u rDN=%s DN=%s\n",
539 rdn_sa->attributeID_id, rdn_name, ldb_dn_get_linearized(dn)));
541 ldb_qsort(ctr1->array, ctr1->count, sizeof(struct replPropertyMetaData1),
542 discard_const_p(void, &rdn_sa->attributeID_id),
543 (ldb_qsort_cmp_fn_t)replmd_replPropertyMetaData1_attid_sort);
548 static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1,
549 const struct ldb_message_element *e2,
550 const struct dsdb_schema *schema)
552 const struct dsdb_attribute *a1;
553 const struct dsdb_attribute *a2;
556 * TODO: make this faster by caching the dsdb_attribute pointer
557 * on the ldb_messag_element
560 a1 = dsdb_attribute_by_lDAPDisplayName(schema, e1->name);
561 a2 = dsdb_attribute_by_lDAPDisplayName(schema, e2->name);
564 * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
568 return strcasecmp(e1->name, e2->name);
570 if (a1->attributeID_id == a2->attributeID_id) {
573 return a1->attributeID_id > a2->attributeID_id ? 1 : -1;
576 static void replmd_ldb_message_sort(struct ldb_message *msg,
577 const struct dsdb_schema *schema)
579 ldb_qsort(msg->elements, msg->num_elements, sizeof(struct ldb_message_element),
580 discard_const_p(void, schema), (ldb_qsort_cmp_fn_t)replmd_ldb_message_element_attid_sort);
583 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
584 const struct GUID *invocation_id, uint64_t seq_num,
585 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted);
589 fix up linked attributes in replmd_add.
590 This involves setting up the right meta-data in extended DN
591 components, and creating backlinks to the object
593 static int replmd_add_fix_la(struct ldb_module *module, struct ldb_message_element *el,
594 uint64_t seq_num, const struct GUID *invocationId, time_t t,
595 struct GUID *guid, const struct dsdb_attribute *sa)
598 TALLOC_CTX *tmp_ctx = talloc_new(el->values);
599 struct ldb_context *ldb = ldb_module_get_ctx(module);
600 struct dsdb_schema *schema = dsdb_get_schema(ldb);
603 unix_to_nt_time(&now, t);
605 for (i=0; i<el->num_values; i++) {
606 struct ldb_val *v = &el->values[i];
607 struct dsdb_dn *dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, v, sa->syntax->ldap_oid);
608 struct GUID target_guid;
612 ret = replmd_build_la_val(el->values, v, dsdb_dn, invocationId,
613 seq_num, seq_num, now, 0, false);
614 if (ret != LDB_SUCCESS) {
615 talloc_free(tmp_ctx);
619 /* note that the DN already has the extended
620 components from the extended_dn_store module */
621 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
622 if (!NT_STATUS_IS_OK(status) || GUID_all_zero(&target_guid)) {
623 ret = dsdb_module_guid_by_dn(module, dsdb_dn->dn, &target_guid);
624 if (ret != LDB_SUCCESS) {
625 talloc_free(tmp_ctx);
630 ret = replmd_add_backlink(module, schema, guid, &target_guid, true, sa, false);
631 if (ret != LDB_SUCCESS) {
632 talloc_free(tmp_ctx);
637 talloc_free(tmp_ctx);
643 intercept add requests
645 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
647 struct ldb_context *ldb;
648 struct ldb_control *control;
649 struct replmd_replicated_request *ac;
650 enum ndr_err_code ndr_err;
651 struct ldb_request *down_req;
652 struct ldb_message *msg;
653 const DATA_BLOB *guid_blob;
655 struct replPropertyMetaDataBlob nmd;
656 struct ldb_val nmd_value;
657 const struct GUID *our_invocation_id;
658 time_t t = time(NULL);
663 bool allow_add_guid = false;
664 bool remove_current_guid = false;
666 /* check if there's a show relax control (used by provision to say 'I know what I'm doing') */
667 control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID);
672 /* do not manipulate our control entries */
673 if (ldb_dn_is_special(req->op.add.message->dn)) {
674 return ldb_next_request(module, req);
677 ldb = ldb_module_get_ctx(module);
679 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_add\n");
681 ac = replmd_ctx_init(module, req);
683 return LDB_ERR_OPERATIONS_ERROR;
686 guid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectGUID");
687 if ( guid_blob != NULL ) {
688 if( !allow_add_guid ) {
689 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
690 "replmd_add: it's not allowed to add an object with objectGUID\n");
692 return LDB_ERR_UNWILLING_TO_PERFORM;
694 NTSTATUS status = GUID_from_data_blob(guid_blob,&guid);
695 if ( !NT_STATUS_IS_OK(status)) {
696 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
697 "replmd_add: Unable to parse as a GUID the attribute objectGUID\n");
699 return LDB_ERR_UNWILLING_TO_PERFORM;
701 /* we remove this attribute as it can be a string and will not be treated
702 correctly and then we will readd it latter on in the good format*/
703 remove_current_guid = true;
707 guid = GUID_random();
710 /* Get a sequence number from the backend */
711 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
712 if (ret != LDB_SUCCESS) {
717 /* get our invocationId */
718 our_invocation_id = samdb_ntds_invocation_id(ldb);
719 if (!our_invocation_id) {
720 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
721 "replmd_add: unable to find invocationId\n");
723 return LDB_ERR_OPERATIONS_ERROR;
726 /* we have to copy the message as the caller might have it as a const */
727 msg = ldb_msg_copy_shallow(ac, req->op.add.message);
731 return LDB_ERR_OPERATIONS_ERROR;
734 /* generated times */
735 unix_to_nt_time(&now, t);
736 time_str = ldb_timestring(msg, t);
740 return LDB_ERR_OPERATIONS_ERROR;
742 if (remove_current_guid) {
743 ldb_msg_remove_attr(msg,"objectGUID");
747 * remove autogenerated attributes
749 ldb_msg_remove_attr(msg, "whenCreated");
750 ldb_msg_remove_attr(msg, "whenChanged");
751 ldb_msg_remove_attr(msg, "uSNCreated");
752 ldb_msg_remove_attr(msg, "uSNChanged");
753 ldb_msg_remove_attr(msg, "replPropertyMetaData");
756 * readd replicated attributes
758 ret = ldb_msg_add_string(msg, "whenCreated", time_str);
759 if (ret != LDB_SUCCESS) {
765 /* build the replication meta_data */
768 nmd.ctr.ctr1.count = msg->num_elements;
769 nmd.ctr.ctr1.array = talloc_array(msg,
770 struct replPropertyMetaData1,
772 if (!nmd.ctr.ctr1.array) {
775 return LDB_ERR_OPERATIONS_ERROR;
778 for (i=0; i < msg->num_elements; i++) {
779 struct ldb_message_element *e = &msg->elements[i];
780 struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
781 const struct dsdb_attribute *sa;
783 if (e->name[0] == '@') continue;
785 sa = dsdb_attribute_by_lDAPDisplayName(ac->schema, e->name);
787 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
788 "replmd_add: attribute '%s' not defined in schema\n",
791 return LDB_ERR_NO_SUCH_ATTRIBUTE;
794 if ((sa->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (sa->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
795 /* if the attribute is not replicated (0x00000001)
796 * or constructed (0x00000004) it has no metadata
801 #if W2K3_LINKED_ATTRIBUTES
802 if (sa->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
803 ret = replmd_add_fix_la(module, e, ac->seq_num, our_invocation_id, t, &guid, sa);
804 if (ret != LDB_SUCCESS) {
808 /* linked attributes are not stored in
809 replPropertyMetaData in FL above w2k */
814 m->attid = sa->attributeID_id;
816 m->originating_change_time = now;
817 m->originating_invocation_id = *our_invocation_id;
818 m->originating_usn = ac->seq_num;
819 m->local_usn = ac->seq_num;
823 /* fix meta data count */
824 nmd.ctr.ctr1.count = ni;
827 * sort meta data array, and move the rdn attribute entry to the end
829 ret = replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, ac->schema, msg->dn);
830 if (ret != LDB_SUCCESS) {
835 /* generated NDR encoded values */
836 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
837 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
839 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
840 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
843 return LDB_ERR_OPERATIONS_ERROR;
847 * add the autogenerated values
849 ret = dsdb_msg_add_guid(msg, &guid, "objectGUID");
850 if (ret != LDB_SUCCESS) {
855 ret = ldb_msg_add_string(msg, "whenChanged", time_str);
856 if (ret != LDB_SUCCESS) {
861 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ac->seq_num);
862 if (ret != LDB_SUCCESS) {
867 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ac->seq_num);
868 if (ret != LDB_SUCCESS) {
873 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
874 if (ret != LDB_SUCCESS) {
881 * sort the attributes by attid before storing the object
883 replmd_ldb_message_sort(msg, ac->schema);
885 ret = ldb_build_add_req(&down_req, ldb, ac,
888 ac, replmd_op_callback,
890 if (ret != LDB_SUCCESS) {
895 /* mark the control done */
897 control->critical = 0;
900 /* go on with the call chain */
901 return ldb_next_request(module, down_req);
906 * update the replPropertyMetaData for one element
908 static int replmd_update_rpmd_element(struct ldb_context *ldb,
909 struct ldb_message *msg,
910 struct ldb_message_element *el,
911 struct replPropertyMetaDataBlob *omd,
912 const struct dsdb_schema *schema,
914 const struct GUID *our_invocation_id,
918 const struct dsdb_attribute *a;
919 struct replPropertyMetaData1 *md1;
921 a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
923 DEBUG(0,(__location__ ": Unable to find attribute %s in schema\n",
925 return LDB_ERR_OPERATIONS_ERROR;
928 if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
932 for (i=0; i<omd->ctr.ctr1.count; i++) {
933 if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) break;
936 #if W2K3_LINKED_ATTRIBUTES
937 if (a->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
938 /* linked attributes are not stored in
939 replPropertyMetaData in FL above w2k, but we do
940 raise the seqnum for the object */
942 ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num) != LDB_SUCCESS) {
943 return LDB_ERR_OPERATIONS_ERROR;
949 if (i == omd->ctr.ctr1.count) {
950 /* we need to add a new one */
951 omd->ctr.ctr1.array = talloc_realloc(msg, omd->ctr.ctr1.array,
952 struct replPropertyMetaData1, omd->ctr.ctr1.count+1);
953 if (omd->ctr.ctr1.array == NULL) {
955 return LDB_ERR_OPERATIONS_ERROR;
957 omd->ctr.ctr1.count++;
958 ZERO_STRUCT(omd->ctr.ctr1.array[i]);
961 /* Get a new sequence number from the backend. We only do this
962 * if we have a change that requires a new
963 * replPropertyMetaData element
966 int ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num);
967 if (ret != LDB_SUCCESS) {
968 return LDB_ERR_OPERATIONS_ERROR;
972 md1 = &omd->ctr.ctr1.array[i];
974 md1->attid = a->attributeID_id;
975 md1->originating_change_time = now;
976 md1->originating_invocation_id = *our_invocation_id;
977 md1->originating_usn = *seq_num;
978 md1->local_usn = *seq_num;
984 * update the replPropertyMetaData object each time we modify an
985 * object. This is needed for DRS replication, as the merge on the
986 * client is based on this object
988 static int replmd_update_rpmd(struct ldb_module *module,
989 const struct dsdb_schema *schema,
990 struct ldb_message *msg, uint64_t *seq_num,
993 const struct ldb_val *omd_value;
994 enum ndr_err_code ndr_err;
995 struct replPropertyMetaDataBlob omd;
998 const struct GUID *our_invocation_id;
1000 const char *attrs[] = { "replPropertyMetaData" , NULL };
1001 struct ldb_result *res;
1002 struct ldb_context *ldb;
1004 ldb = ldb_module_get_ctx(module);
1006 our_invocation_id = samdb_ntds_invocation_id(ldb);
1007 if (!our_invocation_id) {
1008 /* this happens during an initial vampire while
1009 updating the schema */
1010 DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
1014 unix_to_nt_time(&now, t);
1016 /* search for the existing replPropertyMetaDataBlob */
1017 ret = dsdb_search_dn_with_deleted(ldb, msg, &res, msg->dn, attrs);
1018 if (ret != LDB_SUCCESS || res->count != 1) {
1019 DEBUG(0,(__location__ ": Object %s failed to find replPropertyMetaData\n",
1020 ldb_dn_get_linearized(msg->dn)));
1021 return LDB_ERR_OPERATIONS_ERROR;
1025 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
1027 DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
1028 ldb_dn_get_linearized(msg->dn)));
1029 return LDB_ERR_OPERATIONS_ERROR;
1032 ndr_err = ndr_pull_struct_blob(omd_value, msg,
1033 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), &omd,
1034 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1035 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1036 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1037 ldb_dn_get_linearized(msg->dn)));
1038 return LDB_ERR_OPERATIONS_ERROR;
1041 if (omd.version != 1) {
1042 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
1043 omd.version, ldb_dn_get_linearized(msg->dn)));
1044 return LDB_ERR_OPERATIONS_ERROR;
1047 for (i=0; i<msg->num_elements; i++) {
1048 ret = replmd_update_rpmd_element(ldb, msg, &msg->elements[i], &omd, schema, seq_num,
1049 our_invocation_id, now);
1050 if (ret != LDB_SUCCESS) {
1056 * replmd_update_rpmd_element has done an update if the
1059 if (*seq_num != 0) {
1060 struct ldb_val *md_value;
1061 struct ldb_message_element *el;
1063 md_value = talloc(msg, struct ldb_val);
1064 if (md_value == NULL) {
1066 return LDB_ERR_OPERATIONS_ERROR;
1069 ret = replmd_replPropertyMetaDataCtr1_sort(&omd.ctr.ctr1, schema, msg->dn);
1070 if (ret != LDB_SUCCESS) {
1074 ndr_err = ndr_push_struct_blob(md_value, msg,
1075 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1077 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1078 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1079 DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
1080 ldb_dn_get_linearized(msg->dn)));
1081 return LDB_ERR_OPERATIONS_ERROR;
1084 ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
1085 if (ret != LDB_SUCCESS) {
1086 DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
1087 ldb_dn_get_linearized(msg->dn)));
1092 el->values = md_value;
1100 struct dsdb_dn *dsdb_dn;
1105 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
1107 return GUID_compare(pdn1->guid, pdn2->guid);
1110 static struct parsed_dn *parsed_dn_find(struct parsed_dn *pdn, int count, struct GUID *guid, struct ldb_dn *dn)
1112 struct parsed_dn *ret;
1113 if (dn && GUID_all_zero(guid)) {
1114 /* when updating a link using DRS, we sometimes get a
1115 NULL GUID. We then need to try and match by DN */
1117 for (i=0; i<count; i++) {
1118 if (ldb_dn_compare(pdn[i].dsdb_dn->dn, dn) == 0) {
1119 dsdb_get_extended_dn_guid(pdn[i].dsdb_dn->dn, guid, "GUID");
1125 BINARY_ARRAY_SEARCH(pdn, count, guid, guid, GUID_compare, ret);
1130 get a series of message element values as an array of DNs and GUIDs
1131 the result is sorted by GUID
1133 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
1134 struct ldb_message_element *el, struct parsed_dn **pdn,
1135 const char *ldap_oid)
1138 struct ldb_context *ldb = ldb_module_get_ctx(module);
1145 (*pdn) = talloc_array(mem_ctx, struct parsed_dn, el->num_values);
1147 ldb_module_oom(module);
1148 return LDB_ERR_OPERATIONS_ERROR;
1151 for (i=0; i<el->num_values; i++) {
1152 struct ldb_val *v = &el->values[i];
1155 struct parsed_dn *p;
1159 p->dsdb_dn = dsdb_dn_parse(*pdn, ldb, v, ldap_oid);
1160 if (p->dsdb_dn == NULL) {
1161 return LDB_ERR_INVALID_DN_SYNTAX;
1164 dn = p->dsdb_dn->dn;
1166 p->guid = talloc(*pdn, struct GUID);
1167 if (p->guid == NULL) {
1168 ldb_module_oom(module);
1169 return LDB_ERR_OPERATIONS_ERROR;
1172 status = dsdb_get_extended_dn_guid(dn, p->guid, "GUID");
1173 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1174 /* we got a DN without a GUID - go find the GUID */
1175 int ret = dsdb_find_guid_by_dn(ldb, dn, p->guid);
1176 if (ret != LDB_SUCCESS) {
1177 ldb_asprintf_errstring(ldb, "Unable to find GUID for DN %s\n",
1178 ldb_dn_get_linearized(dn));
1181 } else if (!NT_STATUS_IS_OK(status)) {
1182 return LDB_ERR_OPERATIONS_ERROR;
1185 /* keep a pointer to the original ldb_val */
1189 qsort(*pdn, el->num_values, sizeof((*pdn)[0]), (comparison_fn_t)parsed_dn_compare);
1195 build a new extended DN, including all meta data fields
1197 DELETED = 1 or missing
1198 RMD_ADDTIME = originating_add_time
1199 RMD_INVOCID = originating_invocation_id
1200 RMD_CHANGETIME = originating_change_time
1201 RMD_ORIGINATING_USN = originating_usn
1202 RMD_LOCAL_USN = local_usn
1203 RMD_VERSION = version
1205 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1206 const struct GUID *invocation_id, uint64_t seq_num,
1207 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted)
1209 struct ldb_dn *dn = dsdb_dn->dn;
1210 const char *tstring, *usn_string;
1211 struct ldb_val tval;
1213 struct ldb_val usnv, local_usnv;
1214 struct ldb_val vers;
1217 const char *dnstring;
1220 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
1222 return LDB_ERR_OPERATIONS_ERROR;
1224 tval = data_blob_string_const(tstring);
1226 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
1228 return LDB_ERR_OPERATIONS_ERROR;
1230 usnv = data_blob_string_const(usn_string);
1232 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
1234 return LDB_ERR_OPERATIONS_ERROR;
1236 local_usnv = data_blob_string_const(usn_string);
1238 vstring = talloc_asprintf(mem_ctx, "%lu", (unsigned long)version);
1239 vers = data_blob_string_const(vstring);
1241 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1242 if (!NT_STATUS_IS_OK(status)) {
1243 return LDB_ERR_OPERATIONS_ERROR;
1248 dv = data_blob_string_const("1");
1249 ret = ldb_dn_set_extended_component(dn, "DELETED", &dv);
1251 ret = ldb_dn_set_extended_component(dn, "DELETED", NULL);
1253 if (ret != LDB_SUCCESS) return ret;
1254 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", &tval);
1255 if (ret != LDB_SUCCESS) return ret;
1256 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1257 if (ret != LDB_SUCCESS) return ret;
1258 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1259 if (ret != LDB_SUCCESS) return ret;
1260 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
1261 if (ret != LDB_SUCCESS) return ret;
1262 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
1263 if (ret != LDB_SUCCESS) return ret;
1264 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1265 if (ret != LDB_SUCCESS) return ret;
1267 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
1268 if (dnstring == NULL) {
1269 return LDB_ERR_OPERATIONS_ERROR;
1271 *v = data_blob_string_const(dnstring);
1276 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1277 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1278 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
1279 uint32_t version, bool deleted);
1282 check if any links need upgrading from w2k format
1284 static int replmd_check_upgrade_links(struct parsed_dn *dns, uint32_t count, const struct GUID *invocation_id)
1287 for (i=0; i<count; i++) {
1292 status = dsdb_get_extended_dn_uint32(dns[i].dsdb_dn->dn, &version, "RMD_VERSION");
1293 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1297 /* it's an old one that needs upgrading */
1298 ret = replmd_update_la_val(dns, dns[i].v, dns[i].dsdb_dn, dns[i].dsdb_dn, invocation_id,
1300 if (ret != LDB_SUCCESS) {
1308 update an extended DN, including all meta data fields
1310 see replmd_build_la_val for value names
1312 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1313 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1314 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
1315 uint32_t version, bool deleted)
1317 struct ldb_dn *dn = dsdb_dn->dn;
1318 const char *tstring, *usn_string;
1319 struct ldb_val tval;
1321 struct ldb_val usnv, local_usnv;
1322 struct ldb_val vers;
1323 const struct ldb_val *old_addtime;
1324 uint32_t old_version;
1327 const char *dnstring;
1330 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
1332 return LDB_ERR_OPERATIONS_ERROR;
1334 tval = data_blob_string_const(tstring);
1336 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
1338 return LDB_ERR_OPERATIONS_ERROR;
1340 usnv = data_blob_string_const(usn_string);
1342 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
1344 return LDB_ERR_OPERATIONS_ERROR;
1346 local_usnv = data_blob_string_const(usn_string);
1348 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1349 if (!NT_STATUS_IS_OK(status)) {
1350 return LDB_ERR_OPERATIONS_ERROR;
1355 dv = data_blob_string_const("1");
1356 ret = ldb_dn_set_extended_component(dn, "DELETED", &dv);
1358 ret = ldb_dn_set_extended_component(dn, "DELETED", NULL);
1360 if (ret != LDB_SUCCESS) return ret;
1362 /* get the ADDTIME from the original */
1363 old_addtime = ldb_dn_get_extended_component(old_dsdb_dn->dn, "RMD_ADDTIME");
1364 if (old_addtime == NULL) {
1365 old_addtime = &tval;
1367 if (dsdb_dn != old_dsdb_dn) {
1368 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", old_addtime);
1369 if (ret != LDB_SUCCESS) return ret;
1372 /* use our invocation id */
1373 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1374 if (ret != LDB_SUCCESS) return ret;
1376 /* changetime is the current time */
1377 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1378 if (ret != LDB_SUCCESS) return ret;
1380 /* update the USN */
1381 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
1382 if (ret != LDB_SUCCESS) return ret;
1384 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
1385 if (ret != LDB_SUCCESS) return ret;
1387 /* increase the version by 1 */
1388 status = dsdb_get_extended_dn_uint32(old_dsdb_dn->dn, &old_version, "RMD_VERSION");
1389 if (NT_STATUS_IS_OK(status) && old_version >= version) {
1390 version = old_version+1;
1392 vstring = talloc_asprintf(dn, "%lu", (unsigned long)version);
1393 vers = data_blob_string_const(vstring);
1394 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1395 if (ret != LDB_SUCCESS) return ret;
1397 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
1398 if (dnstring == NULL) {
1399 return LDB_ERR_OPERATIONS_ERROR;
1401 *v = data_blob_string_const(dnstring);
1407 handle adding a linked attribute
1409 static int replmd_modify_la_add(struct ldb_module *module,
1410 struct dsdb_schema *schema,
1411 struct ldb_message *msg,
1412 struct ldb_message_element *el,
1413 struct ldb_message_element *old_el,
1414 const struct dsdb_attribute *schema_attr,
1417 struct GUID *msg_guid)
1420 struct parsed_dn *dns, *old_dns;
1421 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1423 struct ldb_val *new_values = NULL;
1424 unsigned int num_new_values = 0;
1425 unsigned old_num_values = old_el?old_el->num_values:0;
1426 const struct GUID *invocation_id;
1427 struct ldb_context *ldb = ldb_module_get_ctx(module);
1430 unix_to_nt_time(&now, t);
1432 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid);
1433 if (ret != LDB_SUCCESS) {
1434 talloc_free(tmp_ctx);
1438 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid);
1439 if (ret != LDB_SUCCESS) {
1440 talloc_free(tmp_ctx);
1444 invocation_id = samdb_ntds_invocation_id(ldb);
1445 if (!invocation_id) {
1446 talloc_free(tmp_ctx);
1447 return LDB_ERR_OPERATIONS_ERROR;
1450 ret = replmd_check_upgrade_links(old_dns, old_num_values, invocation_id);
1451 if (ret != LDB_SUCCESS) {
1452 talloc_free(tmp_ctx);
1456 /* for each new value, see if it exists already with the same GUID */
1457 for (i=0; i<el->num_values; i++) {
1458 struct parsed_dn *p = parsed_dn_find(old_dns, old_num_values, dns[i].guid, NULL);
1460 /* this is a new linked attribute value */
1461 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val, num_new_values+1);
1462 if (new_values == NULL) {
1463 ldb_module_oom(module);
1464 talloc_free(tmp_ctx);
1465 return LDB_ERR_OPERATIONS_ERROR;
1467 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
1468 invocation_id, seq_num, seq_num, now, 0, false);
1469 if (ret != LDB_SUCCESS) {
1470 talloc_free(tmp_ctx);
1475 /* this is only allowed if the GUID was
1476 previously deleted. */
1477 const struct ldb_val *v;
1478 v = ldb_dn_get_extended_component(p->dsdb_dn->dn, "DELETED");
1480 ldb_asprintf_errstring(ldb, "Attribute %s already exists for target GUID %s",
1481 el->name, GUID_string(tmp_ctx, p->guid));
1482 talloc_free(tmp_ctx);
1483 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
1485 ret = replmd_update_la_val(old_el->values, p->v, dns[i].dsdb_dn, p->dsdb_dn,
1486 invocation_id, seq_num, seq_num, now, 0, false);
1487 if (ret != LDB_SUCCESS) {
1488 talloc_free(tmp_ctx);
1493 ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, true);
1494 if (ret != LDB_SUCCESS) {
1495 talloc_free(tmp_ctx);
1500 /* add the new ones on to the end of the old values, constructing a new el->values */
1501 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
1503 old_num_values+num_new_values);
1504 if (el->values == NULL) {
1505 ldb_module_oom(module);
1506 return LDB_ERR_OPERATIONS_ERROR;
1509 memcpy(&el->values[old_num_values], new_values, num_new_values*sizeof(struct ldb_val));
1510 el->num_values = old_num_values + num_new_values;
1512 talloc_steal(msg->elements, el->values);
1513 talloc_steal(el->values, new_values);
1515 talloc_free(tmp_ctx);
1517 /* we now tell the backend to replace all existing values
1518 with the one we have constructed */
1519 el->flags = LDB_FLAG_MOD_REPLACE;
1526 handle deleting all active linked attributes
1528 static int replmd_modify_la_delete(struct ldb_module *module,
1529 struct dsdb_schema *schema,
1530 struct ldb_message *msg,
1531 struct ldb_message_element *el,
1532 struct ldb_message_element *old_el,
1533 const struct dsdb_attribute *schema_attr,
1536 struct GUID *msg_guid)
1539 struct parsed_dn *dns, *old_dns;
1540 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1542 const struct GUID *invocation_id;
1543 struct ldb_context *ldb = ldb_module_get_ctx(module);
1546 unix_to_nt_time(&now, t);
1548 /* check if there is nothing to delete */
1549 if ((!old_el || old_el->num_values == 0) &&
1550 el->num_values == 0) {
1554 if (!old_el || old_el->num_values == 0) {
1555 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1558 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid);
1559 if (ret != LDB_SUCCESS) {
1560 talloc_free(tmp_ctx);
1564 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid);
1565 if (ret != LDB_SUCCESS) {
1566 talloc_free(tmp_ctx);
1570 invocation_id = samdb_ntds_invocation_id(ldb);
1571 if (!invocation_id) {
1572 return LDB_ERR_OPERATIONS_ERROR;
1575 ret = replmd_check_upgrade_links(old_dns, old_el->num_values, invocation_id);
1576 if (ret != LDB_SUCCESS) {
1577 talloc_free(tmp_ctx);
1583 /* see if we are being asked to delete any links that
1584 don't exist or are already deleted */
1585 for (i=0; i<el->num_values; i++) {
1586 struct parsed_dn *p = &dns[i];
1587 struct parsed_dn *p2;
1588 const struct ldb_val *v;
1590 p2 = parsed_dn_find(old_dns, old_el->num_values, p->guid, NULL);
1592 ldb_asprintf_errstring(ldb, "Attribute %s doesn't exist for target GUID %s",
1593 el->name, GUID_string(tmp_ctx, p->guid));
1594 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1596 v = ldb_dn_get_extended_component(p2->dsdb_dn->dn, "DELETED");
1598 ldb_asprintf_errstring(ldb, "Attribute %s already deleted for target GUID %s",
1599 el->name, GUID_string(tmp_ctx, p->guid));
1600 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1604 /* for each new value, see if it exists already with the same GUID
1605 if it is not already deleted and matches the delete list then delete it
1607 for (i=0; i<old_el->num_values; i++) {
1608 struct parsed_dn *p = &old_dns[i];
1609 const struct ldb_val *v;
1611 if (el->num_values && parsed_dn_find(dns, el->num_values, p->guid, NULL) == NULL) {
1615 v = ldb_dn_get_extended_component(p->dsdb_dn->dn, "DELETED");
1616 if (v != NULL) continue;
1618 ret = replmd_update_la_val(old_el->values, p->v, p->dsdb_dn, p->dsdb_dn,
1619 invocation_id, seq_num, seq_num, now, 0, true);
1620 if (ret != LDB_SUCCESS) {
1621 talloc_free(tmp_ctx);
1625 ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, true);
1626 if (ret != LDB_SUCCESS) {
1627 talloc_free(tmp_ctx);
1632 el->values = talloc_steal(msg->elements, old_el->values);
1633 el->num_values = old_el->num_values;
1635 talloc_free(tmp_ctx);
1637 /* we now tell the backend to replace all existing values
1638 with the one we have constructed */
1639 el->flags = LDB_FLAG_MOD_REPLACE;
1645 handle replacing a linked attribute
1647 static int replmd_modify_la_replace(struct ldb_module *module,
1648 struct dsdb_schema *schema,
1649 struct ldb_message *msg,
1650 struct ldb_message_element *el,
1651 struct ldb_message_element *old_el,
1652 const struct dsdb_attribute *schema_attr,
1655 struct GUID *msg_guid)
1658 struct parsed_dn *dns, *old_dns;
1659 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1661 const struct GUID *invocation_id;
1662 struct ldb_context *ldb = ldb_module_get_ctx(module);
1663 struct ldb_val *new_values = NULL;
1664 uint32_t num_new_values = 0;
1665 unsigned old_num_values = old_el?old_el->num_values:0;
1668 unix_to_nt_time(&now, t);
1670 /* check if there is nothing to replace */
1671 if ((!old_el || old_el->num_values == 0) &&
1672 el->num_values == 0) {
1676 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid);
1677 if (ret != LDB_SUCCESS) {
1678 talloc_free(tmp_ctx);
1682 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid);
1683 if (ret != LDB_SUCCESS) {
1684 talloc_free(tmp_ctx);
1688 invocation_id = samdb_ntds_invocation_id(ldb);
1689 if (!invocation_id) {
1690 return LDB_ERR_OPERATIONS_ERROR;
1693 ret = replmd_check_upgrade_links(old_dns, old_num_values, invocation_id);
1694 if (ret != LDB_SUCCESS) {
1695 talloc_free(tmp_ctx);
1699 /* mark all the old ones as deleted */
1700 for (i=0; i<old_num_values; i++) {
1701 struct parsed_dn *old_p = &old_dns[i];
1702 struct parsed_dn *p;
1703 const struct ldb_val *v;
1705 v = ldb_dn_get_extended_component(old_p->dsdb_dn->dn, "DELETED");
1708 ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, false);
1709 if (ret != LDB_SUCCESS) {
1710 talloc_free(tmp_ctx);
1714 p = parsed_dn_find(dns, el->num_values, old_p->guid, NULL);
1716 /* we don't delete it if we are re-adding it */
1720 ret = replmd_update_la_val(old_el->values, old_p->v, old_p->dsdb_dn, old_p->dsdb_dn,
1721 invocation_id, seq_num, seq_num, now, 0, true);
1722 if (ret != LDB_SUCCESS) {
1723 talloc_free(tmp_ctx);
1728 /* for each new value, either update its meta-data, or add it
1731 for (i=0; i<el->num_values; i++) {
1732 struct parsed_dn *p = &dns[i], *old_p;
1735 (old_p = parsed_dn_find(old_dns,
1736 old_num_values, p->guid, NULL)) != NULL) {
1737 /* update in place */
1738 ret = replmd_update_la_val(old_el->values, old_p->v, old_p->dsdb_dn,
1739 old_p->dsdb_dn, invocation_id,
1740 seq_num, seq_num, now, 0, false);
1741 if (ret != LDB_SUCCESS) {
1742 talloc_free(tmp_ctx);
1747 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val,
1749 if (new_values == NULL) {
1750 ldb_module_oom(module);
1751 talloc_free(tmp_ctx);
1752 return LDB_ERR_OPERATIONS_ERROR;
1754 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
1755 invocation_id, seq_num, seq_num, now, 0, false);
1756 if (ret != LDB_SUCCESS) {
1757 talloc_free(tmp_ctx);
1763 ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, false);
1764 if (ret != LDB_SUCCESS) {
1765 talloc_free(tmp_ctx);
1770 /* add the new values to the end of old_el */
1771 if (num_new_values != 0) {
1772 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
1773 struct ldb_val, old_num_values+num_new_values);
1774 if (el->values == NULL) {
1775 ldb_module_oom(module);
1776 return LDB_ERR_OPERATIONS_ERROR;
1778 memcpy(&el->values[old_num_values], &new_values[0],
1779 sizeof(struct ldb_val)*num_new_values);
1780 el->num_values = old_num_values + num_new_values;
1781 talloc_steal(msg->elements, new_values);
1783 el->values = old_el->values;
1784 el->num_values = old_el->num_values;
1785 talloc_steal(msg->elements, el->values);
1788 talloc_free(tmp_ctx);
1790 /* we now tell the backend to replace all existing values
1791 with the one we have constructed */
1792 el->flags = LDB_FLAG_MOD_REPLACE;
1799 handle linked attributes in modify requests
1801 static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
1802 struct ldb_message *msg,
1803 uint64_t seq_num, time_t t)
1805 struct ldb_result *res;
1807 struct ldb_context *ldb = ldb_module_get_ctx(module);
1808 struct ldb_message *old_msg;
1809 struct dsdb_schema *schema = dsdb_get_schema(ldb);
1810 struct GUID old_guid;
1813 /* there the replmd_update_rpmd code has already
1814 * checked and saw that there are no linked
1819 #if !W2K3_LINKED_ATTRIBUTES
1823 if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
1824 /* don't do anything special for linked attributes */
1828 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
1829 DSDB_SEARCH_SHOW_DELETED |
1830 DSDB_SEARCH_REVEAL_INTERNALS |
1831 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT);
1832 if (ret != LDB_SUCCESS) {
1835 old_msg = res->msgs[0];
1837 old_guid = samdb_result_guid(old_msg, "objectGUID");
1839 for (i=0; i<msg->num_elements; i++) {
1840 struct ldb_message_element *el = &msg->elements[i];
1841 struct ldb_message_element *old_el, *new_el;
1842 const struct dsdb_attribute *schema_attr
1843 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1845 ldb_asprintf_errstring(ldb,
1846 "attribute %s is not a valid attribute in schema", el->name);
1847 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1849 if (schema_attr->linkID == 0) {
1852 if ((schema_attr->linkID & 1) == 1) {
1853 /* Odd is for the target. Illegal to modify */
1854 ldb_asprintf_errstring(ldb,
1855 "attribute %s must not be modified directly, it is a linked attribute", el->name);
1856 return LDB_ERR_UNWILLING_TO_PERFORM;
1858 old_el = ldb_msg_find_element(old_msg, el->name);
1859 switch (el->flags & LDB_FLAG_MOD_MASK) {
1860 case LDB_FLAG_MOD_REPLACE:
1861 ret = replmd_modify_la_replace(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid);
1863 case LDB_FLAG_MOD_DELETE:
1864 ret = replmd_modify_la_delete(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid);
1866 case LDB_FLAG_MOD_ADD:
1867 ret = replmd_modify_la_add(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid);
1870 ldb_asprintf_errstring(ldb,
1871 "invalid flags 0x%x for %s linked attribute",
1872 el->flags, el->name);
1873 return LDB_ERR_UNWILLING_TO_PERFORM;
1875 if (ret != LDB_SUCCESS) {
1879 ldb_msg_remove_attr(old_msg, el->name);
1881 ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
1882 new_el->num_values = el->num_values;
1883 new_el->values = el->values;
1885 /* TODO: this relises a bit too heavily on the exact
1886 behaviour of ldb_msg_find_element and
1887 ldb_msg_remove_element */
1888 old_el = ldb_msg_find_element(msg, el->name);
1890 ldb_msg_remove_element(msg, old_el);
1901 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
1903 struct ldb_context *ldb;
1904 struct replmd_replicated_request *ac;
1905 struct ldb_request *down_req;
1906 struct ldb_message *msg;
1907 time_t t = time(NULL);
1910 /* do not manipulate our control entries */
1911 if (ldb_dn_is_special(req->op.mod.message->dn)) {
1912 return ldb_next_request(module, req);
1915 ldb = ldb_module_get_ctx(module);
1917 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
1919 ac = replmd_ctx_init(module, req);
1921 return LDB_ERR_OPERATIONS_ERROR;
1924 /* we have to copy the message as the caller might have it as a const */
1925 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
1929 return LDB_ERR_OPERATIONS_ERROR;
1932 ret = replmd_update_rpmd(module, ac->schema, msg, &ac->seq_num, t);
1933 if (ret != LDB_SUCCESS) {
1938 ret = replmd_modify_handle_linked_attribs(module, msg, ac->seq_num, t);
1939 if (ret != LDB_SUCCESS) {
1945 * - replace the old object with the newly constructed one
1948 ret = ldb_build_mod_req(&down_req, ldb, ac,
1951 ac, replmd_op_callback,
1953 if (ret != LDB_SUCCESS) {
1957 talloc_steal(down_req, msg);
1959 /* we only change whenChanged and uSNChanged if the seq_num
1961 if (ac->seq_num != 0) {
1962 if (add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) {
1967 if (add_uint64_element(msg, "uSNChanged", ac->seq_num) != LDB_SUCCESS) {
1973 /* go on with the call chain */
1974 return ldb_next_request(module, down_req);
1977 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares);
1980 handle a rename request
1982 On a rename we need to do an extra ldb_modify which sets the
1983 whenChanged and uSNChanged attributes. We do this in a callback after the success.
1985 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
1987 struct ldb_context *ldb;
1988 struct replmd_replicated_request *ac;
1990 struct ldb_request *down_req;
1992 /* do not manipulate our control entries */
1993 if (ldb_dn_is_special(req->op.mod.message->dn)) {
1994 return ldb_next_request(module, req);
1997 ldb = ldb_module_get_ctx(module);
1999 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
2001 ac = replmd_ctx_init(module, req);
2003 return LDB_ERR_OPERATIONS_ERROR;
2005 ret = ldb_build_rename_req(&down_req, ldb, ac,
2006 ac->req->op.rename.olddn,
2007 ac->req->op.rename.newdn,
2009 ac, replmd_rename_callback,
2012 if (ret != LDB_SUCCESS) {
2017 /* go on with the call chain */
2018 return ldb_next_request(module, down_req);
2021 /* After the rename is compleated, update the whenchanged etc */
2022 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
2024 struct ldb_context *ldb;
2025 struct replmd_replicated_request *ac;
2026 struct ldb_request *down_req;
2027 struct ldb_message *msg;
2028 time_t t = time(NULL);
2031 ac = talloc_get_type(req->context, struct replmd_replicated_request);
2032 ldb = ldb_module_get_ctx(ac->module);
2034 if (ares->error != LDB_SUCCESS) {
2035 return ldb_module_done(ac->req, ares->controls,
2036 ares->response, ares->error);
2039 if (ares->type != LDB_REPLY_DONE) {
2040 ldb_set_errstring(ldb,
2041 "invalid ldb_reply_type in callback");
2043 return ldb_module_done(ac->req, NULL, NULL,
2044 LDB_ERR_OPERATIONS_ERROR);
2047 /* Get a sequence number from the backend */
2048 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
2049 if (ret != LDB_SUCCESS) {
2054 * - replace the old object with the newly constructed one
2057 msg = ldb_msg_new(ac);
2060 return LDB_ERR_OPERATIONS_ERROR;
2063 msg->dn = ac->req->op.rename.newdn;
2065 ret = ldb_build_mod_req(&down_req, ldb, ac,
2068 ac, replmd_op_callback,
2071 if (ret != LDB_SUCCESS) {
2075 talloc_steal(down_req, msg);
2077 if (add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) {
2082 if (add_uint64_element(msg, "uSNChanged", ac->seq_num) != LDB_SUCCESS) {
2087 /* go on with the call chain - do the modify after the rename */
2088 return ldb_next_request(ac->module, down_req);
2091 /* remove forwards and backlinks as needed when an object
2093 static int replmd_delete_remove_link(struct ldb_module *module,
2094 struct dsdb_schema *schema,
2096 struct ldb_message_element *el,
2097 const struct dsdb_attribute *sa)
2100 TALLOC_CTX *tmp_ctx = talloc_new(module);
2101 struct ldb_context *ldb = ldb_module_get_ctx(module);
2103 for (i=0; i<el->num_values; i++) {
2104 struct dsdb_dn *dsdb_dn;
2108 struct ldb_message *msg;
2109 const struct dsdb_attribute *target_attr;
2110 struct ldb_message_element *el2;
2111 struct ldb_val dn_val;
2113 if (dsdb_dn_is_deleted_val(&el->values[i])) {
2117 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
2119 talloc_free(tmp_ctx);
2120 return LDB_ERR_OPERATIONS_ERROR;
2123 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid2, "GUID");
2124 if (!NT_STATUS_IS_OK(status)) {
2125 talloc_free(tmp_ctx);
2126 return LDB_ERR_OPERATIONS_ERROR;
2129 /* remove the link */
2130 msg = ldb_msg_new(tmp_ctx);
2132 ldb_module_oom(module);
2133 talloc_free(tmp_ctx);
2134 return LDB_ERR_OPERATIONS_ERROR;
2138 msg->dn = dsdb_dn->dn;
2140 target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
2141 if (target_attr == NULL) {
2145 ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName, LDB_FLAG_MOD_DELETE, &el2);
2146 if (ret != LDB_SUCCESS) {
2147 ldb_module_oom(module);
2148 talloc_free(tmp_ctx);
2149 return LDB_ERR_OPERATIONS_ERROR;
2151 dn_val = data_blob_string_const(ldb_dn_get_linearized(dn));
2152 el2->values = &dn_val;
2153 el2->num_values = 1;
2155 ret = dsdb_module_modify(module, msg, 0);
2156 if (ret != LDB_SUCCESS) {
2157 talloc_free(tmp_ctx);
2161 talloc_free(tmp_ctx);
2167 handle update of replication meta data for deletion of objects
2169 This also handles the mapping of delete to a rename operation
2170 to allow deletes to be replicated.
2172 static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
2174 int ret = LDB_ERR_OTHER;
2176 struct ldb_dn *old_dn, *new_dn;
2177 const char *rdn_name;
2178 const struct ldb_val *rdn_value, *new_rdn_value;
2180 struct ldb_context *ldb = ldb_module_get_ctx(module);
2181 struct dsdb_schema *schema = dsdb_get_schema(ldb);
2182 struct ldb_message *msg, *old_msg;
2183 struct ldb_message_element *el;
2184 TALLOC_CTX *tmp_ctx;
2185 struct ldb_result *res, *parent_res;
2186 const char *preserved_attrs[] = {
2187 /* yes, this really is a hard coded list. See MS-ADTS
2188 section 3.1.1.5.5.1.1 */
2189 "nTSecurityDescriptor", "attributeID", "attributeSyntax", "dNReferenceUpdate", "dNSHostName",
2190 "flatName", "governsID", "groupType", "instanceType", "lDAPDisplayName", "legacyExchangeDN",
2191 "isDeleted", "isRecycled", "lastKnownParent", "msDS-LastKnownRDN", "mS-DS-CreatorSID",
2192 "mSMQOwnerID", "nCName", "objectClass", "distinguishedName", "objectGUID", "objectSid",
2193 "oMSyntax", "proxiedObjectName", "name", "replPropertyMetaData", "sAMAccountName",
2194 "securityIdentifier", "sIDHistory", "subClassOf", "systemFlags", "trustPartner", "trustDirection",
2195 "trustType", "trustAttributes", "userAccountControl", "uSNChanged", "uSNCreated", "whenCreate",
2197 uint32_t el_count = 0;
2200 tmp_ctx = talloc_new(ldb);
2202 old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
2204 /* we need the complete msg off disk, so we can work out which
2205 attributes need to be removed */
2206 ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, NULL,
2207 DSDB_SEARCH_SHOW_DELETED |
2208 DSDB_SEARCH_REVEAL_INTERNALS |
2209 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT);
2210 if (ret != LDB_SUCCESS) {
2211 talloc_free(tmp_ctx);
2214 old_msg = res->msgs[0];
2216 /* work out where we will be renaming this object to */
2217 ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn, &new_dn);
2218 if (ret != LDB_SUCCESS) {
2219 /* this is probably an attempted delete on a partition
2220 * that doesn't allow delete operations, such as the
2221 * schema partition */
2222 ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
2223 ldb_dn_get_linearized(old_dn));
2224 talloc_free(tmp_ctx);
2225 return LDB_ERR_UNWILLING_TO_PERFORM;
2228 rdn_name = ldb_dn_get_rdn_name(old_dn);
2229 rdn_value = ldb_dn_get_rdn_val(old_dn);
2231 /* get the objects GUID from the search we just did */
2232 guid = samdb_result_guid(old_msg, "objectGUID");
2234 /* Add a formatted child */
2235 retb = ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ADEL:%s",
2238 GUID_string(tmp_ctx, &guid));
2240 DEBUG(0,(__location__ ": Unable to add a formatted child to dn: %s",
2241 ldb_dn_get_linearized(new_dn)));
2242 talloc_free(tmp_ctx);
2243 return LDB_ERR_OPERATIONS_ERROR;
2247 now we need to modify the object in the following ways:
2249 - add isDeleted=TRUE
2250 - update rDN and name, with new rDN
2251 - remove linked attributes
2252 - remove objectCategory and sAMAccountType
2253 - remove attribs not on the preserved list
2254 - preserved if in above list, or is rDN
2255 - remove all linked attribs from this object
2256 - remove all links from other objects to this object
2257 - add lastKnownParent
2258 - update replPropertyMetaData?
2260 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
2263 msg = ldb_msg_new(tmp_ctx);
2265 ldb_module_oom(module);
2266 talloc_free(tmp_ctx);
2267 return LDB_ERR_OPERATIONS_ERROR;
2272 ret = ldb_msg_add_string(msg, "isDeleted", "TRUE");
2273 if (ret != LDB_SUCCESS) {
2274 DEBUG(0,(__location__ ": Failed to add isDeleted string to the msg\n"));
2275 ldb_module_oom(module);
2276 talloc_free(tmp_ctx);
2279 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
2281 /* we need the storage form of the parent GUID */
2282 ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
2283 ldb_dn_get_parent(tmp_ctx, old_dn), NULL,
2284 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
2285 DSDB_SEARCH_REVEAL_INTERNALS);
2286 if (ret != LDB_SUCCESS) {
2287 talloc_free(tmp_ctx);
2291 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
2292 ldb_dn_get_extended_linearized(tmp_ctx, parent_res->msgs[0]->dn, 1));
2293 if (ret != LDB_SUCCESS) {
2294 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
2295 ldb_module_oom(module);
2296 talloc_free(tmp_ctx);
2299 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
2301 /* work out which of the old attributes we will be removing */
2302 for (i=0; i<old_msg->num_elements; i++) {
2303 const struct dsdb_attribute *sa;
2304 el = &old_msg->elements[i];
2305 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
2307 talloc_free(tmp_ctx);
2308 return LDB_ERR_OPERATIONS_ERROR;
2310 if (ldb_attr_cmp(el->name, rdn_name) == 0) {
2311 /* don't remove the rDN */
2316 ret = replmd_delete_remove_link(module, schema, old_dn, el, sa);
2317 if (ret != LDB_SUCCESS) {
2318 talloc_free(tmp_ctx);
2319 return LDB_ERR_OPERATIONS_ERROR;
2323 if (!sa->linkID && ldb_attr_in_list(preserved_attrs, el->name)) {
2327 ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
2328 if (ret != LDB_SUCCESS) {
2329 talloc_free(tmp_ctx);
2330 ldb_module_oom(module);
2335 /* work out what the new rdn value is, for updating the
2336 rDN and name fields */
2337 new_rdn_value = ldb_dn_get_rdn_val(new_dn);
2338 ret = ldb_msg_add_value(msg, rdn_name, new_rdn_value, &el);
2339 if (ret != LDB_SUCCESS) {
2340 talloc_free(tmp_ctx);
2343 el->flags = LDB_FLAG_MOD_REPLACE;
2345 el = ldb_msg_find_element(old_msg, "name");
2347 ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
2348 if (ret != LDB_SUCCESS) {
2349 talloc_free(tmp_ctx);
2352 el->flags = LDB_FLAG_MOD_REPLACE;
2355 ret = dsdb_module_modify(module, msg, 0);
2356 if (ret != LDB_SUCCESS){
2357 ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
2358 ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
2359 talloc_free(tmp_ctx);
2363 /* now rename onto the new DN */
2364 ret = dsdb_module_rename(module, old_dn, new_dn, 0);
2365 if (ret != LDB_SUCCESS){
2366 DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
2367 ldb_dn_get_linearized(old_dn),
2368 ldb_dn_get_linearized(new_dn),
2369 ldb_errstring(ldb)));
2370 talloc_free(tmp_ctx);
2374 talloc_free(tmp_ctx);
2376 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
2381 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
2386 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
2388 int ret = LDB_ERR_OTHER;
2389 /* TODO: do some error mapping */
2393 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
2395 struct ldb_context *ldb;
2396 struct ldb_request *change_req;
2397 enum ndr_err_code ndr_err;
2398 struct ldb_message *msg;
2399 struct replPropertyMetaDataBlob *md;
2400 struct ldb_val md_value;
2405 * TODO: check if the parent object exist
2409 * TODO: handle the conflict case where an object with the
2413 ldb = ldb_module_get_ctx(ar->module);
2414 msg = ar->objs->objects[ar->index_current].msg;
2415 md = ar->objs->objects[ar->index_current].meta_data;
2417 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
2418 if (ret != LDB_SUCCESS) {
2419 return replmd_replicated_request_error(ar, ret);
2422 ret = ldb_msg_add_value(msg, "objectGUID", &ar->objs->objects[ar->index_current].guid_value, NULL);
2423 if (ret != LDB_SUCCESS) {
2424 return replmd_replicated_request_error(ar, ret);
2427 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
2428 if (ret != LDB_SUCCESS) {
2429 return replmd_replicated_request_error(ar, ret);
2432 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
2433 if (ret != LDB_SUCCESS) {
2434 return replmd_replicated_request_error(ar, ret);
2437 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
2438 if (ret != LDB_SUCCESS) {
2439 return replmd_replicated_request_error(ar, ret);
2442 /* remove any message elements that have zero values */
2443 for (i=0; i<msg->num_elements; i++) {
2444 struct ldb_message_element *el = &msg->elements[i];
2446 if (el->num_values == 0) {
2447 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
2449 memmove(el, el+1, sizeof(*el)*(msg->num_elements - (i+1)));
2450 msg->num_elements--;
2457 * the meta data array is already sorted by the caller
2459 for (i=0; i < md->ctr.ctr1.count; i++) {
2460 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
2462 ndr_err = ndr_push_struct_blob(&md_value, msg,
2463 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
2465 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
2466 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2467 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
2468 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
2470 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
2471 if (ret != LDB_SUCCESS) {
2472 return replmd_replicated_request_error(ar, ret);
2475 replmd_ldb_message_sort(msg, ar->schema);
2478 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_ADD, msg);
2479 DEBUG(4, ("DRS replication add message:\n%s\n", s));
2483 ret = ldb_build_add_req(&change_req,
2491 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
2493 return ldb_next_request(ar->module, change_req);
2497 return true if an update is newer than an existing entry
2498 see section 5.11 of MS-ADTS
2500 static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
2501 const struct GUID *update_invocation_id,
2502 uint32_t current_version,
2503 uint32_t update_version,
2504 NTTIME current_change_time,
2505 NTTIME update_change_time)
2507 if (update_version != current_version) {
2508 return update_version > current_version;
2510 if (update_change_time > current_change_time) {
2513 if (update_change_time == current_change_time) {
2514 return GUID_compare(update_invocation_id, current_invocation_id) > 0;
2519 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
2520 struct replPropertyMetaData1 *new_m)
2522 return replmd_update_is_newer(&cur_m->originating_invocation_id,
2523 &new_m->originating_invocation_id,
2526 cur_m->originating_change_time,
2527 new_m->originating_change_time);
2530 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
2532 struct ldb_context *ldb;
2533 struct ldb_request *change_req;
2534 enum ndr_err_code ndr_err;
2535 struct ldb_message *msg;
2536 struct replPropertyMetaDataBlob *rmd;
2537 struct replPropertyMetaDataBlob omd;
2538 const struct ldb_val *omd_value;
2539 struct replPropertyMetaDataBlob nmd;
2540 struct ldb_val nmd_value;
2542 uint32_t removed_attrs = 0;
2545 ldb = ldb_module_get_ctx(ar->module);
2546 msg = ar->objs->objects[ar->index_current].msg;
2547 rmd = ar->objs->objects[ar->index_current].meta_data;
2552 * TODO: check repl data is correct after a rename
2554 if (ldb_dn_compare(msg->dn, ar->search_msg->dn) != 0) {
2555 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_request rename %s => %s\n",
2556 ldb_dn_get_linearized(ar->search_msg->dn),
2557 ldb_dn_get_linearized(msg->dn));
2558 /* we can't use dsdb_module_rename() here as we need
2559 the rename call to be intercepted by this module, to
2560 allow it to process linked attribute changes */
2561 if (ldb_rename(ldb, ar->search_msg->dn, msg->dn) != LDB_SUCCESS) {
2562 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_replicated_request rename %s => %s failed - %s\n",
2563 ldb_dn_get_linearized(ar->search_msg->dn),
2564 ldb_dn_get_linearized(msg->dn),
2565 ldb_errstring(ldb));
2566 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
2570 /* find existing meta data */
2571 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
2573 ndr_err = ndr_pull_struct_blob(omd_value, ar,
2574 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), &omd,
2575 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
2576 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2577 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
2578 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
2581 if (omd.version != 1) {
2582 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
2588 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
2589 nmd.ctr.ctr1.array = talloc_array(ar,
2590 struct replPropertyMetaData1,
2591 nmd.ctr.ctr1.count);
2592 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOMEM);
2594 /* first copy the old meta data */
2595 for (i=0; i < omd.ctr.ctr1.count; i++) {
2596 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
2600 /* now merge in the new meta data */
2601 for (i=0; i < rmd->ctr.ctr1.count; i++) {
2604 for (j=0; j < ni; j++) {
2607 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
2611 cmp = replmd_replPropertyMetaData1_is_newer(&nmd.ctr.ctr1.array[j],
2612 &rmd->ctr.ctr1.array[i]);
2614 /* replace the entry */
2615 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
2620 DEBUG(1,("Discarding older DRS attribute update to %s on %s from %s\n",
2621 msg->elements[i-removed_attrs].name,
2622 ldb_dn_get_linearized(msg->dn),
2623 GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
2625 /* we don't want to apply this change so remove the attribute */
2626 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
2633 if (found) continue;
2635 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
2640 * finally correct the size of the meta_data array
2642 nmd.ctr.ctr1.count = ni;
2645 * the rdn attribute (the alias for the name attribute),
2646 * 'cn' for most objects is the last entry in the meta data array
2649 * sort the new meta data array
2651 ret = replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, ar->schema, msg->dn);
2652 if (ret != LDB_SUCCESS) {
2657 * check if some replicated attributes left, otherwise skip the ldb_modify() call
2659 if (msg->num_elements == 0) {
2660 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
2663 ar->index_current++;
2664 return replmd_replicated_apply_next(ar);
2667 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
2668 ar->index_current, msg->num_elements);
2670 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
2671 if (ret != LDB_SUCCESS) {
2672 return replmd_replicated_request_error(ar, ret);
2675 for (i=0; i<ni; i++) {
2676 nmd.ctr.ctr1.array[i].local_usn = ar->seq_num;
2679 /* create the meta data value */
2680 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
2681 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
2683 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
2684 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2685 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
2686 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
2690 * when we know that we'll modify the record, add the whenChanged, uSNChanged
2691 * and replPopertyMetaData attributes
2693 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
2694 if (ret != LDB_SUCCESS) {
2695 return replmd_replicated_request_error(ar, ret);
2697 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
2698 if (ret != LDB_SUCCESS) {
2699 return replmd_replicated_request_error(ar, ret);
2701 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
2702 if (ret != LDB_SUCCESS) {
2703 return replmd_replicated_request_error(ar, ret);
2706 replmd_ldb_message_sort(msg, ar->schema);
2708 /* we want to replace the old values */
2709 for (i=0; i < msg->num_elements; i++) {
2710 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
2714 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
2715 DEBUG(4, ("DRS replication modify message:\n%s\n", s));
2719 ret = ldb_build_mod_req(&change_req,
2727 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
2729 return ldb_next_request(ar->module, change_req);
2732 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
2733 struct ldb_reply *ares)
2735 struct replmd_replicated_request *ar = talloc_get_type(req->context,
2736 struct replmd_replicated_request);
2740 return ldb_module_done(ar->req, NULL, NULL,
2741 LDB_ERR_OPERATIONS_ERROR);
2743 if (ares->error != LDB_SUCCESS &&
2744 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
2745 return ldb_module_done(ar->req, ares->controls,
2746 ares->response, ares->error);
2749 switch (ares->type) {
2750 case LDB_REPLY_ENTRY:
2751 ar->search_msg = talloc_steal(ar, ares->message);
2754 case LDB_REPLY_REFERRAL:
2755 /* we ignore referrals */
2758 case LDB_REPLY_DONE:
2759 if (ar->search_msg != NULL) {
2760 ret = replmd_replicated_apply_merge(ar);
2762 ret = replmd_replicated_apply_add(ar);
2764 if (ret != LDB_SUCCESS) {
2765 return ldb_module_done(ar->req, NULL, NULL, ret);
2773 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
2775 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
2777 struct ldb_context *ldb;
2781 struct ldb_request *search_req;
2782 struct ldb_search_options_control *options;
2784 if (ar->index_current >= ar->objs->num_objects) {
2785 /* done with it, go to next stage */
2786 return replmd_replicated_uptodate_vector(ar);
2789 ldb = ldb_module_get_ctx(ar->module);
2790 ar->search_msg = NULL;
2792 tmp_str = ldb_binary_encode(ar, ar->objs->objects[ar->index_current].guid_value);
2793 if (!tmp_str) return replmd_replicated_request_werror(ar, WERR_NOMEM);
2795 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
2796 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
2797 talloc_free(tmp_str);
2799 ret = ldb_build_search_req(&search_req,
2808 replmd_replicated_apply_search_callback,
2811 ret = ldb_request_add_control(search_req, LDB_CONTROL_SHOW_DELETED_OID, true, NULL);
2812 if (ret != LDB_SUCCESS) {
2816 /* we need to cope with cross-partition links, so search for
2817 the GUID over all partitions */
2818 options = talloc(search_req, struct ldb_search_options_control);
2819 if (options == NULL) {
2820 DEBUG(0, (__location__ ": out of memory\n"));
2821 return LDB_ERR_OPERATIONS_ERROR;
2823 options->search_options = LDB_SEARCH_OPTION_PHANTOM_ROOT;
2825 ret = ldb_request_add_control(search_req,
2826 LDB_CONTROL_SEARCH_OPTIONS_OID,
2828 if (ret != LDB_SUCCESS) {
2832 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
2834 return ldb_next_request(ar->module, search_req);
2837 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
2838 struct ldb_reply *ares)
2840 struct ldb_context *ldb;
2841 struct replmd_replicated_request *ar = talloc_get_type(req->context,
2842 struct replmd_replicated_request);
2843 ldb = ldb_module_get_ctx(ar->module);
2846 return ldb_module_done(ar->req, NULL, NULL,
2847 LDB_ERR_OPERATIONS_ERROR);
2849 if (ares->error != LDB_SUCCESS) {
2850 return ldb_module_done(ar->req, ares->controls,
2851 ares->response, ares->error);
2854 if (ares->type != LDB_REPLY_DONE) {
2855 ldb_set_errstring(ldb, "Invalid reply type\n!");
2856 return ldb_module_done(ar->req, NULL, NULL,
2857 LDB_ERR_OPERATIONS_ERROR);
2862 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
2865 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
2867 struct ldb_context *ldb;
2868 struct ldb_request *change_req;
2869 enum ndr_err_code ndr_err;
2870 struct ldb_message *msg;
2871 struct replUpToDateVectorBlob ouv;
2872 const struct ldb_val *ouv_value;
2873 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
2874 struct replUpToDateVectorBlob nuv;
2875 struct ldb_val nuv_value;
2876 struct ldb_message_element *nuv_el = NULL;
2877 const struct GUID *our_invocation_id;
2878 struct ldb_message_element *orf_el = NULL;
2879 struct repsFromToBlob nrf;
2880 struct ldb_val *nrf_value = NULL;
2881 struct ldb_message_element *nrf_el = NULL;
2884 time_t t = time(NULL);
2888 ldb = ldb_module_get_ctx(ar->module);
2889 ruv = ar->objs->uptodateness_vector;
2895 unix_to_nt_time(&now, t);
2898 * first create the new replUpToDateVector
2900 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
2902 ndr_err = ndr_pull_struct_blob(ouv_value, ar,
2903 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), &ouv,
2904 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
2905 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2906 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
2907 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
2910 if (ouv.version != 2) {
2911 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
2916 * the new uptodateness vector will at least
2917 * contain 1 entry, one for the source_dsa
2919 * plus optional values from our old vector and the one from the source_dsa
2921 nuv.ctr.ctr2.count = 1 + ouv.ctr.ctr2.count;
2922 if (ruv) nuv.ctr.ctr2.count += ruv->count;
2923 nuv.ctr.ctr2.cursors = talloc_array(ar,
2924 struct drsuapi_DsReplicaCursor2,
2925 nuv.ctr.ctr2.count);
2926 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOMEM);
2928 /* first copy the old vector */
2929 for (i=0; i < ouv.ctr.ctr2.count; i++) {
2930 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
2934 /* get our invocation_id if we have one already attached to the ldb */
2935 our_invocation_id = samdb_ntds_invocation_id(ldb);
2937 /* merge in the source_dsa vector is available */
2938 for (i=0; (ruv && i < ruv->count); i++) {
2941 if (our_invocation_id &&
2942 GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
2943 our_invocation_id)) {
2947 for (j=0; j < ni; j++) {
2948 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
2949 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
2956 * we update only the highest_usn and not the latest_sync_success time,
2957 * because the last success stands for direct replication
2959 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
2960 nuv.ctr.ctr2.cursors[j].highest_usn = ruv->cursors[i].highest_usn;
2965 if (found) continue;
2967 /* if it's not there yet, add it */
2968 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
2973 * merge in the current highwatermark for the source_dsa
2976 for (j=0; j < ni; j++) {
2977 if (!GUID_equal(&ar->objs->source_dsa->source_dsa_invocation_id,
2978 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
2985 * here we update the highest_usn and last_sync_success time
2986 * because we're directly replicating from the source_dsa
2988 * and use the tmp_highest_usn because this is what we have just applied
2991 nuv.ctr.ctr2.cursors[j].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
2992 nuv.ctr.ctr2.cursors[j].last_sync_success = now;
2997 * here we update the highest_usn and last_sync_success time
2998 * because we're directly replicating from the source_dsa
3000 * and use the tmp_highest_usn because this is what we have just applied
3003 nuv.ctr.ctr2.cursors[ni].source_dsa_invocation_id= ar->objs->source_dsa->source_dsa_invocation_id;
3004 nuv.ctr.ctr2.cursors[ni].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
3005 nuv.ctr.ctr2.cursors[ni].last_sync_success = now;
3010 * finally correct the size of the cursors array
3012 nuv.ctr.ctr2.count = ni;
3017 qsort(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count,
3018 sizeof(struct drsuapi_DsReplicaCursor2),
3019 (comparison_fn_t)drsuapi_DsReplicaCursor2_compare);
3022 * create the change ldb_message
3024 msg = ldb_msg_new(ar);
3025 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3026 msg->dn = ar->search_msg->dn;
3028 ndr_err = ndr_push_struct_blob(&nuv_value, msg,
3029 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
3031 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
3032 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3033 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3034 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3036 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
3037 if (ret != LDB_SUCCESS) {
3038 return replmd_replicated_request_error(ar, ret);
3040 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
3043 * now create the new repsFrom value from the given repsFromTo1 structure
3047 nrf.ctr.ctr1 = *ar->objs->source_dsa;
3048 /* and fix some values... */
3049 nrf.ctr.ctr1.consecutive_sync_failures = 0;
3050 nrf.ctr.ctr1.last_success = now;
3051 nrf.ctr.ctr1.last_attempt = now;
3052 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
3053 nrf.ctr.ctr1.highwatermark.highest_usn = nrf.ctr.ctr1.highwatermark.tmp_highest_usn;
3056 * first see if we already have a repsFrom value for the current source dsa
3057 * if so we'll later replace this value
3059 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
3061 for (i=0; i < orf_el->num_values; i++) {
3062 struct repsFromToBlob *trf;
3064 trf = talloc(ar, struct repsFromToBlob);
3065 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3067 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), trf,
3068 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
3069 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3070 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3071 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3074 if (trf->version != 1) {
3075 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
3079 * we compare the source dsa objectGUID not the invocation_id
3080 * because we want only one repsFrom value per source dsa
3081 * and when the invocation_id of the source dsa has changed we don't need
3082 * the old repsFrom with the old invocation_id
3084 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
3085 &ar->objs->source_dsa->source_dsa_obj_guid)) {
3091 nrf_value = &orf_el->values[i];
3096 * copy over all old values to the new ldb_message
3098 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
3099 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3104 * if we haven't found an old repsFrom value for the current source dsa
3105 * we'll add a new value
3108 struct ldb_val zero_value;
3109 ZERO_STRUCT(zero_value);
3110 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
3111 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3113 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
3116 /* we now fill the value which is already attached to ldb_message */
3117 ndr_err = ndr_push_struct_blob(nrf_value, msg,
3118 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
3120 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
3121 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3122 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3123 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3127 * the ldb_message_element for the attribute, has all the old values and the new one
3128 * so we'll replace the whole attribute with all values
3130 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
3133 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
3134 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
3138 /* prepare the ldb_modify() request */
3139 ret = ldb_build_mod_req(&change_req,
3145 replmd_replicated_uptodate_modify_callback,
3147 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3149 return ldb_next_request(ar->module, change_req);
3152 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
3153 struct ldb_reply *ares)
3155 struct replmd_replicated_request *ar = talloc_get_type(req->context,
3156 struct replmd_replicated_request);
3160 return ldb_module_done(ar->req, NULL, NULL,
3161 LDB_ERR_OPERATIONS_ERROR);
3163 if (ares->error != LDB_SUCCESS &&
3164 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
3165 return ldb_module_done(ar->req, ares->controls,
3166 ares->response, ares->error);
3169 switch (ares->type) {
3170 case LDB_REPLY_ENTRY:
3171 ar->search_msg = talloc_steal(ar, ares->message);
3174 case LDB_REPLY_REFERRAL:
3175 /* we ignore referrals */
3178 case LDB_REPLY_DONE:
3179 if (ar->search_msg == NULL) {
3180 ret = replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
3182 ret = replmd_replicated_uptodate_modify(ar);
3184 if (ret != LDB_SUCCESS) {
3185 return ldb_module_done(ar->req, NULL, NULL, ret);
3194 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
3196 struct ldb_context *ldb;
3198 static const char *attrs[] = {
3199 "replUpToDateVector",
3203 struct ldb_request *search_req;
3205 ldb = ldb_module_get_ctx(ar->module);
3206 ar->search_msg = NULL;
3208 ret = ldb_build_search_req(&search_req,
3211 ar->objs->partition_dn,
3217 replmd_replicated_uptodate_search_callback,
3219 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3221 return ldb_next_request(ar->module, search_req);
3226 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
3228 struct ldb_context *ldb;
3229 struct dsdb_extended_replicated_objects *objs;
3230 struct replmd_replicated_request *ar;
3231 struct ldb_control **ctrls;
3233 struct replmd_private *replmd_private =
3234 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3236 ldb = ldb_module_get_ctx(module);
3238 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
3240 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
3242 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
3243 return LDB_ERR_PROTOCOL_ERROR;
3246 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
3247 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
3248 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
3249 return LDB_ERR_PROTOCOL_ERROR;
3252 ar = replmd_ctx_init(module, req);
3254 return LDB_ERR_OPERATIONS_ERROR;
3256 /* Set the flags to have the replmd_op_callback run over the full set of objects */
3257 ar->apply_mode = true;
3259 ar->schema = dsdb_get_schema(ldb);
3261 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
3263 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
3264 return LDB_ERR_CONSTRAINT_VIOLATION;
3267 ctrls = req->controls;
3269 if (req->controls) {
3270 req->controls = talloc_memdup(ar, req->controls,
3271 talloc_get_size(req->controls));
3272 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3275 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, NULL);
3276 if (ret != LDB_SUCCESS) {
3280 ar->controls = req->controls;
3281 req->controls = ctrls;
3283 DEBUG(4,("linked_attributes_count=%u\n", objs->linked_attributes_count));
3285 /* save away the linked attributes for the end of the
3287 for (i=0; i<ar->objs->linked_attributes_count; i++) {
3288 struct la_entry *la_entry;
3290 if (replmd_private->la_ctx == NULL) {
3291 replmd_private->la_ctx = talloc_new(replmd_private);
3293 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
3294 if (la_entry == NULL) {
3296 return LDB_ERR_OPERATIONS_ERROR;
3298 la_entry->la = talloc(la_entry, struct drsuapi_DsReplicaLinkedAttribute);
3299 if (la_entry->la == NULL) {
3300 talloc_free(la_entry);
3302 return LDB_ERR_OPERATIONS_ERROR;
3304 *la_entry->la = ar->objs->linked_attributes[i];
3306 /* we need to steal the non-scalars so they stay
3307 around until the end of the transaction */
3308 talloc_steal(la_entry->la, la_entry->la->identifier);
3309 talloc_steal(la_entry->la, la_entry->la->value.blob);
3311 DLIST_ADD(replmd_private->la_list, la_entry);
3314 return replmd_replicated_apply_next(ar);
3318 process one linked attribute structure
3320 static int replmd_process_linked_attribute(struct ldb_module *module,
3321 struct la_entry *la_entry)
3323 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
3324 struct ldb_context *ldb = ldb_module_get_ctx(module);
3325 struct dsdb_schema *schema = dsdb_get_schema(ldb);
3326 struct ldb_message *msg;
3327 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
3329 const struct dsdb_attribute *attr;
3330 struct dsdb_dn *dsdb_dn;
3331 uint64_t seq_num = 0;
3332 struct drsuapi_DsReplicaAttribute drs;
3333 struct drsuapi_DsAttributeValue val;
3334 struct ldb_message_element new_el, *old_el;
3336 time_t t = time(NULL);
3337 struct ldb_result *res;
3338 const char *attrs[2];
3339 struct parsed_dn *pdn_list, *pdn;
3340 struct GUID guid = GUID_zero();
3342 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
3343 const struct GUID *our_invocation_id;
3345 drs.value_ctr.num_values = 1;
3346 drs.value_ctr.values = &val;
3347 val.blob = la->value.blob;
3350 linked_attributes[0]:
3351 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
3353 identifier: struct drsuapi_DsReplicaObjectIdentifier
3354 __ndr_size : 0x0000003a (58)
3355 __ndr_size_sid : 0x00000000 (0)
3356 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
3358 __ndr_size_dn : 0x00000000 (0)
3360 attid : DRSUAPI_ATTRIBUTE_member (0x1F)
3361 value: struct drsuapi_DsAttributeValue
3362 __ndr_size : 0x0000007e (126)
3364 blob : DATA_BLOB length=126
3365 flags : 0x00000001 (1)
3366 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
3367 originating_add_time : Wed Sep 2 22:20:01 2009 EST
3368 meta_data: struct drsuapi_DsReplicaMetaData
3369 version : 0x00000015 (21)
3370 originating_change_time : Wed Sep 2 23:39:07 2009 EST
3371 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
3372 originating_usn : 0x000000000001e19c (123292)
3374 (for cases where the link is to a normal DN)
3375 &target: struct drsuapi_DsReplicaObjectIdentifier3
3376 __ndr_size : 0x0000007e (126)
3377 __ndr_size_sid : 0x0000001c (28)
3378 guid : 7639e594-db75-4086-b0d4-67890ae46031
3379 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
3380 __ndr_size_dn : 0x00000022 (34)
3381 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
3384 /* find the attribute being modified */
3385 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
3387 DEBUG(0, (__location__ ": Unable to find attributeID 0x%x\n", la->attid));
3388 talloc_free(tmp_ctx);
3389 return LDB_ERR_OPERATIONS_ERROR;
3392 attrs[0] = attr->lDAPDisplayName;
3395 /* get the existing message from the db for the object with
3396 this GUID, returning attribute being modified. We will then
3397 use this msg as the basis for a modify call */
3398 ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
3399 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
3400 DSDB_SEARCH_SHOW_DELETED |
3401 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
3402 DSDB_SEARCH_REVEAL_INTERNALS,
3403 "objectGUID=%s", GUID_string(tmp_ctx, &la->identifier->guid));
3404 if (ret != LDB_SUCCESS) {
3405 talloc_free(tmp_ctx);
3408 if (res->count != 1) {
3409 ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
3410 GUID_string(tmp_ctx, &la->identifier->guid));
3411 talloc_free(tmp_ctx);
3412 return LDB_ERR_NO_SUCH_OBJECT;
3416 if (msg->num_elements == 0) {
3417 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName, LDB_FLAG_MOD_REPLACE, &old_el);
3418 if (ret != LDB_SUCCESS) {
3419 ldb_module_oom(module);
3420 talloc_free(tmp_ctx);
3421 return LDB_ERR_OPERATIONS_ERROR;
3424 old_el = &msg->elements[0];
3425 old_el->flags = LDB_FLAG_MOD_REPLACE;
3428 /* parse the existing links */
3429 ret = get_parsed_dns(module, tmp_ctx, old_el, &pdn_list, attr->syntax->ldap_oid);
3430 if (ret != LDB_SUCCESS) {
3431 talloc_free(tmp_ctx);
3435 /* get our invocationId */
3436 our_invocation_id = samdb_ntds_invocation_id(ldb);
3437 if (!our_invocation_id) {
3438 ldb_debug_set(ldb, LDB_DEBUG_ERROR, __location__ ": unable to find invocationId\n");
3439 talloc_free(tmp_ctx);
3440 return LDB_ERR_OPERATIONS_ERROR;
3443 ret = replmd_check_upgrade_links(pdn_list, old_el->num_values, our_invocation_id);
3444 if (ret != LDB_SUCCESS) {
3445 talloc_free(tmp_ctx);
3449 status = attr->syntax->drsuapi_to_ldb(ldb, schema, attr, &drs, tmp_ctx, &new_el);
3450 if (!W_ERROR_IS_OK(status)) {
3451 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s\n",
3452 old_el->name, ldb_dn_get_linearized(msg->dn));
3453 return LDB_ERR_OPERATIONS_ERROR;
3456 if (new_el.num_values != 1) {
3457 ldb_asprintf_errstring(ldb, "Failed to find value in linked attribute blob for %s on %s\n",
3458 old_el->name, ldb_dn_get_linearized(msg->dn));
3459 return LDB_ERR_OPERATIONS_ERROR;
3462 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &new_el.values[0], attr->syntax->ldap_oid);
3464 ldb_asprintf_errstring(ldb, "Failed to parse DN in linked attribute blob for %s on %s\n",
3465 old_el->name, ldb_dn_get_linearized(msg->dn));
3466 return LDB_ERR_OPERATIONS_ERROR;
3469 ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid, "GUID");
3470 if (!NT_STATUS_IS_OK(ntstatus) && active) {
3471 ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute blob for %s on %s from %s",
3473 ldb_dn_get_linearized(dsdb_dn->dn),
3474 ldb_dn_get_linearized(msg->dn));
3475 return LDB_ERR_OPERATIONS_ERROR;
3478 /* see if this link already exists */
3479 pdn = parsed_dn_find(pdn_list, old_el->num_values, &guid, dsdb_dn->dn);
3481 /* see if this update is newer than what we have already */
3482 struct GUID invocation_id = GUID_zero();
3483 uint32_t version = 0;
3484 NTTIME change_time = 0;
3485 bool was_active = ldb_dn_get_extended_component(pdn->dsdb_dn->dn, "DELETED") == NULL;
3487 dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
3488 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
3489 dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
3491 if (!replmd_update_is_newer(&invocation_id,
3492 &la->meta_data.originating_invocation_id,
3494 la->meta_data.version,
3496 la->meta_data.originating_change_time)) {
3497 DEBUG(1,("Discarding older DRS linked attribute update to %s on %s from %s\n",
3498 old_el->name, ldb_dn_get_linearized(msg->dn),
3499 GUID_string(tmp_ctx, &la->meta_data.originating_invocation_id)));
3500 talloc_free(tmp_ctx);
3504 /* get a seq_num for this change */
3505 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
3506 if (ret != LDB_SUCCESS) {
3507 talloc_free(tmp_ctx);
3512 /* remove the existing backlink */
3513 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, false, attr, false);
3514 if (ret != LDB_SUCCESS) {
3515 talloc_free(tmp_ctx);
3520 ret = replmd_update_la_val(tmp_ctx, pdn->v, dsdb_dn, pdn->dsdb_dn,
3521 &la->meta_data.originating_invocation_id,
3522 la->meta_data.originating_usn, seq_num,
3523 la->meta_data.originating_change_time,
3524 la->meta_data.version,
3526 if (ret != LDB_SUCCESS) {
3527 talloc_free(tmp_ctx);
3532 /* add the new backlink */
3533 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, true, attr, false);
3534 if (ret != LDB_SUCCESS) {
3535 talloc_free(tmp_ctx);
3540 /* get a seq_num for this change */
3541 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
3542 if (ret != LDB_SUCCESS) {
3543 talloc_free(tmp_ctx);
3547 old_el->values = talloc_realloc(msg->elements, old_el->values,
3548 struct ldb_val, old_el->num_values+1);
3549 if (!old_el->values) {
3550 ldb_module_oom(module);
3551 return LDB_ERR_OPERATIONS_ERROR;
3553 old_el->num_values++;
3555 ret = replmd_build_la_val(tmp_ctx, &old_el->values[old_el->num_values-1], dsdb_dn,
3556 &la->meta_data.originating_invocation_id,
3557 la->meta_data.originating_usn, seq_num,
3558 la->meta_data.originating_change_time,
3559 la->meta_data.version,
3560 (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?false:true);
3561 if (ret != LDB_SUCCESS) {
3562 talloc_free(tmp_ctx);
3567 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid,
3569 if (ret != LDB_SUCCESS) {
3570 talloc_free(tmp_ctx);
3576 /* we only change whenChanged and uSNChanged if the seq_num
3578 if (add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) {
3579 talloc_free(tmp_ctx);
3580 return LDB_ERR_OPERATIONS_ERROR;
3583 if (add_uint64_element(msg, "uSNChanged", seq_num) != LDB_SUCCESS) {
3584 talloc_free(tmp_ctx);
3585 return LDB_ERR_OPERATIONS_ERROR;
3588 ret = dsdb_check_single_valued_link(attr, old_el);
3589 if (ret != LDB_SUCCESS) {
3590 talloc_free(tmp_ctx);
3594 ret = dsdb_module_modify(module, msg, DSDB_MODIFY_RELAX);
3595 if (ret != LDB_SUCCESS) {
3596 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
3598 ldb_ldif_message_string(ldb, tmp_ctx, LDB_CHANGETYPE_MODIFY, msg));
3599 talloc_free(tmp_ctx);
3603 talloc_free(tmp_ctx);
3608 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
3610 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
3611 return replmd_extended_replicated_objects(module, req);
3614 return ldb_next_request(module, req);
3619 we hook into the transaction operations to allow us to
3620 perform the linked attribute updates at the end of the whole
3621 transaction. This allows a forward linked attribute to be created
3622 before the object is created. During a vampire, w2k8 sends us linked
3623 attributes before the objects they are part of.
3625 static int replmd_start_transaction(struct ldb_module *module)
3627 /* create our private structure for this transaction */
3628 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
3629 struct replmd_private);
3630 replmd_txn_cleanup(replmd_private);
3632 /* free any leftover mod_usn records from cancelled
3634 while (replmd_private->ncs) {
3635 struct nc_entry *e = replmd_private->ncs;
3636 DLIST_REMOVE(replmd_private->ncs, e);
3640 return ldb_next_start_trans(module);
3644 on prepare commit we loop over our queued la_context structures and
3647 static int replmd_prepare_commit(struct ldb_module *module)
3649 struct replmd_private *replmd_private =
3650 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3651 struct la_entry *la, *prev;
3652 struct la_backlink *bl;
3655 /* walk the list backwards, to do the first entry first, as we
3656 * added the entries with DLIST_ADD() which puts them at the
3657 * start of the list */
3658 for (la = replmd_private->la_list; la && la->next; la=la->next) ;
3660 for (; la; la=prev) {
3662 DLIST_REMOVE(replmd_private->la_list, la);
3663 ret = replmd_process_linked_attribute(module, la);
3664 if (ret != LDB_SUCCESS) {
3665 replmd_txn_cleanup(replmd_private);
3670 /* process our backlink list, creating and deleting backlinks
3672 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
3673 ret = replmd_process_backlink(module, bl);
3674 if (ret != LDB_SUCCESS) {
3675 replmd_txn_cleanup(replmd_private);
3680 replmd_txn_cleanup(replmd_private);
3682 /* possibly change @REPLCHANGED */
3683 ret = replmd_notify_store(module);
3684 if (ret != LDB_SUCCESS) {
3688 return ldb_next_prepare_commit(module);
3691 static int replmd_del_transaction(struct ldb_module *module)
3693 struct replmd_private *replmd_private =
3694 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3695 replmd_txn_cleanup(replmd_private);
3697 return ldb_next_del_trans(module);
3701 _PUBLIC_ const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
3702 .name = "repl_meta_data",
3703 .init_context = replmd_init,
3705 .modify = replmd_modify,
3706 .rename = replmd_rename,
3707 .del = replmd_delete,
3708 .extended = replmd_extended,
3709 .start_transaction = replmd_start_transaction,
3710 .prepare_commit = replmd_prepare_commit,
3711 .del_transaction = replmd_del_transaction,