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"
50 #include "libcli/security/security.h"
52 #define W2K3_LINKED_ATTRIBUTES 1
54 struct replmd_private {
56 struct la_entry *la_list;
58 struct la_backlink *la_backlinks;
60 struct nc_entry *prev, *next;
67 struct la_entry *next, *prev;
68 struct drsuapi_DsReplicaLinkedAttribute *la;
71 struct replmd_replicated_request {
72 struct ldb_module *module;
73 struct ldb_request *req;
75 const struct dsdb_schema *schema;
77 /* the controls we pass down */
78 struct ldb_control **controls;
80 /* details for the mode where we apply a bunch of inbound replication meessages */
82 uint32_t index_current;
83 struct dsdb_extended_replicated_objects *objs;
85 struct ldb_message *search_msg;
91 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar);
96 allocate the private structure and build the list
97 of partition DNs for use by replmd_notify()
99 static int replmd_init(struct ldb_module *module)
101 struct replmd_private *replmd_private;
102 struct ldb_context *ldb = ldb_module_get_ctx(module);
104 replmd_private = talloc_zero(module, struct replmd_private);
105 if (replmd_private == NULL) {
107 return LDB_ERR_OPERATIONS_ERROR;
109 ldb_module_set_private(module, replmd_private);
111 return ldb_next_init(module);
115 cleanup our per-transaction contexts
117 static void replmd_txn_cleanup(struct replmd_private *replmd_private)
119 talloc_free(replmd_private->la_ctx);
120 replmd_private->la_list = NULL;
121 replmd_private->la_ctx = NULL;
123 talloc_free(replmd_private->bl_ctx);
124 replmd_private->la_backlinks = NULL;
125 replmd_private->bl_ctx = NULL;
130 struct la_backlink *next, *prev;
131 const char *attr_name;
132 struct GUID forward_guid, target_guid;
137 process a backlinks we accumulated during a transaction, adding and
138 deleting the backlinks from the target objects
140 static int replmd_process_backlink(struct ldb_module *module, struct la_backlink *bl)
142 struct ldb_dn *target_dn, *source_dn;
144 struct ldb_context *ldb = ldb_module_get_ctx(module);
145 struct ldb_message *msg;
146 TALLOC_CTX *tmp_ctx = talloc_new(bl);
152 - construct ldb_message
153 - either an add or a delete
155 ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->target_guid, &target_dn);
156 if (ret != LDB_SUCCESS) {
157 ldb_asprintf_errstring(ldb, "Failed to find target DN for linked attribute with GUID %s\n",
158 GUID_string(bl, &bl->target_guid));
159 talloc_free(tmp_ctx);
163 ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->forward_guid, &source_dn);
164 if (ret != LDB_SUCCESS) {
165 ldb_asprintf_errstring(ldb, "Failed to find source DN for linked attribute with GUID %s\n",
166 GUID_string(bl, &bl->forward_guid));
167 talloc_free(tmp_ctx);
171 msg = ldb_msg_new(tmp_ctx);
173 ldb_module_oom(module);
174 talloc_free(tmp_ctx);
175 return LDB_ERR_OPERATIONS_ERROR;
178 /* construct a ldb_message for adding/deleting the backlink */
180 dn_string = ldb_dn_get_extended_linearized(tmp_ctx, source_dn, 1);
182 ldb_module_oom(module);
183 talloc_free(tmp_ctx);
184 return LDB_ERR_OPERATIONS_ERROR;
186 ret = ldb_msg_add_steal_string(msg, bl->attr_name, dn_string);
187 if (ret != LDB_SUCCESS) {
188 talloc_free(tmp_ctx);
191 msg->elements[0].flags = bl->active?LDB_FLAG_MOD_ADD:LDB_FLAG_MOD_DELETE;
193 ret = dsdb_module_modify(module, msg, 0);
194 if (ret != LDB_SUCCESS) {
195 ldb_asprintf_errstring(ldb, "Failed to %s backlink from %s to %s - %s",
196 bl->active?"add":"remove",
197 ldb_dn_get_linearized(source_dn),
198 ldb_dn_get_linearized(target_dn),
200 talloc_free(tmp_ctx);
203 talloc_free(tmp_ctx);
208 add a backlink to the list of backlinks to add/delete in the prepare
211 static int replmd_add_backlink(struct ldb_module *module, const struct dsdb_schema *schema,
212 struct GUID *forward_guid, struct GUID *target_guid,
213 bool active, const struct dsdb_attribute *schema_attr, bool immediate)
215 const struct dsdb_attribute *target_attr;
216 struct la_backlink *bl;
217 struct replmd_private *replmd_private =
218 talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
220 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
223 * windows 2003 has a broken schema where the
224 * definition of msDS-IsDomainFor is missing (which is
225 * supposed to be the backlink of the
226 * msDS-HasDomainNCs attribute
231 /* see if its already in the list */
232 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
233 if (GUID_equal(forward_guid, &bl->forward_guid) &&
234 GUID_equal(target_guid, &bl->target_guid) &&
235 (target_attr->lDAPDisplayName == bl->attr_name ||
236 strcmp(target_attr->lDAPDisplayName, bl->attr_name) == 0)) {
242 /* we found an existing one */
243 if (bl->active == active) {
246 DLIST_REMOVE(replmd_private->la_backlinks, bl);
251 if (replmd_private->bl_ctx == NULL) {
252 replmd_private->bl_ctx = talloc_new(replmd_private);
253 if (replmd_private->bl_ctx == NULL) {
254 ldb_module_oom(module);
255 return LDB_ERR_OPERATIONS_ERROR;
260 bl = talloc(replmd_private->bl_ctx, struct la_backlink);
262 ldb_module_oom(module);
263 return LDB_ERR_OPERATIONS_ERROR;
266 bl->attr_name = target_attr->lDAPDisplayName;
267 bl->forward_guid = *forward_guid;
268 bl->target_guid = *target_guid;
271 /* the caller may ask for this backlink to be processed
274 int ret = replmd_process_backlink(module, bl);
279 DLIST_ADD(replmd_private->la_backlinks, bl);
286 * Callback for most write operations in this module:
288 * notify the repl task that a object has changed. The notifies are
289 * gathered up in the replmd_private structure then written to the
290 * @REPLCHANGED object in each partition during the prepare_commit
292 static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
295 struct replmd_replicated_request *ac =
296 talloc_get_type_abort(req->context, struct replmd_replicated_request);
297 struct replmd_private *replmd_private =
298 talloc_get_type_abort(ldb_module_get_private(ac->module), struct replmd_private);
299 struct nc_entry *modified_partition;
300 struct ldb_control *partition_ctrl;
301 const struct dsdb_control_current_partition *partition;
303 struct ldb_control **controls;
305 partition_ctrl = ldb_reply_get_control(ares, DSDB_CONTROL_CURRENT_PARTITION_OID);
307 /* Remove the 'partition' control from what we pass up the chain */
308 controls = controls_except_specified(ares->controls, ares, partition_ctrl);
310 if (ares->error != LDB_SUCCESS) {
311 return ldb_module_done(ac->req, controls,
312 ares->response, ares->error);
315 if (ares->type != LDB_REPLY_DONE) {
316 ldb_set_errstring(ldb_module_get_ctx(ac->module), "Invalid reply type for notify\n!");
317 return ldb_module_done(ac->req, NULL,
318 NULL, LDB_ERR_OPERATIONS_ERROR);
321 if (!partition_ctrl) {
322 ldb_set_errstring(ldb_module_get_ctx(ac->module),"No partition control on reply");
323 return ldb_module_done(ac->req, NULL,
324 NULL, LDB_ERR_OPERATIONS_ERROR);
327 partition = talloc_get_type_abort(partition_ctrl->data,
328 struct dsdb_control_current_partition);
330 if (ac->seq_num > 0) {
331 for (modified_partition = replmd_private->ncs; modified_partition;
332 modified_partition = modified_partition->next) {
333 if (ldb_dn_compare(modified_partition->dn, partition->dn) == 0) {
338 if (modified_partition == NULL) {
339 modified_partition = talloc_zero(replmd_private, struct nc_entry);
340 if (!modified_partition) {
341 ldb_oom(ldb_module_get_ctx(ac->module));
342 return ldb_module_done(ac->req, NULL,
343 NULL, LDB_ERR_OPERATIONS_ERROR);
345 modified_partition->dn = ldb_dn_copy(modified_partition, partition->dn);
346 if (!modified_partition->dn) {
347 ldb_oom(ldb_module_get_ctx(ac->module));
348 return ldb_module_done(ac->req, NULL,
349 NULL, LDB_ERR_OPERATIONS_ERROR);
351 DLIST_ADD(replmd_private->ncs, modified_partition);
354 if (ac->seq_num > modified_partition->mod_usn) {
355 modified_partition->mod_usn = ac->seq_num;
359 if (ac->apply_mode) {
363 ret = replmd_replicated_apply_next(ac);
364 if (ret != LDB_SUCCESS) {
365 return ldb_module_done(ac->req, NULL, NULL, ret);
369 /* free the partition control container here, for the
370 * common path. Other cases will have it cleaned up
371 * eventually with the ares */
372 talloc_free(partition_ctrl);
373 return ldb_module_done(ac->req,
374 controls_except_specified(controls, ares, partition_ctrl),
375 ares->response, LDB_SUCCESS);
381 * update a @REPLCHANGED record in each partition if there have been
382 * any writes of replicated data in the partition
384 static int replmd_notify_store(struct ldb_module *module)
386 struct replmd_private *replmd_private =
387 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
388 struct ldb_context *ldb = ldb_module_get_ctx(module);
390 while (replmd_private->ncs) {
392 struct nc_entry *modified_partition = replmd_private->ncs;
394 ret = dsdb_save_partition_usn(ldb, modified_partition->dn, modified_partition->mod_usn);
395 if (ret != LDB_SUCCESS) {
396 DEBUG(0,(__location__ ": Failed to save partition uSN for %s\n",
397 ldb_dn_get_linearized(modified_partition->dn)));
400 DLIST_REMOVE(replmd_private->ncs, modified_partition);
401 talloc_free(modified_partition);
409 created a replmd_replicated_request context
411 static struct replmd_replicated_request *replmd_ctx_init(struct ldb_module *module,
412 struct ldb_request *req)
414 struct ldb_context *ldb;
415 struct replmd_replicated_request *ac;
417 ldb = ldb_module_get_ctx(module);
419 ac = talloc_zero(req, struct replmd_replicated_request);
428 ac->schema = dsdb_get_schema(ldb);
430 ldb_debug_set(ldb, LDB_DEBUG_FATAL,
431 "replmd_modify: no dsdb_schema loaded");
432 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
440 add a time element to a record
442 static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
444 struct ldb_message_element *el;
447 if (ldb_msg_find_element(msg, attr) != NULL) {
451 s = ldb_timestring(msg, t);
453 return LDB_ERR_OPERATIONS_ERROR;
456 if (ldb_msg_add_string(msg, attr, s) != LDB_SUCCESS) {
457 return LDB_ERR_OPERATIONS_ERROR;
460 el = ldb_msg_find_element(msg, attr);
461 /* always set as replace. This works because on add ops, the flag
463 el->flags = LDB_FLAG_MOD_REPLACE;
469 add a uint64_t element to a record
471 static int add_uint64_element(struct ldb_message *msg, const char *attr, uint64_t v)
473 struct ldb_message_element *el;
475 if (ldb_msg_find_element(msg, attr) != NULL) {
479 if (ldb_msg_add_fmt(msg, attr, "%llu", (unsigned long long)v) != LDB_SUCCESS) {
480 return LDB_ERR_OPERATIONS_ERROR;
483 el = ldb_msg_find_element(msg, attr);
484 /* always set as replace. This works because on add ops, the flag
486 el->flags = LDB_FLAG_MOD_REPLACE;
491 static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMetaData1 *m1,
492 const struct replPropertyMetaData1 *m2,
493 const uint32_t *rdn_attid)
495 if (m1->attid == m2->attid) {
500 * the rdn attribute should be at the end!
501 * so we need to return a value greater than zero
502 * which means m1 is greater than m2
504 if (m1->attid == *rdn_attid) {
509 * the rdn attribute should be at the end!
510 * so we need to return a value less than zero
511 * which means m2 is greater than m1
513 if (m2->attid == *rdn_attid) {
517 return m1->attid > m2->attid ? 1 : -1;
520 static int replmd_replPropertyMetaDataCtr1_sort(struct replPropertyMetaDataCtr1 *ctr1,
521 const struct dsdb_schema *schema,
524 const char *rdn_name;
525 const struct dsdb_attribute *rdn_sa;
527 rdn_name = ldb_dn_get_rdn_name(dn);
529 DEBUG(0,(__location__ ": No rDN for %s?\n", ldb_dn_get_linearized(dn)));
530 return LDB_ERR_OPERATIONS_ERROR;
533 rdn_sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
534 if (rdn_sa == NULL) {
535 DEBUG(0,(__location__ ": No sa found for rDN %s for %s\n", rdn_name, ldb_dn_get_linearized(dn)));
536 return LDB_ERR_OPERATIONS_ERROR;
539 DEBUG(6,("Sorting rpmd with attid exception %u rDN=%s DN=%s\n",
540 rdn_sa->attributeID_id, rdn_name, ldb_dn_get_linearized(dn)));
542 ldb_qsort(ctr1->array, ctr1->count, sizeof(struct replPropertyMetaData1),
543 discard_const_p(void, &rdn_sa->attributeID_id),
544 (ldb_qsort_cmp_fn_t)replmd_replPropertyMetaData1_attid_sort);
549 static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1,
550 const struct ldb_message_element *e2,
551 const struct dsdb_schema *schema)
553 const struct dsdb_attribute *a1;
554 const struct dsdb_attribute *a2;
557 * TODO: make this faster by caching the dsdb_attribute pointer
558 * on the ldb_messag_element
561 a1 = dsdb_attribute_by_lDAPDisplayName(schema, e1->name);
562 a2 = dsdb_attribute_by_lDAPDisplayName(schema, e2->name);
565 * TODO: remove this check, we should rely on e1 and e2 having valid attribute names
569 return strcasecmp(e1->name, e2->name);
571 if (a1->attributeID_id == a2->attributeID_id) {
574 return a1->attributeID_id > a2->attributeID_id ? 1 : -1;
577 static void replmd_ldb_message_sort(struct ldb_message *msg,
578 const struct dsdb_schema *schema)
580 ldb_qsort(msg->elements, msg->num_elements, sizeof(struct ldb_message_element),
581 discard_const_p(void, schema), (ldb_qsort_cmp_fn_t)replmd_ldb_message_element_attid_sort);
584 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
585 const struct GUID *invocation_id, uint64_t seq_num,
586 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted);
590 fix up linked attributes in replmd_add.
591 This involves setting up the right meta-data in extended DN
592 components, and creating backlinks to the object
594 static int replmd_add_fix_la(struct ldb_module *module, struct ldb_message_element *el,
595 uint64_t seq_num, const struct GUID *invocationId, time_t t,
596 struct GUID *guid, const struct dsdb_attribute *sa)
599 TALLOC_CTX *tmp_ctx = talloc_new(el->values);
600 struct ldb_context *ldb = ldb_module_get_ctx(module);
601 struct dsdb_schema *schema = dsdb_get_schema(ldb);
604 unix_to_nt_time(&now, t);
606 for (i=0; i<el->num_values; i++) {
607 struct ldb_val *v = &el->values[i];
608 struct dsdb_dn *dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, v, sa->syntax->ldap_oid);
609 struct GUID target_guid;
613 /* note that the DN already has the extended
614 components from the extended_dn_store module */
615 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
616 if (!NT_STATUS_IS_OK(status) || GUID_all_zero(&target_guid)) {
617 ret = dsdb_module_guid_by_dn(module, dsdb_dn->dn, &target_guid);
618 if (ret != LDB_SUCCESS) {
619 talloc_free(tmp_ctx);
622 ret = dsdb_set_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
623 if (ret != LDB_SUCCESS) {
624 talloc_free(tmp_ctx);
629 ret = replmd_build_la_val(el->values, v, dsdb_dn, invocationId,
630 seq_num, seq_num, now, 0, false);
631 if (ret != LDB_SUCCESS) {
632 talloc_free(tmp_ctx);
636 ret = replmd_add_backlink(module, schema, guid, &target_guid, true, sa, false);
637 if (ret != LDB_SUCCESS) {
638 talloc_free(tmp_ctx);
643 talloc_free(tmp_ctx);
649 intercept add requests
651 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
653 struct ldb_context *ldb;
654 struct ldb_control *control;
655 struct replmd_replicated_request *ac;
656 enum ndr_err_code ndr_err;
657 struct ldb_request *down_req;
658 struct ldb_message *msg;
659 const DATA_BLOB *guid_blob;
661 struct replPropertyMetaDataBlob nmd;
662 struct ldb_val nmd_value;
663 const struct GUID *our_invocation_id;
664 time_t t = time(NULL);
669 bool allow_add_guid = false;
670 bool remove_current_guid = false;
672 /* check if there's a show relax control (used by provision to say 'I know what I'm doing') */
673 control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID);
678 /* do not manipulate our control entries */
679 if (ldb_dn_is_special(req->op.add.message->dn)) {
680 return ldb_next_request(module, req);
683 ldb = ldb_module_get_ctx(module);
685 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_add\n");
687 ac = replmd_ctx_init(module, req);
689 return LDB_ERR_OPERATIONS_ERROR;
692 guid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectGUID");
693 if ( guid_blob != NULL ) {
694 if( !allow_add_guid ) {
695 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
696 "replmd_add: it's not allowed to add an object with objectGUID\n");
698 return LDB_ERR_UNWILLING_TO_PERFORM;
700 NTSTATUS status = GUID_from_data_blob(guid_blob,&guid);
701 if ( !NT_STATUS_IS_OK(status)) {
702 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
703 "replmd_add: Unable to parse as a GUID the attribute objectGUID\n");
705 return LDB_ERR_UNWILLING_TO_PERFORM;
707 /* we remove this attribute as it can be a string and will not be treated
708 correctly and then we will readd it latter on in the good format*/
709 remove_current_guid = true;
713 guid = GUID_random();
716 /* Get a sequence number from the backend */
717 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
718 if (ret != LDB_SUCCESS) {
723 /* get our invocationId */
724 our_invocation_id = samdb_ntds_invocation_id(ldb);
725 if (!our_invocation_id) {
726 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
727 "replmd_add: unable to find invocationId\n");
729 return LDB_ERR_OPERATIONS_ERROR;
732 /* we have to copy the message as the caller might have it as a const */
733 msg = ldb_msg_copy_shallow(ac, req->op.add.message);
737 return LDB_ERR_OPERATIONS_ERROR;
740 /* generated times */
741 unix_to_nt_time(&now, t);
742 time_str = ldb_timestring(msg, t);
746 return LDB_ERR_OPERATIONS_ERROR;
748 if (remove_current_guid) {
749 ldb_msg_remove_attr(msg,"objectGUID");
753 * remove autogenerated attributes
755 ldb_msg_remove_attr(msg, "whenCreated");
756 ldb_msg_remove_attr(msg, "whenChanged");
757 ldb_msg_remove_attr(msg, "uSNCreated");
758 ldb_msg_remove_attr(msg, "uSNChanged");
759 ldb_msg_remove_attr(msg, "replPropertyMetaData");
762 * readd replicated attributes
764 ret = ldb_msg_add_string(msg, "whenCreated", time_str);
765 if (ret != LDB_SUCCESS) {
771 /* build the replication meta_data */
774 nmd.ctr.ctr1.count = msg->num_elements;
775 nmd.ctr.ctr1.array = talloc_array(msg,
776 struct replPropertyMetaData1,
778 if (!nmd.ctr.ctr1.array) {
781 return LDB_ERR_OPERATIONS_ERROR;
784 for (i=0; i < msg->num_elements; i++) {
785 struct ldb_message_element *e = &msg->elements[i];
786 struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
787 const struct dsdb_attribute *sa;
789 if (e->name[0] == '@') continue;
791 sa = dsdb_attribute_by_lDAPDisplayName(ac->schema, e->name);
793 ldb_debug_set(ldb, LDB_DEBUG_ERROR,
794 "replmd_add: attribute '%s' not defined in schema\n",
797 return LDB_ERR_NO_SUCH_ATTRIBUTE;
800 if ((sa->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (sa->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
801 /* if the attribute is not replicated (0x00000001)
802 * or constructed (0x00000004) it has no metadata
807 #if W2K3_LINKED_ATTRIBUTES
808 if (sa->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
809 ret = replmd_add_fix_la(module, e, ac->seq_num, our_invocation_id, t, &guid, sa);
810 if (ret != LDB_SUCCESS) {
814 /* linked attributes are not stored in
815 replPropertyMetaData in FL above w2k */
820 m->attid = sa->attributeID_id;
822 m->originating_change_time = now;
823 m->originating_invocation_id = *our_invocation_id;
824 m->originating_usn = ac->seq_num;
825 m->local_usn = ac->seq_num;
829 /* fix meta data count */
830 nmd.ctr.ctr1.count = ni;
833 * sort meta data array, and move the rdn attribute entry to the end
835 ret = replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, ac->schema, msg->dn);
836 if (ret != LDB_SUCCESS) {
841 /* generated NDR encoded values */
842 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
843 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
845 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
846 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
849 return LDB_ERR_OPERATIONS_ERROR;
853 * add the autogenerated values
855 ret = dsdb_msg_add_guid(msg, &guid, "objectGUID");
856 if (ret != LDB_SUCCESS) {
861 ret = ldb_msg_add_string(msg, "whenChanged", time_str);
862 if (ret != LDB_SUCCESS) {
867 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ac->seq_num);
868 if (ret != LDB_SUCCESS) {
873 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ac->seq_num);
874 if (ret != LDB_SUCCESS) {
879 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
880 if (ret != LDB_SUCCESS) {
887 * sort the attributes by attid before storing the object
889 replmd_ldb_message_sort(msg, ac->schema);
891 ret = ldb_build_add_req(&down_req, ldb, ac,
894 ac, replmd_op_callback,
896 if (ret != LDB_SUCCESS) {
901 /* mark the control done */
903 control->critical = 0;
906 /* go on with the call chain */
907 return ldb_next_request(module, down_req);
912 * update the replPropertyMetaData for one element
914 static int replmd_update_rpmd_element(struct ldb_context *ldb,
915 struct ldb_message *msg,
916 struct ldb_message_element *el,
917 struct replPropertyMetaDataBlob *omd,
918 const struct dsdb_schema *schema,
920 const struct GUID *our_invocation_id,
924 const struct dsdb_attribute *a;
925 struct replPropertyMetaData1 *md1;
927 a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
929 DEBUG(0,(__location__ ": Unable to find attribute %s in schema\n",
931 return LDB_ERR_OPERATIONS_ERROR;
934 if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
938 for (i=0; i<omd->ctr.ctr1.count; i++) {
939 if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) break;
942 #if W2K3_LINKED_ATTRIBUTES
943 if (a->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
944 /* linked attributes are not stored in
945 replPropertyMetaData in FL above w2k, but we do
946 raise the seqnum for the object */
948 ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num) != LDB_SUCCESS) {
949 return LDB_ERR_OPERATIONS_ERROR;
955 if (i == omd->ctr.ctr1.count) {
956 /* we need to add a new one */
957 omd->ctr.ctr1.array = talloc_realloc(msg, omd->ctr.ctr1.array,
958 struct replPropertyMetaData1, omd->ctr.ctr1.count+1);
959 if (omd->ctr.ctr1.array == NULL) {
961 return LDB_ERR_OPERATIONS_ERROR;
963 omd->ctr.ctr1.count++;
964 ZERO_STRUCT(omd->ctr.ctr1.array[i]);
967 /* Get a new sequence number from the backend. We only do this
968 * if we have a change that requires a new
969 * replPropertyMetaData element
972 int ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, seq_num);
973 if (ret != LDB_SUCCESS) {
974 return LDB_ERR_OPERATIONS_ERROR;
978 md1 = &omd->ctr.ctr1.array[i];
980 md1->attid = a->attributeID_id;
981 md1->originating_change_time = now;
982 md1->originating_invocation_id = *our_invocation_id;
983 md1->originating_usn = *seq_num;
984 md1->local_usn = *seq_num;
990 * update the replPropertyMetaData object each time we modify an
991 * object. This is needed for DRS replication, as the merge on the
992 * client is based on this object
994 static int replmd_update_rpmd(struct ldb_module *module,
995 const struct dsdb_schema *schema,
996 struct ldb_message *msg, uint64_t *seq_num,
999 const struct ldb_val *omd_value;
1000 enum ndr_err_code ndr_err;
1001 struct replPropertyMetaDataBlob omd;
1004 const struct GUID *our_invocation_id;
1006 const char *attrs[] = { "replPropertyMetaData" , NULL };
1007 struct ldb_result *res;
1008 struct ldb_context *ldb;
1010 ldb = ldb_module_get_ctx(module);
1012 our_invocation_id = samdb_ntds_invocation_id(ldb);
1013 if (!our_invocation_id) {
1014 /* this happens during an initial vampire while
1015 updating the schema */
1016 DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
1020 unix_to_nt_time(&now, t);
1022 /* search for the existing replPropertyMetaDataBlob */
1023 ret = dsdb_search_dn_with_deleted(ldb, msg, &res, msg->dn, attrs);
1024 if (ret != LDB_SUCCESS || res->count != 1) {
1025 DEBUG(0,(__location__ ": Object %s failed to find replPropertyMetaData\n",
1026 ldb_dn_get_linearized(msg->dn)));
1027 return LDB_ERR_OPERATIONS_ERROR;
1031 omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
1033 DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
1034 ldb_dn_get_linearized(msg->dn)));
1035 return LDB_ERR_OPERATIONS_ERROR;
1038 ndr_err = ndr_pull_struct_blob(omd_value, msg,
1039 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), &omd,
1040 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
1041 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1042 DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
1043 ldb_dn_get_linearized(msg->dn)));
1044 return LDB_ERR_OPERATIONS_ERROR;
1047 if (omd.version != 1) {
1048 DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
1049 omd.version, ldb_dn_get_linearized(msg->dn)));
1050 return LDB_ERR_OPERATIONS_ERROR;
1053 for (i=0; i<msg->num_elements; i++) {
1054 ret = replmd_update_rpmd_element(ldb, msg, &msg->elements[i], &omd, schema, seq_num,
1055 our_invocation_id, now);
1056 if (ret != LDB_SUCCESS) {
1062 * replmd_update_rpmd_element has done an update if the
1065 if (*seq_num != 0) {
1066 struct ldb_val *md_value;
1067 struct ldb_message_element *el;
1069 md_value = talloc(msg, struct ldb_val);
1070 if (md_value == NULL) {
1072 return LDB_ERR_OPERATIONS_ERROR;
1075 ret = replmd_replPropertyMetaDataCtr1_sort(&omd.ctr.ctr1, schema, msg->dn);
1076 if (ret != LDB_SUCCESS) {
1080 ndr_err = ndr_push_struct_blob(md_value, msg,
1081 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
1083 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
1084 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1085 DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
1086 ldb_dn_get_linearized(msg->dn)));
1087 return LDB_ERR_OPERATIONS_ERROR;
1090 ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
1091 if (ret != LDB_SUCCESS) {
1092 DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
1093 ldb_dn_get_linearized(msg->dn)));
1098 el->values = md_value;
1106 struct dsdb_dn *dsdb_dn;
1111 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
1113 return GUID_compare(pdn1->guid, pdn2->guid);
1116 static struct parsed_dn *parsed_dn_find(struct parsed_dn *pdn, int count, struct GUID *guid, struct ldb_dn *dn)
1118 struct parsed_dn *ret;
1119 if (dn && GUID_all_zero(guid)) {
1120 /* when updating a link using DRS, we sometimes get a
1121 NULL GUID. We then need to try and match by DN */
1123 for (i=0; i<count; i++) {
1124 if (ldb_dn_compare(pdn[i].dsdb_dn->dn, dn) == 0) {
1125 dsdb_get_extended_dn_guid(pdn[i].dsdb_dn->dn, guid, "GUID");
1131 BINARY_ARRAY_SEARCH(pdn, count, guid, guid, GUID_compare, ret);
1136 get a series of message element values as an array of DNs and GUIDs
1137 the result is sorted by GUID
1139 static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
1140 struct ldb_message_element *el, struct parsed_dn **pdn,
1141 const char *ldap_oid)
1144 struct ldb_context *ldb = ldb_module_get_ctx(module);
1151 (*pdn) = talloc_array(mem_ctx, struct parsed_dn, el->num_values);
1153 ldb_module_oom(module);
1154 return LDB_ERR_OPERATIONS_ERROR;
1157 for (i=0; i<el->num_values; i++) {
1158 struct ldb_val *v = &el->values[i];
1161 struct parsed_dn *p;
1165 p->dsdb_dn = dsdb_dn_parse(*pdn, ldb, v, ldap_oid);
1166 if (p->dsdb_dn == NULL) {
1167 return LDB_ERR_INVALID_DN_SYNTAX;
1170 dn = p->dsdb_dn->dn;
1172 p->guid = talloc(*pdn, struct GUID);
1173 if (p->guid == NULL) {
1174 ldb_module_oom(module);
1175 return LDB_ERR_OPERATIONS_ERROR;
1178 status = dsdb_get_extended_dn_guid(dn, p->guid, "GUID");
1179 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1180 /* we got a DN without a GUID - go find the GUID */
1181 int ret = dsdb_module_guid_by_dn(module, dn, p->guid);
1182 if (ret != LDB_SUCCESS) {
1183 ldb_asprintf_errstring(ldb, "Unable to find GUID for DN %s\n",
1184 ldb_dn_get_linearized(dn));
1187 ret = dsdb_set_extended_dn_guid(dn, p->guid, "GUID");
1188 if (ret != LDB_SUCCESS) {
1191 } else if (!NT_STATUS_IS_OK(status)) {
1192 return LDB_ERR_OPERATIONS_ERROR;
1195 /* keep a pointer to the original ldb_val */
1199 qsort(*pdn, el->num_values, sizeof((*pdn)[0]), (comparison_fn_t)parsed_dn_compare);
1205 build a new extended DN, including all meta data fields
1207 DELETED = 1 or missing
1208 RMD_ADDTIME = originating_add_time
1209 RMD_INVOCID = originating_invocation_id
1210 RMD_CHANGETIME = originating_change_time
1211 RMD_ORIGINATING_USN = originating_usn
1212 RMD_LOCAL_USN = local_usn
1213 RMD_VERSION = version
1215 static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1216 const struct GUID *invocation_id, uint64_t seq_num,
1217 uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted)
1219 struct ldb_dn *dn = dsdb_dn->dn;
1220 const char *tstring, *usn_string;
1221 struct ldb_val tval;
1223 struct ldb_val usnv, local_usnv;
1224 struct ldb_val vers;
1227 const char *dnstring;
1230 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
1232 return LDB_ERR_OPERATIONS_ERROR;
1234 tval = data_blob_string_const(tstring);
1236 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
1238 return LDB_ERR_OPERATIONS_ERROR;
1240 usnv = data_blob_string_const(usn_string);
1242 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
1244 return LDB_ERR_OPERATIONS_ERROR;
1246 local_usnv = data_blob_string_const(usn_string);
1248 vstring = talloc_asprintf(mem_ctx, "%lu", (unsigned long)version);
1249 vers = data_blob_string_const(vstring);
1251 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1252 if (!NT_STATUS_IS_OK(status)) {
1253 return LDB_ERR_OPERATIONS_ERROR;
1258 dv = data_blob_string_const("1");
1259 ret = ldb_dn_set_extended_component(dn, "DELETED", &dv);
1261 ret = ldb_dn_set_extended_component(dn, "DELETED", NULL);
1263 if (ret != LDB_SUCCESS) return ret;
1264 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", &tval);
1265 if (ret != LDB_SUCCESS) return ret;
1266 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1267 if (ret != LDB_SUCCESS) return ret;
1268 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1269 if (ret != LDB_SUCCESS) return ret;
1270 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
1271 if (ret != LDB_SUCCESS) return ret;
1272 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
1273 if (ret != LDB_SUCCESS) return ret;
1274 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1275 if (ret != LDB_SUCCESS) return ret;
1277 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
1278 if (dnstring == NULL) {
1279 return LDB_ERR_OPERATIONS_ERROR;
1281 *v = data_blob_string_const(dnstring);
1286 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1287 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1288 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
1289 uint32_t version, bool deleted);
1292 check if any links need upgrading from w2k format
1294 static int replmd_check_upgrade_links(struct parsed_dn *dns, uint32_t count, const struct GUID *invocation_id)
1297 for (i=0; i<count; i++) {
1302 status = dsdb_get_extended_dn_uint32(dns[i].dsdb_dn->dn, &version, "RMD_VERSION");
1303 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1307 /* it's an old one that needs upgrading */
1308 ret = replmd_update_la_val(dns, dns[i].v, dns[i].dsdb_dn, dns[i].dsdb_dn, invocation_id,
1310 if (ret != LDB_SUCCESS) {
1318 update an extended DN, including all meta data fields
1320 see replmd_build_la_val for value names
1322 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
1323 struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
1324 uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
1325 uint32_t version, bool deleted)
1327 struct ldb_dn *dn = dsdb_dn->dn;
1328 const char *tstring, *usn_string;
1329 struct ldb_val tval;
1331 struct ldb_val usnv, local_usnv;
1332 struct ldb_val vers;
1333 const struct ldb_val *old_addtime;
1334 uint32_t old_version;
1337 const char *dnstring;
1340 tstring = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)nttime);
1342 return LDB_ERR_OPERATIONS_ERROR;
1344 tval = data_blob_string_const(tstring);
1346 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
1348 return LDB_ERR_OPERATIONS_ERROR;
1350 usnv = data_blob_string_const(usn_string);
1352 usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)local_usn);
1354 return LDB_ERR_OPERATIONS_ERROR;
1356 local_usnv = data_blob_string_const(usn_string);
1358 status = GUID_to_ndr_blob(invocation_id, dn, &iid);
1359 if (!NT_STATUS_IS_OK(status)) {
1360 return LDB_ERR_OPERATIONS_ERROR;
1365 dv = data_blob_string_const("1");
1366 ret = ldb_dn_set_extended_component(dn, "DELETED", &dv);
1368 ret = ldb_dn_set_extended_component(dn, "DELETED", NULL);
1370 if (ret != LDB_SUCCESS) return ret;
1372 /* get the ADDTIME from the original */
1373 old_addtime = ldb_dn_get_extended_component(old_dsdb_dn->dn, "RMD_ADDTIME");
1374 if (old_addtime == NULL) {
1375 old_addtime = &tval;
1377 if (dsdb_dn != old_dsdb_dn) {
1378 ret = ldb_dn_set_extended_component(dn, "RMD_ADDTIME", old_addtime);
1379 if (ret != LDB_SUCCESS) return ret;
1382 /* use our invocation id */
1383 ret = ldb_dn_set_extended_component(dn, "RMD_INVOCID", &iid);
1384 if (ret != LDB_SUCCESS) return ret;
1386 /* changetime is the current time */
1387 ret = ldb_dn_set_extended_component(dn, "RMD_CHANGETIME", &tval);
1388 if (ret != LDB_SUCCESS) return ret;
1390 /* update the USN */
1391 ret = ldb_dn_set_extended_component(dn, "RMD_ORIGINATING_USN", &usnv);
1392 if (ret != LDB_SUCCESS) return ret;
1394 ret = ldb_dn_set_extended_component(dn, "RMD_LOCAL_USN", &local_usnv);
1395 if (ret != LDB_SUCCESS) return ret;
1397 /* increase the version by 1 */
1398 status = dsdb_get_extended_dn_uint32(old_dsdb_dn->dn, &old_version, "RMD_VERSION");
1399 if (NT_STATUS_IS_OK(status) && old_version >= version) {
1400 version = old_version+1;
1402 vstring = talloc_asprintf(dn, "%lu", (unsigned long)version);
1403 vers = data_blob_string_const(vstring);
1404 ret = ldb_dn_set_extended_component(dn, "RMD_VERSION", &vers);
1405 if (ret != LDB_SUCCESS) return ret;
1407 dnstring = dsdb_dn_get_extended_linearized(mem_ctx, dsdb_dn, 1);
1408 if (dnstring == NULL) {
1409 return LDB_ERR_OPERATIONS_ERROR;
1411 *v = data_blob_string_const(dnstring);
1417 handle adding a linked attribute
1419 static int replmd_modify_la_add(struct ldb_module *module,
1420 struct dsdb_schema *schema,
1421 struct ldb_message *msg,
1422 struct ldb_message_element *el,
1423 struct ldb_message_element *old_el,
1424 const struct dsdb_attribute *schema_attr,
1427 struct GUID *msg_guid)
1430 struct parsed_dn *dns, *old_dns;
1431 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1433 struct ldb_val *new_values = NULL;
1434 unsigned int num_new_values = 0;
1435 unsigned old_num_values = old_el?old_el->num_values:0;
1436 const struct GUID *invocation_id;
1437 struct ldb_context *ldb = ldb_module_get_ctx(module);
1440 unix_to_nt_time(&now, t);
1442 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid);
1443 if (ret != LDB_SUCCESS) {
1444 talloc_free(tmp_ctx);
1448 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid);
1449 if (ret != LDB_SUCCESS) {
1450 talloc_free(tmp_ctx);
1454 invocation_id = samdb_ntds_invocation_id(ldb);
1455 if (!invocation_id) {
1456 talloc_free(tmp_ctx);
1457 return LDB_ERR_OPERATIONS_ERROR;
1460 ret = replmd_check_upgrade_links(old_dns, old_num_values, invocation_id);
1461 if (ret != LDB_SUCCESS) {
1462 talloc_free(tmp_ctx);
1466 /* for each new value, see if it exists already with the same GUID */
1467 for (i=0; i<el->num_values; i++) {
1468 struct parsed_dn *p = parsed_dn_find(old_dns, old_num_values, dns[i].guid, NULL);
1470 /* this is a new linked attribute value */
1471 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val, num_new_values+1);
1472 if (new_values == NULL) {
1473 ldb_module_oom(module);
1474 talloc_free(tmp_ctx);
1475 return LDB_ERR_OPERATIONS_ERROR;
1477 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
1478 invocation_id, seq_num, seq_num, now, 0, false);
1479 if (ret != LDB_SUCCESS) {
1480 talloc_free(tmp_ctx);
1485 /* this is only allowed if the GUID was
1486 previously deleted. */
1487 const struct ldb_val *v;
1488 v = ldb_dn_get_extended_component(p->dsdb_dn->dn, "DELETED");
1490 ldb_asprintf_errstring(ldb, "Attribute %s already exists for target GUID %s",
1491 el->name, GUID_string(tmp_ctx, p->guid));
1492 talloc_free(tmp_ctx);
1493 return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
1495 ret = replmd_update_la_val(old_el->values, p->v, dns[i].dsdb_dn, p->dsdb_dn,
1496 invocation_id, seq_num, seq_num, now, 0, false);
1497 if (ret != LDB_SUCCESS) {
1498 talloc_free(tmp_ctx);
1503 ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, true);
1504 if (ret != LDB_SUCCESS) {
1505 talloc_free(tmp_ctx);
1510 /* add the new ones on to the end of the old values, constructing a new el->values */
1511 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
1513 old_num_values+num_new_values);
1514 if (el->values == NULL) {
1515 ldb_module_oom(module);
1516 return LDB_ERR_OPERATIONS_ERROR;
1519 memcpy(&el->values[old_num_values], new_values, num_new_values*sizeof(struct ldb_val));
1520 el->num_values = old_num_values + num_new_values;
1522 talloc_steal(msg->elements, el->values);
1523 talloc_steal(el->values, new_values);
1525 talloc_free(tmp_ctx);
1527 /* we now tell the backend to replace all existing values
1528 with the one we have constructed */
1529 el->flags = LDB_FLAG_MOD_REPLACE;
1536 handle deleting all active linked attributes
1538 static int replmd_modify_la_delete(struct ldb_module *module,
1539 struct dsdb_schema *schema,
1540 struct ldb_message *msg,
1541 struct ldb_message_element *el,
1542 struct ldb_message_element *old_el,
1543 const struct dsdb_attribute *schema_attr,
1546 struct GUID *msg_guid)
1549 struct parsed_dn *dns, *old_dns;
1550 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1552 const struct GUID *invocation_id;
1553 struct ldb_context *ldb = ldb_module_get_ctx(module);
1556 unix_to_nt_time(&now, t);
1558 /* check if there is nothing to delete */
1559 if ((!old_el || old_el->num_values == 0) &&
1560 el->num_values == 0) {
1564 if (!old_el || old_el->num_values == 0) {
1565 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1568 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid);
1569 if (ret != LDB_SUCCESS) {
1570 talloc_free(tmp_ctx);
1574 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid);
1575 if (ret != LDB_SUCCESS) {
1576 talloc_free(tmp_ctx);
1580 invocation_id = samdb_ntds_invocation_id(ldb);
1581 if (!invocation_id) {
1582 return LDB_ERR_OPERATIONS_ERROR;
1585 ret = replmd_check_upgrade_links(old_dns, old_el->num_values, invocation_id);
1586 if (ret != LDB_SUCCESS) {
1587 talloc_free(tmp_ctx);
1593 /* see if we are being asked to delete any links that
1594 don't exist or are already deleted */
1595 for (i=0; i<el->num_values; i++) {
1596 struct parsed_dn *p = &dns[i];
1597 struct parsed_dn *p2;
1598 const struct ldb_val *v;
1600 p2 = parsed_dn_find(old_dns, old_el->num_values, p->guid, NULL);
1602 ldb_asprintf_errstring(ldb, "Attribute %s doesn't exist for target GUID %s",
1603 el->name, GUID_string(tmp_ctx, p->guid));
1604 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1606 v = ldb_dn_get_extended_component(p2->dsdb_dn->dn, "DELETED");
1608 ldb_asprintf_errstring(ldb, "Attribute %s already deleted for target GUID %s",
1609 el->name, GUID_string(tmp_ctx, p->guid));
1610 return LDB_ERR_NO_SUCH_ATTRIBUTE;
1614 /* for each new value, see if it exists already with the same GUID
1615 if it is not already deleted and matches the delete list then delete it
1617 for (i=0; i<old_el->num_values; i++) {
1618 struct parsed_dn *p = &old_dns[i];
1619 const struct ldb_val *v;
1621 if (el->num_values && parsed_dn_find(dns, el->num_values, p->guid, NULL) == NULL) {
1625 v = ldb_dn_get_extended_component(p->dsdb_dn->dn, "DELETED");
1626 if (v != NULL) continue;
1628 ret = replmd_update_la_val(old_el->values, p->v, p->dsdb_dn, p->dsdb_dn,
1629 invocation_id, seq_num, seq_num, now, 0, true);
1630 if (ret != LDB_SUCCESS) {
1631 talloc_free(tmp_ctx);
1635 ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, true);
1636 if (ret != LDB_SUCCESS) {
1637 talloc_free(tmp_ctx);
1642 el->values = talloc_steal(msg->elements, old_el->values);
1643 el->num_values = old_el->num_values;
1645 talloc_free(tmp_ctx);
1647 /* we now tell the backend to replace all existing values
1648 with the one we have constructed */
1649 el->flags = LDB_FLAG_MOD_REPLACE;
1655 handle replacing a linked attribute
1657 static int replmd_modify_la_replace(struct ldb_module *module,
1658 struct dsdb_schema *schema,
1659 struct ldb_message *msg,
1660 struct ldb_message_element *el,
1661 struct ldb_message_element *old_el,
1662 const struct dsdb_attribute *schema_attr,
1665 struct GUID *msg_guid)
1668 struct parsed_dn *dns, *old_dns;
1669 TALLOC_CTX *tmp_ctx = talloc_new(msg);
1671 const struct GUID *invocation_id;
1672 struct ldb_context *ldb = ldb_module_get_ctx(module);
1673 struct ldb_val *new_values = NULL;
1674 uint32_t num_new_values = 0;
1675 unsigned old_num_values = old_el?old_el->num_values:0;
1678 unix_to_nt_time(&now, t);
1680 /* check if there is nothing to replace */
1681 if ((!old_el || old_el->num_values == 0) &&
1682 el->num_values == 0) {
1686 ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid);
1687 if (ret != LDB_SUCCESS) {
1688 talloc_free(tmp_ctx);
1692 ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid);
1693 if (ret != LDB_SUCCESS) {
1694 talloc_free(tmp_ctx);
1698 invocation_id = samdb_ntds_invocation_id(ldb);
1699 if (!invocation_id) {
1700 return LDB_ERR_OPERATIONS_ERROR;
1703 ret = replmd_check_upgrade_links(old_dns, old_num_values, invocation_id);
1704 if (ret != LDB_SUCCESS) {
1705 talloc_free(tmp_ctx);
1709 /* mark all the old ones as deleted */
1710 for (i=0; i<old_num_values; i++) {
1711 struct parsed_dn *old_p = &old_dns[i];
1712 struct parsed_dn *p;
1713 const struct ldb_val *v;
1715 v = ldb_dn_get_extended_component(old_p->dsdb_dn->dn, "DELETED");
1718 ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, false);
1719 if (ret != LDB_SUCCESS) {
1720 talloc_free(tmp_ctx);
1724 p = parsed_dn_find(dns, el->num_values, old_p->guid, NULL);
1726 /* we don't delete it if we are re-adding it */
1730 ret = replmd_update_la_val(old_el->values, old_p->v, old_p->dsdb_dn, old_p->dsdb_dn,
1731 invocation_id, seq_num, seq_num, now, 0, true);
1732 if (ret != LDB_SUCCESS) {
1733 talloc_free(tmp_ctx);
1738 /* for each new value, either update its meta-data, or add it
1741 for (i=0; i<el->num_values; i++) {
1742 struct parsed_dn *p = &dns[i], *old_p;
1745 (old_p = parsed_dn_find(old_dns,
1746 old_num_values, p->guid, NULL)) != NULL) {
1747 /* update in place */
1748 ret = replmd_update_la_val(old_el->values, old_p->v, old_p->dsdb_dn,
1749 old_p->dsdb_dn, invocation_id,
1750 seq_num, seq_num, now, 0, false);
1751 if (ret != LDB_SUCCESS) {
1752 talloc_free(tmp_ctx);
1757 new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val,
1759 if (new_values == NULL) {
1760 ldb_module_oom(module);
1761 talloc_free(tmp_ctx);
1762 return LDB_ERR_OPERATIONS_ERROR;
1764 ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
1765 invocation_id, seq_num, seq_num, now, 0, false);
1766 if (ret != LDB_SUCCESS) {
1767 talloc_free(tmp_ctx);
1773 ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, false);
1774 if (ret != LDB_SUCCESS) {
1775 talloc_free(tmp_ctx);
1780 /* add the new values to the end of old_el */
1781 if (num_new_values != 0) {
1782 el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
1783 struct ldb_val, old_num_values+num_new_values);
1784 if (el->values == NULL) {
1785 ldb_module_oom(module);
1786 return LDB_ERR_OPERATIONS_ERROR;
1788 memcpy(&el->values[old_num_values], &new_values[0],
1789 sizeof(struct ldb_val)*num_new_values);
1790 el->num_values = old_num_values + num_new_values;
1791 talloc_steal(msg->elements, new_values);
1793 el->values = old_el->values;
1794 el->num_values = old_el->num_values;
1795 talloc_steal(msg->elements, el->values);
1798 talloc_free(tmp_ctx);
1800 /* we now tell the backend to replace all existing values
1801 with the one we have constructed */
1802 el->flags = LDB_FLAG_MOD_REPLACE;
1809 handle linked attributes in modify requests
1811 static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
1812 struct ldb_message *msg,
1813 uint64_t seq_num, time_t t)
1815 struct ldb_result *res;
1817 struct ldb_context *ldb = ldb_module_get_ctx(module);
1818 struct ldb_message *old_msg;
1819 struct dsdb_schema *schema = dsdb_get_schema(ldb);
1820 struct GUID old_guid;
1823 /* there the replmd_update_rpmd code has already
1824 * checked and saw that there are no linked
1829 #if !W2K3_LINKED_ATTRIBUTES
1833 if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
1834 /* don't do anything special for linked attributes */
1838 ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
1839 DSDB_SEARCH_SHOW_DELETED |
1840 DSDB_SEARCH_REVEAL_INTERNALS |
1841 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT);
1842 if (ret != LDB_SUCCESS) {
1845 old_msg = res->msgs[0];
1847 old_guid = samdb_result_guid(old_msg, "objectGUID");
1849 for (i=0; i<msg->num_elements; i++) {
1850 struct ldb_message_element *el = &msg->elements[i];
1851 struct ldb_message_element *old_el, *new_el;
1852 const struct dsdb_attribute *schema_attr
1853 = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
1855 ldb_asprintf_errstring(ldb,
1856 "attribute %s is not a valid attribute in schema", el->name);
1857 return LDB_ERR_OBJECT_CLASS_VIOLATION;
1859 if (schema_attr->linkID == 0) {
1862 if ((schema_attr->linkID & 1) == 1) {
1863 /* Odd is for the target. Illegal to modify */
1864 ldb_asprintf_errstring(ldb,
1865 "attribute %s must not be modified directly, it is a linked attribute", el->name);
1866 return LDB_ERR_UNWILLING_TO_PERFORM;
1868 old_el = ldb_msg_find_element(old_msg, el->name);
1869 switch (el->flags & LDB_FLAG_MOD_MASK) {
1870 case LDB_FLAG_MOD_REPLACE:
1871 ret = replmd_modify_la_replace(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid);
1873 case LDB_FLAG_MOD_DELETE:
1874 ret = replmd_modify_la_delete(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid);
1876 case LDB_FLAG_MOD_ADD:
1877 ret = replmd_modify_la_add(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid);
1880 ldb_asprintf_errstring(ldb,
1881 "invalid flags 0x%x for %s linked attribute",
1882 el->flags, el->name);
1883 return LDB_ERR_UNWILLING_TO_PERFORM;
1885 if (ret != LDB_SUCCESS) {
1889 ldb_msg_remove_attr(old_msg, el->name);
1891 ldb_msg_add_empty(old_msg, el->name, 0, &new_el);
1892 new_el->num_values = el->num_values;
1893 new_el->values = talloc_steal(msg->elements, el->values);
1895 /* TODO: this relises a bit too heavily on the exact
1896 behaviour of ldb_msg_find_element and
1897 ldb_msg_remove_element */
1898 old_el = ldb_msg_find_element(msg, el->name);
1900 ldb_msg_remove_element(msg, old_el);
1911 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
1913 struct ldb_context *ldb;
1914 struct replmd_replicated_request *ac;
1915 struct ldb_request *down_req;
1916 struct ldb_message *msg;
1917 time_t t = time(NULL);
1920 /* do not manipulate our control entries */
1921 if (ldb_dn_is_special(req->op.mod.message->dn)) {
1922 return ldb_next_request(module, req);
1925 ldb = ldb_module_get_ctx(module);
1927 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
1929 ac = replmd_ctx_init(module, req);
1931 return LDB_ERR_OPERATIONS_ERROR;
1934 /* we have to copy the message as the caller might have it as a const */
1935 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
1939 return LDB_ERR_OPERATIONS_ERROR;
1942 ldb_msg_remove_attr(msg, "whenChanged");
1943 ldb_msg_remove_attr(msg, "uSNChanged");
1945 ret = replmd_update_rpmd(module, ac->schema, msg, &ac->seq_num, t);
1946 if (ret != LDB_SUCCESS) {
1951 ret = replmd_modify_handle_linked_attribs(module, msg, ac->seq_num, t);
1952 if (ret != LDB_SUCCESS) {
1958 * - replace the old object with the newly constructed one
1961 ret = ldb_build_mod_req(&down_req, ldb, ac,
1964 ac, replmd_op_callback,
1966 if (ret != LDB_SUCCESS) {
1970 talloc_steal(down_req, msg);
1972 /* we only change whenChanged and uSNChanged if the seq_num
1974 if (ac->seq_num != 0) {
1975 if (add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) {
1980 if (add_uint64_element(msg, "uSNChanged", ac->seq_num) != LDB_SUCCESS) {
1986 /* go on with the call chain */
1987 return ldb_next_request(module, down_req);
1990 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares);
1993 handle a rename request
1995 On a rename we need to do an extra ldb_modify which sets the
1996 whenChanged and uSNChanged attributes. We do this in a callback after the success.
1998 static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
2000 struct ldb_context *ldb;
2001 struct replmd_replicated_request *ac;
2003 struct ldb_request *down_req;
2005 /* do not manipulate our control entries */
2006 if (ldb_dn_is_special(req->op.mod.message->dn)) {
2007 return ldb_next_request(module, req);
2010 ldb = ldb_module_get_ctx(module);
2012 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_rename\n");
2014 ac = replmd_ctx_init(module, req);
2016 return LDB_ERR_OPERATIONS_ERROR;
2018 ret = ldb_build_rename_req(&down_req, ldb, ac,
2019 ac->req->op.rename.olddn,
2020 ac->req->op.rename.newdn,
2022 ac, replmd_rename_callback,
2025 if (ret != LDB_SUCCESS) {
2030 /* go on with the call chain */
2031 return ldb_next_request(module, down_req);
2034 /* After the rename is compleated, update the whenchanged etc */
2035 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
2037 struct ldb_context *ldb;
2038 struct replmd_replicated_request *ac;
2039 struct ldb_request *down_req;
2040 struct ldb_message *msg;
2041 time_t t = time(NULL);
2044 ac = talloc_get_type(req->context, struct replmd_replicated_request);
2045 ldb = ldb_module_get_ctx(ac->module);
2047 if (ares->error != LDB_SUCCESS) {
2048 return ldb_module_done(ac->req, ares->controls,
2049 ares->response, ares->error);
2052 if (ares->type != LDB_REPLY_DONE) {
2053 ldb_set_errstring(ldb,
2054 "invalid ldb_reply_type in callback");
2056 return ldb_module_done(ac->req, NULL, NULL,
2057 LDB_ERR_OPERATIONS_ERROR);
2060 /* Get a sequence number from the backend */
2061 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ac->seq_num);
2062 if (ret != LDB_SUCCESS) {
2067 * - replace the old object with the newly constructed one
2070 msg = ldb_msg_new(ac);
2073 return LDB_ERR_OPERATIONS_ERROR;
2076 msg->dn = ac->req->op.rename.newdn;
2078 ret = ldb_build_mod_req(&down_req, ldb, ac,
2081 ac, replmd_op_callback,
2084 if (ret != LDB_SUCCESS) {
2088 talloc_steal(down_req, msg);
2090 if (add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) {
2095 if (add_uint64_element(msg, "uSNChanged", ac->seq_num) != LDB_SUCCESS) {
2100 /* go on with the call chain - do the modify after the rename */
2101 return ldb_next_request(ac->module, down_req);
2105 remove links from objects that point at this object when an object
2108 static int replmd_delete_remove_link(struct ldb_module *module,
2109 struct dsdb_schema *schema,
2111 struct ldb_message_element *el,
2112 const struct dsdb_attribute *sa)
2115 TALLOC_CTX *tmp_ctx = talloc_new(module);
2116 struct ldb_context *ldb = ldb_module_get_ctx(module);
2118 for (i=0; i<el->num_values; i++) {
2119 struct dsdb_dn *dsdb_dn;
2123 struct ldb_message *msg;
2124 const struct dsdb_attribute *target_attr;
2125 struct ldb_message_element *el2;
2126 struct ldb_val dn_val;
2128 if (dsdb_dn_is_deleted_val(&el->values[i])) {
2132 dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
2134 talloc_free(tmp_ctx);
2135 return LDB_ERR_OPERATIONS_ERROR;
2138 status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid2, "GUID");
2139 if (!NT_STATUS_IS_OK(status)) {
2140 talloc_free(tmp_ctx);
2141 return LDB_ERR_OPERATIONS_ERROR;
2144 /* remove the link */
2145 msg = ldb_msg_new(tmp_ctx);
2147 ldb_module_oom(module);
2148 talloc_free(tmp_ctx);
2149 return LDB_ERR_OPERATIONS_ERROR;
2153 msg->dn = dsdb_dn->dn;
2155 target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
2156 if (target_attr == NULL) {
2160 ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName, LDB_FLAG_MOD_DELETE, &el2);
2161 if (ret != LDB_SUCCESS) {
2162 ldb_module_oom(module);
2163 talloc_free(tmp_ctx);
2164 return LDB_ERR_OPERATIONS_ERROR;
2166 dn_val = data_blob_string_const(ldb_dn_get_linearized(dn));
2167 el2->values = &dn_val;
2168 el2->num_values = 1;
2170 ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE);
2171 if (ret != LDB_SUCCESS) {
2172 talloc_free(tmp_ctx);
2176 talloc_free(tmp_ctx);
2182 handle update of replication meta data for deletion of objects
2184 This also handles the mapping of delete to a rename operation
2185 to allow deletes to be replicated.
2187 static int replmd_delete(struct ldb_module *module, struct ldb_request *req)
2189 int ret = LDB_ERR_OTHER;
2191 struct ldb_dn *old_dn, *new_dn;
2192 const char *rdn_name;
2193 const struct ldb_val *rdn_value, *new_rdn_value;
2195 struct ldb_context *ldb = ldb_module_get_ctx(module);
2196 struct dsdb_schema *schema = dsdb_get_schema(ldb);
2197 struct ldb_message *msg, *old_msg;
2198 struct ldb_message_element *el;
2199 TALLOC_CTX *tmp_ctx;
2200 struct ldb_result *res, *parent_res;
2201 const char *preserved_attrs[] = {
2202 /* yes, this really is a hard coded list. See MS-ADTS
2203 section 3.1.1.5.5.1.1 */
2204 "nTSecurityDescriptor", "attributeID", "attributeSyntax", "dNReferenceUpdate", "dNSHostName",
2205 "flatName", "governsID", "groupType", "instanceType", "lDAPDisplayName", "legacyExchangeDN",
2206 "isDeleted", "isRecycled", "lastKnownParent", "msDS-LastKnownRDN", "mS-DS-CreatorSID",
2207 "mSMQOwnerID", "nCName", "objectClass", "distinguishedName", "objectGUID", "objectSid",
2208 "oMSyntax", "proxiedObjectName", "name", "replPropertyMetaData", "sAMAccountName",
2209 "securityIdentifier", "sIDHistory", "subClassOf", "systemFlags", "trustPartner", "trustDirection",
2210 "trustType", "trustAttributes", "userAccountControl", "uSNChanged", "uSNCreated", "whenCreated",
2211 "whenChanged", NULL};
2212 uint32_t el_count = 0;
2215 if (ldb_dn_is_special(req->op.del.dn)) {
2216 return ldb_next_request(module, req);
2219 tmp_ctx = talloc_new(ldb);
2221 old_dn = ldb_dn_copy(tmp_ctx, req->op.del.dn);
2223 /* we need the complete msg off disk, so we can work out which
2224 attributes need to be removed */
2225 ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, NULL,
2226 DSDB_SEARCH_SHOW_DELETED |
2227 DSDB_SEARCH_REVEAL_INTERNALS |
2228 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT);
2229 if (ret != LDB_SUCCESS) {
2230 talloc_free(tmp_ctx);
2233 old_msg = res->msgs[0];
2235 if (ldb_msg_check_string_attribute(old_msg, "isDeleted", "TRUE")) {
2236 struct auth_session_info *session_info =
2237 (struct auth_session_info *)ldb_get_opaque(ldb, "sessionInfo");
2238 if (security_session_user_level(session_info) != SECURITY_SYSTEM) {
2239 ldb_asprintf_errstring(ldb, "Refusing to delete deleted object %s",
2240 ldb_dn_get_linearized(old_msg->dn));
2241 return LDB_ERR_UNWILLING_TO_PERFORM;
2244 /* it is already deleted - really remove it this time */
2245 talloc_free(tmp_ctx);
2246 return ldb_next_request(module, req);
2249 /* work out where we will be renaming this object to */
2250 ret = dsdb_get_deleted_objects_dn(ldb, tmp_ctx, old_dn, &new_dn);
2251 if (ret != LDB_SUCCESS) {
2252 /* this is probably an attempted delete on a partition
2253 * that doesn't allow delete operations, such as the
2254 * schema partition */
2255 ldb_asprintf_errstring(ldb, "No Deleted Objects container for DN %s",
2256 ldb_dn_get_linearized(old_dn));
2257 talloc_free(tmp_ctx);
2258 return LDB_ERR_UNWILLING_TO_PERFORM;
2261 rdn_name = ldb_dn_get_rdn_name(old_dn);
2262 rdn_value = ldb_dn_get_rdn_val(old_dn);
2264 /* get the objects GUID from the search we just did */
2265 guid = samdb_result_guid(old_msg, "objectGUID");
2267 /* Add a formatted child */
2268 retb = ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ADEL:%s",
2271 GUID_string(tmp_ctx, &guid));
2273 DEBUG(0,(__location__ ": Unable to add a formatted child to dn: %s",
2274 ldb_dn_get_linearized(new_dn)));
2275 talloc_free(tmp_ctx);
2276 return LDB_ERR_OPERATIONS_ERROR;
2280 now we need to modify the object in the following ways:
2282 - add isDeleted=TRUE
2283 - update rDN and name, with new rDN
2284 - remove linked attributes
2285 - remove objectCategory and sAMAccountType
2286 - remove attribs not on the preserved list
2287 - preserved if in above list, or is rDN
2288 - remove all linked attribs from this object
2289 - remove all links from other objects to this object
2290 - add lastKnownParent
2291 - update replPropertyMetaData?
2293 see MS-ADTS "Tombstone Requirements" section 3.1.1.5.5.1.1
2296 msg = ldb_msg_new(tmp_ctx);
2298 ldb_module_oom(module);
2299 talloc_free(tmp_ctx);
2300 return LDB_ERR_OPERATIONS_ERROR;
2305 ret = ldb_msg_add_string(msg, "isDeleted", "TRUE");
2306 if (ret != LDB_SUCCESS) {
2307 DEBUG(0,(__location__ ": Failed to add isDeleted string to the msg\n"));
2308 ldb_module_oom(module);
2309 talloc_free(tmp_ctx);
2312 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
2314 /* we also mark it as recycled, meaning this object can't be
2315 recovered (we are stripping its attributes) */
2316 if (dsdb_functional_level(ldb) >= DS_DOMAIN_FUNCTION_2008_R2) {
2317 ret = ldb_msg_add_string(msg, "isRecycled", "TRUE");
2318 if (ret != LDB_SUCCESS) {
2319 DEBUG(0,(__location__ ": Failed to add isRecycled string to the msg\n"));
2320 ldb_module_oom(module);
2321 talloc_free(tmp_ctx);
2324 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
2327 /* we need the storage form of the parent GUID */
2328 ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
2329 ldb_dn_get_parent(tmp_ctx, old_dn), NULL,
2330 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
2331 DSDB_SEARCH_REVEAL_INTERNALS);
2332 if (ret != LDB_SUCCESS) {
2333 talloc_free(tmp_ctx);
2337 ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
2338 ldb_dn_get_extended_linearized(tmp_ctx, parent_res->msgs[0]->dn, 1));
2339 if (ret != LDB_SUCCESS) {
2340 DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
2341 ldb_module_oom(module);
2342 talloc_free(tmp_ctx);
2345 msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
2347 /* work out which of the old attributes we will be removing */
2348 for (i=0; i<old_msg->num_elements; i++) {
2349 const struct dsdb_attribute *sa;
2350 el = &old_msg->elements[i];
2351 sa = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
2353 talloc_free(tmp_ctx);
2354 return LDB_ERR_OPERATIONS_ERROR;
2356 if (ldb_attr_cmp(el->name, rdn_name) == 0) {
2357 /* don't remove the rDN */
2361 if (sa->linkID && sa->linkID & 1) {
2362 ret = replmd_delete_remove_link(module, schema, old_dn, el, sa);
2363 if (ret != LDB_SUCCESS) {
2364 talloc_free(tmp_ctx);
2365 return LDB_ERR_OPERATIONS_ERROR;
2370 if (!sa->linkID && ldb_attr_in_list(preserved_attrs, el->name)) {
2374 ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
2375 if (ret != LDB_SUCCESS) {
2376 talloc_free(tmp_ctx);
2377 ldb_module_oom(module);
2382 /* work out what the new rdn value is, for updating the
2383 rDN and name fields */
2384 new_rdn_value = ldb_dn_get_rdn_val(new_dn);
2385 ret = ldb_msg_add_value(msg, rdn_name, new_rdn_value, &el);
2386 if (ret != LDB_SUCCESS) {
2387 talloc_free(tmp_ctx);
2390 el->flags = LDB_FLAG_MOD_REPLACE;
2392 el = ldb_msg_find_element(old_msg, "name");
2394 ret = ldb_msg_add_value(msg, "name", new_rdn_value, &el);
2395 if (ret != LDB_SUCCESS) {
2396 talloc_free(tmp_ctx);
2399 el->flags = LDB_FLAG_MOD_REPLACE;
2402 ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE);
2403 if (ret != LDB_SUCCESS) {
2404 ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
2405 ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
2406 talloc_free(tmp_ctx);
2410 /* now rename onto the new DN */
2411 ret = dsdb_module_rename(module, old_dn, new_dn, 0);
2412 if (ret != LDB_SUCCESS){
2413 DEBUG(0,(__location__ ": Failed to rename object from '%s' to '%s' - %s\n",
2414 ldb_dn_get_linearized(old_dn),
2415 ldb_dn_get_linearized(new_dn),
2416 ldb_errstring(ldb)));
2417 talloc_free(tmp_ctx);
2421 talloc_free(tmp_ctx);
2423 return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
2428 static int replmd_replicated_request_error(struct replmd_replicated_request *ar, int ret)
2433 static int replmd_replicated_request_werror(struct replmd_replicated_request *ar, WERROR status)
2435 int ret = LDB_ERR_OTHER;
2436 /* TODO: do some error mapping */
2440 static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
2442 struct ldb_context *ldb;
2443 struct ldb_request *change_req;
2444 enum ndr_err_code ndr_err;
2445 struct ldb_message *msg;
2446 struct replPropertyMetaDataBlob *md;
2447 struct ldb_val md_value;
2452 * TODO: check if the parent object exist
2456 * TODO: handle the conflict case where an object with the
2460 ldb = ldb_module_get_ctx(ar->module);
2461 msg = ar->objs->objects[ar->index_current].msg;
2462 md = ar->objs->objects[ar->index_current].meta_data;
2464 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
2465 if (ret != LDB_SUCCESS) {
2466 return replmd_replicated_request_error(ar, ret);
2469 ret = ldb_msg_add_value(msg, "objectGUID", &ar->objs->objects[ar->index_current].guid_value, NULL);
2470 if (ret != LDB_SUCCESS) {
2471 return replmd_replicated_request_error(ar, ret);
2474 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
2475 if (ret != LDB_SUCCESS) {
2476 return replmd_replicated_request_error(ar, ret);
2479 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNCreated", ar->seq_num);
2480 if (ret != LDB_SUCCESS) {
2481 return replmd_replicated_request_error(ar, ret);
2484 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
2485 if (ret != LDB_SUCCESS) {
2486 return replmd_replicated_request_error(ar, ret);
2489 /* remove any message elements that have zero values */
2490 for (i=0; i<msg->num_elements; i++) {
2491 struct ldb_message_element *el = &msg->elements[i];
2493 if (el->num_values == 0) {
2494 DEBUG(4,(__location__ ": Removing attribute %s with num_values==0\n",
2496 memmove(el, el+1, sizeof(*el)*(msg->num_elements - (i+1)));
2497 msg->num_elements--;
2504 * the meta data array is already sorted by the caller
2506 for (i=0; i < md->ctr.ctr1.count; i++) {
2507 md->ctr.ctr1.array[i].local_usn = ar->seq_num;
2509 ndr_err = ndr_push_struct_blob(&md_value, msg,
2510 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
2512 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
2513 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2514 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
2515 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
2517 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &md_value, NULL);
2518 if (ret != LDB_SUCCESS) {
2519 return replmd_replicated_request_error(ar, ret);
2522 replmd_ldb_message_sort(msg, ar->schema);
2525 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_ADD, msg);
2526 DEBUG(4, ("DRS replication add message:\n%s\n", s));
2530 ret = ldb_build_add_req(&change_req,
2538 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
2540 return ldb_next_request(ar->module, change_req);
2544 return true if an update is newer than an existing entry
2545 see section 5.11 of MS-ADTS
2547 static bool replmd_update_is_newer(const struct GUID *current_invocation_id,
2548 const struct GUID *update_invocation_id,
2549 uint32_t current_version,
2550 uint32_t update_version,
2551 NTTIME current_change_time,
2552 NTTIME update_change_time)
2554 if (update_version != current_version) {
2555 return update_version > current_version;
2557 if (update_change_time > current_change_time) {
2560 if (update_change_time == current_change_time) {
2561 return GUID_compare(update_invocation_id, current_invocation_id) > 0;
2566 static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *cur_m,
2567 struct replPropertyMetaData1 *new_m)
2569 return replmd_update_is_newer(&cur_m->originating_invocation_id,
2570 &new_m->originating_invocation_id,
2573 cur_m->originating_change_time,
2574 new_m->originating_change_time);
2577 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
2579 struct ldb_context *ldb;
2580 struct ldb_request *change_req;
2581 enum ndr_err_code ndr_err;
2582 struct ldb_message *msg;
2583 struct replPropertyMetaDataBlob *rmd;
2584 struct replPropertyMetaDataBlob omd;
2585 const struct ldb_val *omd_value;
2586 struct replPropertyMetaDataBlob nmd;
2587 struct ldb_val nmd_value;
2589 uint32_t removed_attrs = 0;
2592 ldb = ldb_module_get_ctx(ar->module);
2593 msg = ar->objs->objects[ar->index_current].msg;
2594 rmd = ar->objs->objects[ar->index_current].meta_data;
2599 * TODO: check repl data is correct after a rename
2601 if (ldb_dn_compare(msg->dn, ar->search_msg->dn) != 0) {
2602 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_request rename %s => %s\n",
2603 ldb_dn_get_linearized(ar->search_msg->dn),
2604 ldb_dn_get_linearized(msg->dn));
2605 if (dsdb_module_rename(ar->module,
2606 ar->search_msg->dn, msg->dn,
2607 DSDB_FLAG_OWN_MODULE) != LDB_SUCCESS) {
2608 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_replicated_request rename %s => %s failed - %s\n",
2609 ldb_dn_get_linearized(ar->search_msg->dn),
2610 ldb_dn_get_linearized(msg->dn),
2611 ldb_errstring(ldb));
2612 return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
2616 /* find existing meta data */
2617 omd_value = ldb_msg_find_ldb_val(ar->search_msg, "replPropertyMetaData");
2619 ndr_err = ndr_pull_struct_blob(omd_value, ar,
2620 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), &omd,
2621 (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
2622 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2623 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
2624 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
2627 if (omd.version != 1) {
2628 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
2634 nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
2635 nmd.ctr.ctr1.array = talloc_array(ar,
2636 struct replPropertyMetaData1,
2637 nmd.ctr.ctr1.count);
2638 if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOMEM);
2640 /* first copy the old meta data */
2641 for (i=0; i < omd.ctr.ctr1.count; i++) {
2642 nmd.ctr.ctr1.array[ni] = omd.ctr.ctr1.array[i];
2646 /* now merge in the new meta data */
2647 for (i=0; i < rmd->ctr.ctr1.count; i++) {
2650 for (j=0; j < ni; j++) {
2653 if (rmd->ctr.ctr1.array[i].attid != nmd.ctr.ctr1.array[j].attid) {
2657 cmp = replmd_replPropertyMetaData1_is_newer(&nmd.ctr.ctr1.array[j],
2658 &rmd->ctr.ctr1.array[i]);
2660 /* replace the entry */
2661 nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
2666 DEBUG(1,("Discarding older DRS attribute update to %s on %s from %s\n",
2667 msg->elements[i-removed_attrs].name,
2668 ldb_dn_get_linearized(msg->dn),
2669 GUID_string(ar, &rmd->ctr.ctr1.array[i].originating_invocation_id)));
2671 /* we don't want to apply this change so remove the attribute */
2672 ldb_msg_remove_element(msg, &msg->elements[i-removed_attrs]);
2679 if (found) continue;
2681 nmd.ctr.ctr1.array[ni] = rmd->ctr.ctr1.array[i];
2686 * finally correct the size of the meta_data array
2688 nmd.ctr.ctr1.count = ni;
2691 * the rdn attribute (the alias for the name attribute),
2692 * 'cn' for most objects is the last entry in the meta data array
2695 * sort the new meta data array
2697 ret = replmd_replPropertyMetaDataCtr1_sort(&nmd.ctr.ctr1, ar->schema, msg->dn);
2698 if (ret != LDB_SUCCESS) {
2703 * check if some replicated attributes left, otherwise skip the ldb_modify() call
2705 if (msg->num_elements == 0) {
2706 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: skip replace\n",
2709 ar->index_current++;
2710 return replmd_replicated_apply_next(ar);
2713 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_replicated_apply_merge[%u]: replace %u attributes\n",
2714 ar->index_current, msg->num_elements);
2716 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
2717 if (ret != LDB_SUCCESS) {
2718 return replmd_replicated_request_error(ar, ret);
2721 for (i=0; i<ni; i++) {
2722 nmd.ctr.ctr1.array[i].local_usn = ar->seq_num;
2725 /* create the meta data value */
2726 ndr_err = ndr_push_struct_blob(&nmd_value, msg,
2727 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
2729 (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
2730 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2731 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
2732 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
2736 * when we know that we'll modify the record, add the whenChanged, uSNChanged
2737 * and replPopertyMetaData attributes
2739 ret = ldb_msg_add_string(msg, "whenChanged", ar->objs->objects[ar->index_current].when_changed);
2740 if (ret != LDB_SUCCESS) {
2741 return replmd_replicated_request_error(ar, ret);
2743 ret = samdb_msg_add_uint64(ldb, msg, msg, "uSNChanged", ar->seq_num);
2744 if (ret != LDB_SUCCESS) {
2745 return replmd_replicated_request_error(ar, ret);
2747 ret = ldb_msg_add_value(msg, "replPropertyMetaData", &nmd_value, NULL);
2748 if (ret != LDB_SUCCESS) {
2749 return replmd_replicated_request_error(ar, ret);
2752 replmd_ldb_message_sort(msg, ar->schema);
2754 /* we want to replace the old values */
2755 for (i=0; i < msg->num_elements; i++) {
2756 msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
2760 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
2761 DEBUG(4, ("DRS replication modify message:\n%s\n", s));
2765 ret = ldb_build_mod_req(&change_req,
2773 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
2775 return ldb_next_request(ar->module, change_req);
2778 static int replmd_replicated_apply_search_callback(struct ldb_request *req,
2779 struct ldb_reply *ares)
2781 struct replmd_replicated_request *ar = talloc_get_type(req->context,
2782 struct replmd_replicated_request);
2786 return ldb_module_done(ar->req, NULL, NULL,
2787 LDB_ERR_OPERATIONS_ERROR);
2789 if (ares->error != LDB_SUCCESS &&
2790 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
2791 return ldb_module_done(ar->req, ares->controls,
2792 ares->response, ares->error);
2795 switch (ares->type) {
2796 case LDB_REPLY_ENTRY:
2797 ar->search_msg = talloc_steal(ar, ares->message);
2800 case LDB_REPLY_REFERRAL:
2801 /* we ignore referrals */
2804 case LDB_REPLY_DONE:
2805 if (ar->search_msg != NULL) {
2806 ret = replmd_replicated_apply_merge(ar);
2808 ret = replmd_replicated_apply_add(ar);
2810 if (ret != LDB_SUCCESS) {
2811 return ldb_module_done(ar->req, NULL, NULL, ret);
2819 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar);
2821 static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
2823 struct ldb_context *ldb;
2827 struct ldb_request *search_req;
2828 struct ldb_search_options_control *options;
2830 if (ar->index_current >= ar->objs->num_objects) {
2831 /* done with it, go to next stage */
2832 return replmd_replicated_uptodate_vector(ar);
2835 ldb = ldb_module_get_ctx(ar->module);
2836 ar->search_msg = NULL;
2838 tmp_str = ldb_binary_encode(ar, ar->objs->objects[ar->index_current].guid_value);
2839 if (!tmp_str) return replmd_replicated_request_werror(ar, WERR_NOMEM);
2841 filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
2842 if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
2843 talloc_free(tmp_str);
2845 ret = ldb_build_search_req(&search_req,
2854 replmd_replicated_apply_search_callback,
2857 ret = ldb_request_add_control(search_req, LDB_CONTROL_SHOW_DELETED_OID, true, NULL);
2858 if (ret != LDB_SUCCESS) {
2862 /* we need to cope with cross-partition links, so search for
2863 the GUID over all partitions */
2864 options = talloc(search_req, struct ldb_search_options_control);
2865 if (options == NULL) {
2866 DEBUG(0, (__location__ ": out of memory\n"));
2867 return LDB_ERR_OPERATIONS_ERROR;
2869 options->search_options = LDB_SEARCH_OPTION_PHANTOM_ROOT;
2871 ret = ldb_request_add_control(search_req,
2872 LDB_CONTROL_SEARCH_OPTIONS_OID,
2874 if (ret != LDB_SUCCESS) {
2878 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
2880 return ldb_next_request(ar->module, search_req);
2883 static int replmd_replicated_uptodate_modify_callback(struct ldb_request *req,
2884 struct ldb_reply *ares)
2886 struct ldb_context *ldb;
2887 struct replmd_replicated_request *ar = talloc_get_type(req->context,
2888 struct replmd_replicated_request);
2889 ldb = ldb_module_get_ctx(ar->module);
2892 return ldb_module_done(ar->req, NULL, NULL,
2893 LDB_ERR_OPERATIONS_ERROR);
2895 if (ares->error != LDB_SUCCESS) {
2896 return ldb_module_done(ar->req, ares->controls,
2897 ares->response, ares->error);
2900 if (ares->type != LDB_REPLY_DONE) {
2901 ldb_set_errstring(ldb, "Invalid reply type\n!");
2902 return ldb_module_done(ar->req, NULL, NULL,
2903 LDB_ERR_OPERATIONS_ERROR);
2908 return ldb_module_done(ar->req, NULL, NULL, LDB_SUCCESS);
2911 static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *ar)
2913 struct ldb_context *ldb;
2914 struct ldb_request *change_req;
2915 enum ndr_err_code ndr_err;
2916 struct ldb_message *msg;
2917 struct replUpToDateVectorBlob ouv;
2918 const struct ldb_val *ouv_value;
2919 const struct drsuapi_DsReplicaCursor2CtrEx *ruv;
2920 struct replUpToDateVectorBlob nuv;
2921 struct ldb_val nuv_value;
2922 struct ldb_message_element *nuv_el = NULL;
2923 const struct GUID *our_invocation_id;
2924 struct ldb_message_element *orf_el = NULL;
2925 struct repsFromToBlob nrf;
2926 struct ldb_val *nrf_value = NULL;
2927 struct ldb_message_element *nrf_el = NULL;
2930 time_t t = time(NULL);
2934 ldb = ldb_module_get_ctx(ar->module);
2935 ruv = ar->objs->uptodateness_vector;
2941 unix_to_nt_time(&now, t);
2944 * first create the new replUpToDateVector
2946 ouv_value = ldb_msg_find_ldb_val(ar->search_msg, "replUpToDateVector");
2948 ndr_err = ndr_pull_struct_blob(ouv_value, ar,
2949 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), &ouv,
2950 (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
2951 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
2952 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
2953 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
2956 if (ouv.version != 2) {
2957 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
2962 * the new uptodateness vector will at least
2963 * contain 1 entry, one for the source_dsa
2965 * plus optional values from our old vector and the one from the source_dsa
2967 nuv.ctr.ctr2.count = 1 + ouv.ctr.ctr2.count;
2968 if (ruv) nuv.ctr.ctr2.count += ruv->count;
2969 nuv.ctr.ctr2.cursors = talloc_array(ar,
2970 struct drsuapi_DsReplicaCursor2,
2971 nuv.ctr.ctr2.count);
2972 if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOMEM);
2974 /* first copy the old vector */
2975 for (i=0; i < ouv.ctr.ctr2.count; i++) {
2976 nuv.ctr.ctr2.cursors[ni] = ouv.ctr.ctr2.cursors[i];
2980 /* get our invocation_id if we have one already attached to the ldb */
2981 our_invocation_id = samdb_ntds_invocation_id(ldb);
2983 /* merge in the source_dsa vector is available */
2984 for (i=0; (ruv && i < ruv->count); i++) {
2987 if (our_invocation_id &&
2988 GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
2989 our_invocation_id)) {
2993 for (j=0; j < ni; j++) {
2994 if (!GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
2995 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
3002 * we update only the highest_usn and not the latest_sync_success time,
3003 * because the last success stands for direct replication
3005 if (ruv->cursors[i].highest_usn > nuv.ctr.ctr2.cursors[j].highest_usn) {
3006 nuv.ctr.ctr2.cursors[j].highest_usn = ruv->cursors[i].highest_usn;
3011 if (found) continue;
3013 /* if it's not there yet, add it */
3014 nuv.ctr.ctr2.cursors[ni] = ruv->cursors[i];
3019 * merge in the current highwatermark for the source_dsa
3022 for (j=0; j < ni; j++) {
3023 if (!GUID_equal(&ar->objs->source_dsa->source_dsa_invocation_id,
3024 &nuv.ctr.ctr2.cursors[j].source_dsa_invocation_id)) {
3031 * here we update the highest_usn and last_sync_success time
3032 * because we're directly replicating from the source_dsa
3034 * and use the tmp_highest_usn because this is what we have just applied
3037 nuv.ctr.ctr2.cursors[j].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
3038 nuv.ctr.ctr2.cursors[j].last_sync_success = now;
3043 * here we update the highest_usn and last_sync_success time
3044 * because we're directly replicating from the source_dsa
3046 * and use the tmp_highest_usn because this is what we have just applied
3049 nuv.ctr.ctr2.cursors[ni].source_dsa_invocation_id= ar->objs->source_dsa->source_dsa_invocation_id;
3050 nuv.ctr.ctr2.cursors[ni].highest_usn = ar->objs->source_dsa->highwatermark.tmp_highest_usn;
3051 nuv.ctr.ctr2.cursors[ni].last_sync_success = now;
3056 * finally correct the size of the cursors array
3058 nuv.ctr.ctr2.count = ni;
3063 qsort(nuv.ctr.ctr2.cursors, nuv.ctr.ctr2.count,
3064 sizeof(struct drsuapi_DsReplicaCursor2),
3065 (comparison_fn_t)drsuapi_DsReplicaCursor2_compare);
3068 * create the change ldb_message
3070 msg = ldb_msg_new(ar);
3071 if (!msg) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3072 msg->dn = ar->search_msg->dn;
3074 ndr_err = ndr_push_struct_blob(&nuv_value, msg,
3075 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
3077 (ndr_push_flags_fn_t)ndr_push_replUpToDateVectorBlob);
3078 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3079 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3080 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3082 ret = ldb_msg_add_value(msg, "replUpToDateVector", &nuv_value, &nuv_el);
3083 if (ret != LDB_SUCCESS) {
3084 return replmd_replicated_request_error(ar, ret);
3086 nuv_el->flags = LDB_FLAG_MOD_REPLACE;
3089 * now create the new repsFrom value from the given repsFromTo1 structure
3093 nrf.ctr.ctr1 = *ar->objs->source_dsa;
3094 /* and fix some values... */
3095 nrf.ctr.ctr1.consecutive_sync_failures = 0;
3096 nrf.ctr.ctr1.last_success = now;
3097 nrf.ctr.ctr1.last_attempt = now;
3098 nrf.ctr.ctr1.result_last_attempt = WERR_OK;
3099 nrf.ctr.ctr1.highwatermark.highest_usn = nrf.ctr.ctr1.highwatermark.tmp_highest_usn;
3102 * first see if we already have a repsFrom value for the current source dsa
3103 * if so we'll later replace this value
3105 orf_el = ldb_msg_find_element(ar->search_msg, "repsFrom");
3107 for (i=0; i < orf_el->num_values; i++) {
3108 struct repsFromToBlob *trf;
3110 trf = talloc(ar, struct repsFromToBlob);
3111 if (!trf) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3113 ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")), trf,
3114 (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
3115 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3116 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3117 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3120 if (trf->version != 1) {
3121 return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
3125 * we compare the source dsa objectGUID not the invocation_id
3126 * because we want only one repsFrom value per source dsa
3127 * and when the invocation_id of the source dsa has changed we don't need
3128 * the old repsFrom with the old invocation_id
3130 if (!GUID_equal(&trf->ctr.ctr1.source_dsa_obj_guid,
3131 &ar->objs->source_dsa->source_dsa_obj_guid)) {
3137 nrf_value = &orf_el->values[i];
3142 * copy over all old values to the new ldb_message
3144 ret = ldb_msg_add_empty(msg, "repsFrom", 0, &nrf_el);
3145 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3150 * if we haven't found an old repsFrom value for the current source dsa
3151 * we'll add a new value
3154 struct ldb_val zero_value;
3155 ZERO_STRUCT(zero_value);
3156 ret = ldb_msg_add_value(msg, "repsFrom", &zero_value, &nrf_el);
3157 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3159 nrf_value = &nrf_el->values[nrf_el->num_values - 1];
3162 /* we now fill the value which is already attached to ldb_message */
3163 ndr_err = ndr_push_struct_blob(nrf_value, msg,
3164 lp_iconv_convenience(ldb_get_opaque(ldb, "loadparm")),
3166 (ndr_push_flags_fn_t)ndr_push_repsFromToBlob);
3167 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
3168 NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
3169 return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
3173 * the ldb_message_element for the attribute, has all the old values and the new one
3174 * so we'll replace the whole attribute with all values
3176 nrf_el->flags = LDB_FLAG_MOD_REPLACE;
3179 char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
3180 DEBUG(4, ("DRS replication uptodate modify message:\n%s\n", s));
3184 /* prepare the ldb_modify() request */
3185 ret = ldb_build_mod_req(&change_req,
3191 replmd_replicated_uptodate_modify_callback,
3193 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3195 return ldb_next_request(ar->module, change_req);
3198 static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
3199 struct ldb_reply *ares)
3201 struct replmd_replicated_request *ar = talloc_get_type(req->context,
3202 struct replmd_replicated_request);
3206 return ldb_module_done(ar->req, NULL, NULL,
3207 LDB_ERR_OPERATIONS_ERROR);
3209 if (ares->error != LDB_SUCCESS &&
3210 ares->error != LDB_ERR_NO_SUCH_OBJECT) {
3211 return ldb_module_done(ar->req, ares->controls,
3212 ares->response, ares->error);
3215 switch (ares->type) {
3216 case LDB_REPLY_ENTRY:
3217 ar->search_msg = talloc_steal(ar, ares->message);
3220 case LDB_REPLY_REFERRAL:
3221 /* we ignore referrals */
3224 case LDB_REPLY_DONE:
3225 if (ar->search_msg == NULL) {
3226 ret = replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
3228 ret = replmd_replicated_uptodate_modify(ar);
3230 if (ret != LDB_SUCCESS) {
3231 return ldb_module_done(ar->req, NULL, NULL, ret);
3240 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
3242 struct ldb_context *ldb;
3244 static const char *attrs[] = {
3245 "replUpToDateVector",
3249 struct ldb_request *search_req;
3251 ldb = ldb_module_get_ctx(ar->module);
3252 ar->search_msg = NULL;
3254 ret = ldb_build_search_req(&search_req,
3257 ar->objs->partition_dn,
3263 replmd_replicated_uptodate_search_callback,
3265 if (ret != LDB_SUCCESS) return replmd_replicated_request_error(ar, ret);
3267 return ldb_next_request(ar->module, search_req);
3272 static int replmd_extended_replicated_objects(struct ldb_module *module, struct ldb_request *req)
3274 struct ldb_context *ldb;
3275 struct dsdb_extended_replicated_objects *objs;
3276 struct replmd_replicated_request *ar;
3277 struct ldb_control **ctrls;
3279 struct replmd_private *replmd_private =
3280 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3282 ldb = ldb_module_get_ctx(module);
3284 ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_extended_replicated_objects\n");
3286 objs = talloc_get_type(req->op.extended.data, struct dsdb_extended_replicated_objects);
3288 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: invalid extended data\n");
3289 return LDB_ERR_PROTOCOL_ERROR;
3292 if (objs->version != DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION) {
3293 ldb_debug(ldb, LDB_DEBUG_FATAL, "replmd_extended_replicated_objects: extended data invalid version [%u != %u]\n",
3294 objs->version, DSDB_EXTENDED_REPLICATED_OBJECTS_VERSION);
3295 return LDB_ERR_PROTOCOL_ERROR;
3298 ar = replmd_ctx_init(module, req);
3300 return LDB_ERR_OPERATIONS_ERROR;
3302 /* Set the flags to have the replmd_op_callback run over the full set of objects */
3303 ar->apply_mode = true;
3305 ar->schema = dsdb_get_schema(ldb);
3307 ldb_debug_set(ldb, LDB_DEBUG_FATAL, "replmd_ctx_init: no loaded schema found\n");
3309 DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
3310 return LDB_ERR_CONSTRAINT_VIOLATION;
3313 ctrls = req->controls;
3315 if (req->controls) {
3316 req->controls = talloc_memdup(ar, req->controls,
3317 talloc_get_size(req->controls));
3318 if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOMEM);
3321 ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, NULL);
3322 if (ret != LDB_SUCCESS) {
3326 ar->controls = req->controls;
3327 req->controls = ctrls;
3329 DEBUG(4,("linked_attributes_count=%u\n", objs->linked_attributes_count));
3331 /* save away the linked attributes for the end of the
3333 for (i=0; i<ar->objs->linked_attributes_count; i++) {
3334 struct la_entry *la_entry;
3336 if (replmd_private->la_ctx == NULL) {
3337 replmd_private->la_ctx = talloc_new(replmd_private);
3339 la_entry = talloc(replmd_private->la_ctx, struct la_entry);
3340 if (la_entry == NULL) {
3342 return LDB_ERR_OPERATIONS_ERROR;
3344 la_entry->la = talloc(la_entry, struct drsuapi_DsReplicaLinkedAttribute);
3345 if (la_entry->la == NULL) {
3346 talloc_free(la_entry);
3348 return LDB_ERR_OPERATIONS_ERROR;
3350 *la_entry->la = ar->objs->linked_attributes[i];
3352 /* we need to steal the non-scalars so they stay
3353 around until the end of the transaction */
3354 talloc_steal(la_entry->la, la_entry->la->identifier);
3355 talloc_steal(la_entry->la, la_entry->la->value.blob);
3357 DLIST_ADD(replmd_private->la_list, la_entry);
3360 return replmd_replicated_apply_next(ar);
3364 process one linked attribute structure
3366 static int replmd_process_linked_attribute(struct ldb_module *module,
3367 struct la_entry *la_entry)
3369 struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
3370 struct ldb_context *ldb = ldb_module_get_ctx(module);
3371 struct dsdb_schema *schema = dsdb_get_schema(ldb);
3372 struct ldb_message *msg;
3373 TALLOC_CTX *tmp_ctx = talloc_new(la_entry);
3375 const struct dsdb_attribute *attr;
3376 struct dsdb_dn *dsdb_dn;
3377 uint64_t seq_num = 0;
3378 struct ldb_message_element *old_el;
3380 time_t t = time(NULL);
3381 struct ldb_result *res;
3382 const char *attrs[2];
3383 struct parsed_dn *pdn_list, *pdn;
3384 struct GUID guid = GUID_zero();
3386 bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
3387 const struct GUID *our_invocation_id;
3390 linked_attributes[0]:
3391 &objs->linked_attributes[i]: struct drsuapi_DsReplicaLinkedAttribute
3393 identifier: struct drsuapi_DsReplicaObjectIdentifier
3394 __ndr_size : 0x0000003a (58)
3395 __ndr_size_sid : 0x00000000 (0)
3396 guid : 8e95b6a9-13dd-4158-89db-3220a5be5cc7
3398 __ndr_size_dn : 0x00000000 (0)
3400 attid : DRSUAPI_ATTRIBUTE_member (0x1F)
3401 value: struct drsuapi_DsAttributeValue
3402 __ndr_size : 0x0000007e (126)
3404 blob : DATA_BLOB length=126
3405 flags : 0x00000001 (1)
3406 1: DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
3407 originating_add_time : Wed Sep 2 22:20:01 2009 EST
3408 meta_data: struct drsuapi_DsReplicaMetaData
3409 version : 0x00000015 (21)
3410 originating_change_time : Wed Sep 2 23:39:07 2009 EST
3411 originating_invocation_id: 794640f3-18cf-40ee-a211-a93992b67a64
3412 originating_usn : 0x000000000001e19c (123292)
3414 (for cases where the link is to a normal DN)
3415 &target: struct drsuapi_DsReplicaObjectIdentifier3
3416 __ndr_size : 0x0000007e (126)
3417 __ndr_size_sid : 0x0000001c (28)
3418 guid : 7639e594-db75-4086-b0d4-67890ae46031
3419 sid : S-1-5-21-2848215498-2472035911-1947525656-19924
3420 __ndr_size_dn : 0x00000022 (34)
3421 dn : 'CN=UOne,OU=TestOU,DC=vsofs8,DC=com'
3424 /* find the attribute being modified */
3425 attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
3427 DEBUG(0, (__location__ ": Unable to find attributeID 0x%x\n", la->attid));
3428 talloc_free(tmp_ctx);
3429 return LDB_ERR_OPERATIONS_ERROR;
3432 attrs[0] = attr->lDAPDisplayName;
3435 /* get the existing message from the db for the object with
3436 this GUID, returning attribute being modified. We will then
3437 use this msg as the basis for a modify call */
3438 ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE, attrs,
3439 DSDB_SEARCH_SEARCH_ALL_PARTITIONS |
3440 DSDB_SEARCH_SHOW_DELETED |
3441 DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
3442 DSDB_SEARCH_REVEAL_INTERNALS,
3443 "objectGUID=%s", GUID_string(tmp_ctx, &la->identifier->guid));
3444 if (ret != LDB_SUCCESS) {
3445 talloc_free(tmp_ctx);
3448 if (res->count != 1) {
3449 ldb_asprintf_errstring(ldb, "DRS linked attribute for GUID %s - DN not found",
3450 GUID_string(tmp_ctx, &la->identifier->guid));
3451 talloc_free(tmp_ctx);
3452 return LDB_ERR_NO_SUCH_OBJECT;
3456 if (msg->num_elements == 0) {
3457 ret = ldb_msg_add_empty(msg, attr->lDAPDisplayName, LDB_FLAG_MOD_REPLACE, &old_el);
3458 if (ret != LDB_SUCCESS) {
3459 ldb_module_oom(module);
3460 talloc_free(tmp_ctx);
3461 return LDB_ERR_OPERATIONS_ERROR;
3464 old_el = &msg->elements[0];
3465 old_el->flags = LDB_FLAG_MOD_REPLACE;
3468 /* parse the existing links */
3469 ret = get_parsed_dns(module, tmp_ctx, old_el, &pdn_list, attr->syntax->ldap_oid);
3470 if (ret != LDB_SUCCESS) {
3471 talloc_free(tmp_ctx);
3475 /* get our invocationId */
3476 our_invocation_id = samdb_ntds_invocation_id(ldb);
3477 if (!our_invocation_id) {
3478 ldb_debug_set(ldb, LDB_DEBUG_ERROR, __location__ ": unable to find invocationId\n");
3479 talloc_free(tmp_ctx);
3480 return LDB_ERR_OPERATIONS_ERROR;
3483 ret = replmd_check_upgrade_links(pdn_list, old_el->num_values, our_invocation_id);
3484 if (ret != LDB_SUCCESS) {
3485 talloc_free(tmp_ctx);
3489 status = dsdb_dn_la_from_blob(ldb, attr, schema, tmp_ctx, la->value.blob, &dsdb_dn);
3490 if (!W_ERROR_IS_OK(status)) {
3491 ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
3492 old_el->name, ldb_dn_get_linearized(msg->dn), win_errstr(status));
3493 return LDB_ERR_OPERATIONS_ERROR;
3496 ntstatus = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid, "GUID");
3497 if (!NT_STATUS_IS_OK(ntstatus) && active) {
3498 ldb_asprintf_errstring(ldb, "Failed to find GUID in linked attribute blob for %s on %s from %s",
3500 ldb_dn_get_linearized(dsdb_dn->dn),
3501 ldb_dn_get_linearized(msg->dn));
3502 return LDB_ERR_OPERATIONS_ERROR;
3505 /* re-resolve the DN by GUID, as the DRS server may give us an
3507 ret = dsdb_module_dn_by_guid(module, dsdb_dn, &guid, &dsdb_dn->dn);
3508 if (ret != LDB_SUCCESS) {
3509 ldb_asprintf_errstring(ldb, __location__ ": Failed to re-resolve GUID %s",
3510 GUID_string(tmp_ctx, &guid));
3511 talloc_free(tmp_ctx);
3515 /* see if this link already exists */
3516 pdn = parsed_dn_find(pdn_list, old_el->num_values, &guid, dsdb_dn->dn);
3518 /* see if this update is newer than what we have already */
3519 struct GUID invocation_id = GUID_zero();
3520 uint32_t version = 0;
3521 NTTIME change_time = 0;
3522 bool was_active = ldb_dn_get_extended_component(pdn->dsdb_dn->dn, "DELETED") == NULL;
3524 dsdb_get_extended_dn_guid(pdn->dsdb_dn->dn, &invocation_id, "RMD_INVOCID");
3525 dsdb_get_extended_dn_uint32(pdn->dsdb_dn->dn, &version, "RMD_VERSION");
3526 dsdb_get_extended_dn_nttime(pdn->dsdb_dn->dn, &change_time, "RMD_CHANGETIME");
3528 if (!replmd_update_is_newer(&invocation_id,
3529 &la->meta_data.originating_invocation_id,
3531 la->meta_data.version,
3533 la->meta_data.originating_change_time)) {
3534 DEBUG(1,("Discarding older DRS linked attribute update to %s on %s from %s\n",
3535 old_el->name, ldb_dn_get_linearized(msg->dn),
3536 GUID_string(tmp_ctx, &la->meta_data.originating_invocation_id)));
3537 talloc_free(tmp_ctx);
3541 /* get a seq_num for this change */
3542 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
3543 if (ret != LDB_SUCCESS) {
3544 talloc_free(tmp_ctx);
3549 /* remove the existing backlink */
3550 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, false, attr, false);
3551 if (ret != LDB_SUCCESS) {
3552 talloc_free(tmp_ctx);
3557 ret = replmd_update_la_val(tmp_ctx, pdn->v, dsdb_dn, pdn->dsdb_dn,
3558 &la->meta_data.originating_invocation_id,
3559 la->meta_data.originating_usn, seq_num,
3560 la->meta_data.originating_change_time,
3561 la->meta_data.version,
3563 if (ret != LDB_SUCCESS) {
3564 talloc_free(tmp_ctx);
3569 /* add the new backlink */
3570 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, true, attr, false);
3571 if (ret != LDB_SUCCESS) {
3572 talloc_free(tmp_ctx);
3577 /* get a seq_num for this change */
3578 ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
3579 if (ret != LDB_SUCCESS) {
3580 talloc_free(tmp_ctx);
3584 old_el->values = talloc_realloc(msg->elements, old_el->values,
3585 struct ldb_val, old_el->num_values+1);
3586 if (!old_el->values) {
3587 ldb_module_oom(module);
3588 return LDB_ERR_OPERATIONS_ERROR;
3590 old_el->num_values++;
3592 ret = replmd_build_la_val(tmp_ctx, &old_el->values[old_el->num_values-1], dsdb_dn,
3593 &la->meta_data.originating_invocation_id,
3594 la->meta_data.originating_usn, seq_num,
3595 la->meta_data.originating_change_time,
3596 la->meta_data.version,
3597 (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?false:true);
3598 if (ret != LDB_SUCCESS) {
3599 talloc_free(tmp_ctx);
3604 ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid,
3606 if (ret != LDB_SUCCESS) {
3607 talloc_free(tmp_ctx);
3613 /* we only change whenChanged and uSNChanged if the seq_num
3615 if (add_time_element(msg, "whenChanged", t) != LDB_SUCCESS) {
3616 talloc_free(tmp_ctx);
3617 return LDB_ERR_OPERATIONS_ERROR;
3620 if (add_uint64_element(msg, "uSNChanged", seq_num) != LDB_SUCCESS) {
3621 talloc_free(tmp_ctx);
3622 return LDB_ERR_OPERATIONS_ERROR;
3625 ret = dsdb_check_single_valued_link(attr, old_el);
3626 if (ret != LDB_SUCCESS) {
3627 talloc_free(tmp_ctx);
3631 ret = dsdb_module_modify(module, msg, DSDB_MODIFY_RELAX);
3632 if (ret != LDB_SUCCESS) {
3633 ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
3635 ldb_ldif_message_string(ldb, tmp_ctx, LDB_CHANGETYPE_MODIFY, msg));
3636 talloc_free(tmp_ctx);
3640 talloc_free(tmp_ctx);
3645 static int replmd_extended(struct ldb_module *module, struct ldb_request *req)
3647 if (strcmp(req->op.extended.oid, DSDB_EXTENDED_REPLICATED_OBJECTS_OID) == 0) {
3648 return replmd_extended_replicated_objects(module, req);
3651 return ldb_next_request(module, req);
3656 we hook into the transaction operations to allow us to
3657 perform the linked attribute updates at the end of the whole
3658 transaction. This allows a forward linked attribute to be created
3659 before the object is created. During a vampire, w2k8 sends us linked
3660 attributes before the objects they are part of.
3662 static int replmd_start_transaction(struct ldb_module *module)
3664 /* create our private structure for this transaction */
3665 struct replmd_private *replmd_private = talloc_get_type(ldb_module_get_private(module),
3666 struct replmd_private);
3667 replmd_txn_cleanup(replmd_private);
3669 /* free any leftover mod_usn records from cancelled
3671 while (replmd_private->ncs) {
3672 struct nc_entry *e = replmd_private->ncs;
3673 DLIST_REMOVE(replmd_private->ncs, e);
3677 return ldb_next_start_trans(module);
3681 on prepare commit we loop over our queued la_context structures and
3684 static int replmd_prepare_commit(struct ldb_module *module)
3686 struct replmd_private *replmd_private =
3687 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3688 struct la_entry *la, *prev;
3689 struct la_backlink *bl;
3692 /* walk the list backwards, to do the first entry first, as we
3693 * added the entries with DLIST_ADD() which puts them at the
3694 * start of the list */
3695 for (la = replmd_private->la_list; la && la->next; la=la->next) ;
3697 for (; la; la=prev) {
3699 DLIST_REMOVE(replmd_private->la_list, la);
3700 ret = replmd_process_linked_attribute(module, la);
3701 if (ret != LDB_SUCCESS) {
3702 replmd_txn_cleanup(replmd_private);
3707 /* process our backlink list, creating and deleting backlinks
3709 for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
3710 ret = replmd_process_backlink(module, bl);
3711 if (ret != LDB_SUCCESS) {
3712 replmd_txn_cleanup(replmd_private);
3717 replmd_txn_cleanup(replmd_private);
3719 /* possibly change @REPLCHANGED */
3720 ret = replmd_notify_store(module);
3721 if (ret != LDB_SUCCESS) {
3725 return ldb_next_prepare_commit(module);
3728 static int replmd_del_transaction(struct ldb_module *module)
3730 struct replmd_private *replmd_private =
3731 talloc_get_type(ldb_module_get_private(module), struct replmd_private);
3732 replmd_txn_cleanup(replmd_private);
3734 return ldb_next_del_trans(module);
3738 _PUBLIC_ const struct ldb_module_ops ldb_repl_meta_data_module_ops = {
3739 .name = "repl_meta_data",
3740 .init_context = replmd_init,
3742 .modify = replmd_modify,
3743 .rename = replmd_rename,
3744 .del = replmd_delete,
3745 .extended = replmd_extended,
3746 .start_transaction = replmd_start_transaction,
3747 .prepare_commit = replmd_prepare_commit,
3748 .del_transaction = replmd_del_transaction,