replmd: Ensure that binary blobs in links are ordered in the database
[nivanova/samba-autobuild/.git] / source4 / dsdb / samdb / ldb_modules / repl_meta_data.c
index b01c956d1c3249e1ac9fe19f4c2f212cde101916..9edcc33b08abb0ac164a0c240fc7b98d6b03b85c 100644 (file)
@@ -59,14 +59,15 @@ static const NTTIME DELETED_OBJECT_CONTAINER_CHANGE_TIME = 2650466015990000000UL
 struct replmd_private {
        TALLOC_CTX *la_ctx;
        struct la_entry *la_list;
-       TALLOC_CTX *bl_ctx;
-       struct la_backlink *la_backlinks;
        struct nc_entry {
                struct nc_entry *prev, *next;
                struct ldb_dn *dn;
                uint64_t mod_usn;
                uint64_t mod_usn_urgent;
        } *ncs;
+       struct ldb_dn *schema_dn;
+       bool originating_updates;
+       bool sorted_links;
 };
 
 struct la_entry {
@@ -79,16 +80,25 @@ struct replmd_replicated_request {
        struct ldb_request *req;
 
        const struct dsdb_schema *schema;
+       struct GUID our_invocation_id;
 
        /* the controls we pass down */
        struct ldb_control **controls;
 
+       /*
+        * Backlinks for the replmd_add() case (we want to create
+        * backlinks after creating the user, but before the end of
+        * the ADD request) 
+        */
+       struct la_backlink *la_backlinks;
+
        /* details for the mode where we apply a bunch of inbound replication meessages */
        bool apply_mode;
        uint32_t index_current;
        struct dsdb_extended_replicated_objects *objs;
 
        struct ldb_message *search_msg;
+       struct GUID local_parent_guid;
 
        uint64_t seq_num;
        bool is_urgent;
@@ -96,8 +106,18 @@ struct replmd_replicated_request {
        bool isDeleted;
 };
 
+struct parsed_dn {
+       struct dsdb_dn *dsdb_dn;
+       struct GUID guid;
+       struct ldb_val *v;
+};
+
 static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar);
 static int replmd_delete_internals(struct ldb_module *module, struct ldb_request *req, bool re_delete);
+static int replmd_check_upgrade_links(struct ldb_context *ldb,
+                                     struct parsed_dn *dns, uint32_t count,
+                                     struct ldb_message_element *el,
+                                     const char *ldap_oid);
 
 enum urgent_situation {
        REPL_URGENT_ON_CREATE = 1,
@@ -232,14 +252,37 @@ static int replmd_init(struct ldb_module *module)
 {
        struct replmd_private *replmd_private;
        struct ldb_context *ldb = ldb_module_get_ctx(module);
-
+       static const char *samba_dsdb_attrs[] = { SAMBA_COMPATIBLE_FEATURES_ATTR, NULL };
+       struct ldb_dn *samba_dsdb_dn;
+       struct ldb_result *res;
+       int ret;
+       TALLOC_CTX *frame = talloc_stackframe();
        replmd_private = talloc_zero(module, struct replmd_private);
        if (replmd_private == NULL) {
                ldb_oom(ldb);
+               TALLOC_FREE(frame);
                return LDB_ERR_OPERATIONS_ERROR;
        }
        ldb_module_set_private(module, replmd_private);
 
+       replmd_private->schema_dn = ldb_get_schema_basedn(ldb);
+
+       samba_dsdb_dn = ldb_dn_new(frame, ldb, "@SAMBA_DSDB");
+       if (!samba_dsdb_dn) {
+               TALLOC_FREE(frame);
+               return ldb_oom(ldb);
+       }
+
+       ret = dsdb_module_search_dn(module, frame, &res, samba_dsdb_dn,
+                                   samba_dsdb_attrs, DSDB_FLAG_NEXT_MODULE, NULL);
+       if (ret == LDB_SUCCESS) {
+               replmd_private->sorted_links
+                       = ldb_msg_check_string_attribute(res->msgs[0],
+                                                        SAMBA_COMPATIBLE_FEATURES_ATTR,
+                                                        SAMBA_SORTED_LINKS_FEATURE);
+       }
+       TALLOC_FREE(frame);
+
        return ldb_next_init(module);
 }
 
@@ -252,19 +295,66 @@ static void replmd_txn_cleanup(struct replmd_private *replmd_private)
        replmd_private->la_list = NULL;
        replmd_private->la_ctx = NULL;
 
-       talloc_free(replmd_private->bl_ctx);
-       replmd_private->la_backlinks = NULL;
-       replmd_private->bl_ctx = NULL;
 }
 
 
 struct la_backlink {
        struct la_backlink *next, *prev;
        const char *attr_name;
-       struct GUID forward_guid, target_guid;
+       struct ldb_dn *forward_dn;
+       struct GUID target_guid;
        bool active;
 };
 
+/*
+  a ldb_modify request operating on modules below the
+  current module
+ */
+static int linked_attr_modify(struct ldb_module *module,
+                             const struct ldb_message *message,
+                             struct ldb_request *parent)
+{
+       struct ldb_request *mod_req;
+       int ret;
+       struct ldb_context *ldb = ldb_module_get_ctx(module);
+       TALLOC_CTX *tmp_ctx = talloc_new(module);
+       struct ldb_result *res;
+
+       res = talloc_zero(tmp_ctx, struct ldb_result);
+       if (!res) {
+               talloc_free(tmp_ctx);
+               return ldb_oom(ldb_module_get_ctx(module));
+       }
+
+       ret = ldb_build_mod_req(&mod_req, ldb, tmp_ctx,
+                               message,
+                               NULL,
+                               res,
+                               ldb_modify_default_callback,
+                               parent);
+       LDB_REQ_SET_LOCATION(mod_req);
+       if (ret != LDB_SUCCESS) {
+               talloc_free(tmp_ctx);
+               return ret;
+       }
+
+       ret = ldb_request_add_control(mod_req, DSDB_CONTROL_REPLICATED_UPDATE_OID,
+                                     false, NULL);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
+
+       /* Run the new request */
+       ret = ldb_next_request(module, mod_req);
+
+       if (ret == LDB_SUCCESS) {
+               ret = ldb_wait(mod_req->handle, LDB_WAIT_ALL);
+       }
+
+       talloc_free(tmp_ctx);
+       return ret;
+}
+
 /*
   process a backlinks we accumulated during a transaction, adding and
   deleting the backlinks from the target objects
@@ -275,7 +365,7 @@ static int replmd_process_backlink(struct ldb_module *module, struct la_backlink
        int ret;
        struct ldb_context *ldb = ldb_module_get_ctx(module);
        struct ldb_message *msg;
-       TALLOC_CTX *tmp_ctx = talloc_new(bl);
+       TALLOC_CTX *frame = talloc_stackframe();
        char *dn_string;
 
        /*
@@ -284,39 +374,44 @@ static int replmd_process_backlink(struct ldb_module *module, struct la_backlink
          - construct ldb_message
               - either an add or a delete
         */
-       ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->target_guid, &target_dn, parent);
+       ret = dsdb_module_dn_by_guid(module, frame, &bl->target_guid, &target_dn, parent);
        if (ret != LDB_SUCCESS) {
+               struct GUID_txt_buf guid_str;
                DEBUG(2,(__location__ ": WARNING: Failed to find target DN for linked attribute with GUID %s\n",
-                        GUID_string(bl, &bl->target_guid)));
+                        GUID_buf_string(&bl->target_guid, &guid_str)));
+               talloc_free(frame);
                return LDB_SUCCESS;
        }
 
-       ret = dsdb_module_dn_by_guid(module, tmp_ctx, &bl->forward_guid, &source_dn, parent);
-       if (ret != LDB_SUCCESS) {
-               ldb_asprintf_errstring(ldb, "Failed to find source DN for linked attribute with GUID %s\n",
-                                      GUID_string(bl, &bl->forward_guid));
-               talloc_free(tmp_ctx);
-               return ret;
+       msg = ldb_msg_new(frame);
+       if (msg == NULL) {
+               ldb_module_oom(module);
+               talloc_free(frame);
+               return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       msg = ldb_msg_new(tmp_ctx);
-       if (msg == NULL) {
+       source_dn = ldb_dn_copy(frame, bl->forward_dn);
+       if (!source_dn) {
                ldb_module_oom(module);
-               talloc_free(tmp_ctx);
+               talloc_free(frame);
                return LDB_ERR_OPERATIONS_ERROR;
+       } else {
+               /* Filter down to the attributes we want in the backlink */
+               const char *accept[] = { "GUID", "SID", NULL };
+               ldb_dn_extended_filter(source_dn, accept);
        }
 
        /* construct a ldb_message for adding/deleting the backlink */
        msg->dn = target_dn;
-       dn_string = ldb_dn_get_extended_linearized(tmp_ctx, source_dn, 1);
+       dn_string = ldb_dn_get_extended_linearized(frame, bl->forward_dn, 1);
        if (!dn_string) {
                ldb_module_oom(module);
-               talloc_free(tmp_ctx);
+               talloc_free(frame);
                return LDB_ERR_OPERATIONS_ERROR;
        }
        ret = ldb_msg_add_steal_string(msg, bl->attr_name, dn_string);
        if (ret != LDB_SUCCESS) {
-               talloc_free(tmp_ctx);
+               talloc_free(frame);
                return ret;
        }
        msg->elements[0].flags = bl->active?LDB_FLAG_MOD_ADD:LDB_FLAG_MOD_DELETE;
@@ -344,25 +439,36 @@ static int replmd_process_backlink(struct ldb_module *module, struct la_backlink
                                       ldb_dn_get_linearized(source_dn),
                                       ldb_dn_get_linearized(target_dn),
                                       ldb_errstring(ldb));
-               talloc_free(tmp_ctx);
+               talloc_free(frame);
                return ret;
        }
-       talloc_free(tmp_ctx);
+       talloc_free(frame);
        return ret;
 }
 
 /*
   add a backlink to the list of backlinks to add/delete in the prepare
   commit
+
+  forward_dn is stolen onto the defereed context
  */
-static int replmd_add_backlink(struct ldb_module *module, const struct dsdb_schema *schema,
-                              struct GUID *forward_guid, struct GUID *target_guid,
-                              bool active, const struct dsdb_attribute *schema_attr, bool immediate)
+static int replmd_defer_add_backlink(struct ldb_module *module,
+                                    struct replmd_private *replmd_private,
+                                    const struct dsdb_schema *schema,
+                                    struct replmd_replicated_request *ac,
+                                    struct ldb_dn *forward_dn,
+                                    struct GUID *target_guid, bool active,
+                                    const struct dsdb_attribute *schema_attr,
+                                    struct ldb_request *parent)
 {
        const struct dsdb_attribute *target_attr;
        struct la_backlink *bl;
-       struct replmd_private *replmd_private =
-               talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
+       
+       bl = talloc(ac, struct la_backlink);
+       if (bl == NULL) {
+               ldb_module_oom(module);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
 
        target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
        if (!target_attr) {
@@ -375,64 +481,50 @@ static int replmd_add_backlink(struct ldb_module *module, const struct dsdb_sche
                return LDB_SUCCESS;
        }
 
-       /* see if its already in the list */
-       for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
-               if (GUID_equal(forward_guid, &bl->forward_guid) &&
-                   GUID_equal(target_guid, &bl->target_guid) &&
-                   (target_attr->lDAPDisplayName == bl->attr_name ||
-                    strcmp(target_attr->lDAPDisplayName, bl->attr_name) == 0)) {
-                       break;
-               }
-       }
-
-       if (bl) {
-               /* we found an existing one */
-               if (bl->active == active) {
-                       return LDB_SUCCESS;
-               }
-               DLIST_REMOVE(replmd_private->la_backlinks, bl);
-               talloc_free(bl);
-               return LDB_SUCCESS;
-       }
-
-       if (replmd_private->bl_ctx == NULL) {
-               replmd_private->bl_ctx = talloc_new(replmd_private);
-               if (replmd_private->bl_ctx == NULL) {
-                       ldb_module_oom(module);
-                       return LDB_ERR_OPERATIONS_ERROR;
-               }
-       }
-
-       /* its a new one */
-       bl = talloc(replmd_private->bl_ctx, struct la_backlink);
-       if (bl == NULL) {
-               ldb_module_oom(module);
-               return LDB_ERR_OPERATIONS_ERROR;
-       }
-
-       /* Ensure the schema does not go away before the bl->attr_name is used */
-       if (!talloc_reference(bl, schema)) {
-               talloc_free(bl);
-               ldb_module_oom(module);
-               return LDB_ERR_OPERATIONS_ERROR;
-       }
-
        bl->attr_name = target_attr->lDAPDisplayName;
-       bl->forward_guid = *forward_guid;
+       bl->forward_dn = talloc_steal(bl, forward_dn);
        bl->target_guid = *target_guid;
        bl->active = active;
 
-       /* the caller may ask for this backlink to be processed
-          immediately */
-       if (immediate) {
-               int ret = replmd_process_backlink(module, bl, NULL);
-               talloc_free(bl);
-               return ret;
+       DLIST_ADD(ac->la_backlinks, bl);
+
+       return LDB_SUCCESS;
+}
+
+/*
+  add a backlink to the list of backlinks to add/delete in the prepare
+  commit
+ */
+static int replmd_add_backlink(struct ldb_module *module,
+                              struct replmd_private *replmd_private,
+                              const struct dsdb_schema *schema,
+                              struct ldb_dn *forward_dn,
+                              struct GUID *target_guid, bool active,
+                              const struct dsdb_attribute *schema_attr,
+                              struct ldb_request *parent)
+{
+       const struct dsdb_attribute *target_attr;
+       struct la_backlink bl;
+       int ret;
+       
+       target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
+       if (!target_attr) {
+               /*
+                * windows 2003 has a broken schema where the
+                * definition of msDS-IsDomainFor is missing (which is
+                * supposed to be the backlink of the
+                * msDS-HasDomainNCs attribute
+                */
+               return LDB_SUCCESS;
        }
 
-       DLIST_ADD(replmd_private->la_backlinks, bl);
+       bl.attr_name = target_attr->lDAPDisplayName;
+       bl.forward_dn = forward_dn;
+       bl.target_guid = *target_guid;
+       bl.active = active;
 
-       return LDB_SUCCESS;
+       ret = replmd_process_backlink(module, &bl, parent);
+       return ret;
 }
 
 
@@ -481,6 +573,23 @@ static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
                                       NULL, LDB_ERR_OPERATIONS_ERROR);
        }
 
+       if (ac->apply_mode == false) {
+               struct la_backlink *bl;
+               /*
+                * process our backlink list after an replmd_add(),
+                * creating and deleting backlinks as necessary (this
+                * code is sync).  The other cases are handled inline
+                * with the modify.
+                */
+               for (bl=ac->la_backlinks; bl; bl=bl->next) {
+                       ret = replmd_process_backlink(ac->module, bl, ac->req);
+                       if (ret != LDB_SUCCESS) {
+                               return ldb_module_done(ac->req, NULL,
+                                                      NULL, ret);
+                       }
+               }
+       }
+       
        if (!partition_ctrl) {
                ldb_set_errstring(ldb_module_get_ctx(ac->module),"No partition control on reply");
                return ldb_module_done(ac->req, NULL,
@@ -520,6 +629,9 @@ static int replmd_op_callback(struct ldb_request *req, struct ldb_reply *ares)
                                modified_partition->mod_usn_urgent = ac->seq_num;
                        }
                }
+               if (!ac->apply_mode) {
+                       replmd_private->originating_updates = true;
+               }
        }
 
        if (ac->apply_mode) {
@@ -560,6 +672,23 @@ static int replmd_notify_store(struct ldb_module *module, struct ldb_request *pa
                                 ldb_dn_get_linearized(modified_partition->dn)));
                        return ret;
                }
+
+               if (ldb_dn_compare(modified_partition->dn,
+                                  replmd_private->schema_dn) == 0) {
+                       struct ldb_result *ext_res;
+                       ret = dsdb_module_extended(module,
+                                                  replmd_private->schema_dn,
+                                                  &ext_res,
+                                                  DSDB_EXTENDED_SCHEMA_UPDATE_NOW_OID,
+                                                  ext_res,
+                                                  DSDB_FLAG_NEXT_MODULE,
+                                                  parent);
+                       if (ret != LDB_SUCCESS) {
+                               return ret;
+                       }
+                       talloc_free(ext_res);
+               }
+
                DLIST_REMOVE(replmd_private->ncs, modified_partition);
                talloc_free(modified_partition);
        }
@@ -576,6 +705,7 @@ static struct replmd_replicated_request *replmd_ctx_init(struct ldb_module *modu
 {
        struct ldb_context *ldb;
        struct replmd_replicated_request *ac;
+       const struct GUID *our_invocation_id;
 
        ldb = ldb_module_get_ctx(module);
 
@@ -593,8 +723,19 @@ static struct replmd_replicated_request *replmd_ctx_init(struct ldb_module *modu
                ldb_debug_set(ldb, LDB_DEBUG_FATAL,
                              "replmd_modify: no dsdb_schema loaded");
                DEBUG(0,(__location__ ": %s\n", ldb_errstring(ldb)));
+               talloc_free(ac);
+               return NULL;
+       }
+
+       /* get our invocationId */
+       our_invocation_id = samdb_ntds_invocation_id(ldb);
+       if (!our_invocation_id) {
+               ldb_debug_set(ldb, LDB_DEBUG_FATAL,
+                             "replmd_add: unable to find invocationId\n");
+               talloc_free(ac);
                return NULL;
        }
+       ac->our_invocation_id = *our_invocation_id;
 
        return ac;
 }
@@ -672,24 +813,6 @@ static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMeta
                return 0;
        }
 
-       /*
-        * the rdn attribute should be at the end!
-        * so we need to return a value greater than zero
-        * which means m1 is greater than m2
-        */
-       if (attid_1 == *rdn_attid) {
-               return 1;
-       }
-
-       /*
-        * the rdn attribute should be at the end!
-        * so we need to return a value less than zero
-        * which means m2 is greater than m1
-        */
-       if (attid_2 == *rdn_attid) {
-               return -1;
-       }
-
        /*
         * See above regarding this being an unsigned comparison.
         * Otherwise when the high bit is set on non-standard
@@ -701,7 +824,6 @@ static int replmd_replPropertyMetaData1_attid_sort(const struct replPropertyMeta
 
 static int replmd_replPropertyMetaDataCtr1_verify(struct ldb_context *ldb,
                                                  struct replPropertyMetaDataCtr1 *ctr1,
-                                                 const struct dsdb_attribute *rdn_sa,
                                                  struct ldb_dn *dn)
 {
        if (ctr1->count == 0) {
@@ -710,12 +832,6 @@ static int replmd_replPropertyMetaDataCtr1_verify(struct ldb_context *ldb,
                              ldb_dn_get_linearized(dn));
                return LDB_ERR_CONSTRAINT_VIOLATION;
        }
-       if (ctr1->array[ctr1->count - 1].attid != rdn_sa->attributeID_id) {
-               ldb_debug_set(ldb, LDB_DEBUG_FATAL,
-                             "No rDN found in replPropertyMetaData for %s!\n",
-                             ldb_dn_get_linearized(dn));
-               return LDB_ERR_CONSTRAINT_VIOLATION;
-       }
 
        /* the objectClass attribute is value 0x00000000, so must be first */
        if (ctr1->array[0].attid != DRSUAPI_ATTID_objectClass) {
@@ -730,34 +846,12 @@ static int replmd_replPropertyMetaDataCtr1_verify(struct ldb_context *ldb,
 
 static int replmd_replPropertyMetaDataCtr1_sort_and_verify(struct ldb_context *ldb,
                                                           struct replPropertyMetaDataCtr1 *ctr1,
-                                                          const struct dsdb_schema *schema,
                                                           struct ldb_dn *dn)
 {
-       const char *rdn_name;
-       const struct dsdb_attribute *rdn_sa;
-
-       rdn_name = ldb_dn_get_rdn_name(dn);
-       if (!rdn_name) {
-               ldb_debug_set(ldb, LDB_DEBUG_FATAL,
-                             __location__ ": No rDN for %s?\n",
-                             ldb_dn_get_linearized(dn));
-               return LDB_ERR_INVALID_DN_SYNTAX;
-       }
-
-       rdn_sa = dsdb_attribute_by_lDAPDisplayName(schema, rdn_name);
-       if (rdn_sa == NULL) {
-               ldb_debug_set(ldb, LDB_DEBUG_FATAL,
-                             __location__ ": No sa found for rDN %s for %s\n",
-                             rdn_name, ldb_dn_get_linearized(dn));
-               return LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE;
-       }
-
-       DEBUG(6,("Sorting rpmd with attid exception %u rDN=%s DN=%s\n",
-                rdn_sa->attributeID_id, rdn_name, ldb_dn_get_linearized(dn)));
-
-       LDB_TYPESAFE_QSORT(ctr1->array, ctr1->count, &rdn_sa->attributeID_id,
+       /* Note this is O(n^2) for the almost-sorted case, which this is */
+       LDB_TYPESAFE_QSORT(ctr1->array, ctr1->count, NULL,
                           replmd_replPropertyMetaData1_attid_sort);
-       return replmd_replPropertyMetaDataCtr1_verify(ldb, ctr1, rdn_sa, dn);
+       return replmd_replPropertyMetaDataCtr1_verify(ldb, ctr1, dn);
 }
 
 static int replmd_ldb_message_element_attid_sort(const struct ldb_message_element *e1,
@@ -798,74 +892,105 @@ static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct ds
                               const struct GUID *invocation_id, uint64_t seq_num,
                               uint64_t local_usn, NTTIME nttime, uint32_t version, bool deleted);
 
+static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
+                         struct ldb_message_element *el, struct parsed_dn **pdn,
+                         const char *ldap_oid, struct ldb_request *parent);
 
 /*
   fix up linked attributes in replmd_add.
   This involves setting up the right meta-data in extended DN
   components, and creating backlinks to the object
  */
-static int replmd_add_fix_la(struct ldb_module *module, struct ldb_message_element *el,
-                            uint64_t seq_num, const struct GUID *invocationId, time_t t,
-                            struct GUID *guid, const struct dsdb_attribute *sa, struct ldb_request *parent)
+static int replmd_add_fix_la(struct ldb_module *module, TALLOC_CTX *mem_ctx,
+                            struct replmd_private *replmd_private,
+                            struct ldb_message_element *el,
+                            struct replmd_replicated_request *ac,
+                            NTTIME now,
+                            struct ldb_dn *forward_dn,
+                            const struct dsdb_attribute *sa,
+                            struct ldb_request *parent)
 {
        unsigned int i;
-       TALLOC_CTX *tmp_ctx = talloc_new(el->values);
+       TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
        struct ldb_context *ldb = ldb_module_get_ctx(module);
-
+       struct parsed_dn *pdn;
        /* We will take a reference to the schema in replmd_add_backlink */
        const struct dsdb_schema *schema = dsdb_get_schema(ldb, NULL);
-       NTTIME now;
-
-       unix_to_nt_time(&now, t);
-
-       for (i=0; i<el->num_values; i++) {
-               struct ldb_val *v = &el->values[i];
-               struct dsdb_dn *dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, v, sa->syntax->ldap_oid);
-               struct GUID target_guid;
-               NTSTATUS status;
-               int ret;
+       struct ldb_val *new_values = NULL;
+       
+       int ret = get_parsed_dns(module, tmp_ctx, el, &pdn,
+                                sa->syntax->ldap_oid, parent);
+       if (ret != LDB_SUCCESS) {
+               talloc_free(tmp_ctx);
+               return ret;
+       }
 
-               /* note that the DN already has the extended
-                  components from the extended_dn_store module */
-               status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
-               if (!NT_STATUS_IS_OK(status) || GUID_all_zero(&target_guid)) {
-                       ret = dsdb_module_guid_by_dn(module, dsdb_dn->dn, &target_guid, parent);
-                       if (ret != LDB_SUCCESS) {
-                               talloc_free(tmp_ctx);
-                               return ret;
-                       }
-                       ret = dsdb_set_extended_dn_guid(dsdb_dn->dn, &target_guid, "GUID");
-                       if (ret != LDB_SUCCESS) {
-                               talloc_free(tmp_ctx);
-                               return ret;
-                       }
-               }
+       new_values = talloc_array(tmp_ctx, struct ldb_val, el->num_values);
+       if (new_values == NULL) {
+               ldb_module_oom(module);
+               talloc_free(tmp_ctx);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
 
-               ret = replmd_build_la_val(el->values, v, dsdb_dn, invocationId,
-                                         seq_num, seq_num, now, 0, false);
+       for (i = 0; i < el->num_values; i++) {
+               struct parsed_dn *p = &pdn[i];
+               ret = replmd_build_la_val(el->values, p->v, p->dsdb_dn,
+                                         &ac->our_invocation_id,
+                                         ac->seq_num, ac->seq_num, now, 0, false);
                if (ret != LDB_SUCCESS) {
                        talloc_free(tmp_ctx);
                        return ret;
                }
 
-               ret = replmd_add_backlink(module, schema, guid, &target_guid, true, sa, false);
+               ret = replmd_defer_add_backlink(module, replmd_private,
+                                               schema, ac,
+                                               forward_dn, &p->guid, true, sa,
+                                               parent);
                if (ret != LDB_SUCCESS) {
                        talloc_free(tmp_ctx);
                        return ret;
                }
+
+               new_values[i] = *p->v;
        }
+       el->values = talloc_steal(mem_ctx, new_values);
 
        talloc_free(tmp_ctx);
        return LDB_SUCCESS;
 }
 
+static int replmd_add_make_extended_dn(struct ldb_request *req,
+                                      const DATA_BLOB *guid_blob,
+                                      struct ldb_dn **_extended_dn)
+{
+       int ret;
+        const DATA_BLOB *sid_blob;
+       /* Calculate an extended DN for any linked attributes */
+       struct ldb_dn *extended_dn = ldb_dn_copy(req, req->op.add.message->dn);
+       if (!extended_dn) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       ret = ldb_dn_set_extended_component(extended_dn, "GUID", guid_blob);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
+
+       sid_blob = ldb_msg_find_ldb_val(req->op.add.message, "objectSID");
+       if (sid_blob != NULL) {
+               ret = ldb_dn_set_extended_component(extended_dn, "SID", sid_blob);
+               if (ret != LDB_SUCCESS) {
+                       return ret;
+               }
+       }
+       *_extended_dn = extended_dn;
+       return LDB_SUCCESS;
+}
 
 /*
   intercept add requests
  */
 static int replmd_add(struct ldb_module *module, struct ldb_request *req)
 {
-       struct samldb_msds_intid_persistant *msds_intid_struct;
        struct ldb_context *ldb;
         struct ldb_control *control;
        struct replmd_replicated_request *ac;
@@ -873,10 +998,18 @@ static int replmd_add(struct ldb_module *module, struct ldb_request *req)
        struct ldb_request *down_req;
        struct ldb_message *msg;
         const DATA_BLOB *guid_blob;
+        DATA_BLOB guid_blob_stack;
        struct GUID guid;
+       uint8_t guid_data[16];
        struct replPropertyMetaDataBlob nmd;
        struct ldb_val nmd_value;
-       const struct GUID *our_invocation_id;
+       struct ldb_dn *extended_dn = NULL;
+       
+       /*
+        * The use of a time_t here seems odd, but as the NTTIME
+        * elements are actually declared as NTTIME_1sec in the IDL,
+        * getting a higher resolution timestamp is not required.
+        */
        time_t t = time(NULL);
        NTTIME now;
        char *time_str;
@@ -887,7 +1020,10 @@ static int replmd_add(struct ldb_module *module, struct ldb_request *req)
        bool allow_add_guid = false;
        bool remove_current_guid = false;
        bool is_urgent = false;
+       bool is_schema_nc = false;
        struct ldb_message_element *objectclass_el;
+       struct replmd_private *replmd_private =
+               talloc_get_type_abort(ldb_module_get_private(module), struct replmd_private);
 
         /* check if there's a show relax control (used by provision to say 'I know what I'm doing') */
         control = ldb_request_get_control(req, LDB_CONTROL_RELAX_OID);
@@ -925,6 +1061,13 @@ static int replmd_add(struct ldb_module *module, struct ldb_request *req)
        } else {
                /* a new GUID */
                guid = GUID_random();
+               
+               guid_blob_stack = data_blob_const(guid_data, sizeof(guid_data));
+               
+               /* This can't fail */
+               ndr_push_struct_into_fixed_blob(&guid_blob_stack, &guid,
+                                               (ndr_push_flags_fn_t)ndr_push_GUID);
+               guid_blob = &guid_blob_stack;
        }
 
        ac = replmd_ctx_init(module, req);
@@ -941,15 +1084,6 @@ static int replmd_add(struct ldb_module *module, struct ldb_request *req)
                return ret;
        }
 
-       /* get our invocationId */
-       our_invocation_id = samdb_ntds_invocation_id(ldb);
-       if (!our_invocation_id) {
-               ldb_debug_set(ldb, LDB_DEBUG_ERROR,
-                             "replmd_add: unable to find invocationId\n");
-               talloc_free(ac);
-               return LDB_ERR_OPERATIONS_ERROR;
-       }
-
        /* we have to copy the message as the caller might have it as a const */
        msg = ldb_msg_copy_shallow(ac, req->op.add.message);
        if (msg == NULL) {
@@ -1002,12 +1136,17 @@ static int replmd_add(struct ldb_module *module, struct ldb_request *req)
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       for (i=0; i < msg->num_elements; i++) {
+       is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
+
+       for (i=0; i < msg->num_elements;) {
                struct ldb_message_element *e = &msg->elements[i];
                struct replPropertyMetaData1 *m = &nmd.ctr.ctr1.array[ni];
                const struct dsdb_attribute *sa;
 
-               if (e->name[0] == '@') continue;
+               if (e->name[0] == '@') {
+                       i++;
+                       continue;
+               }
 
                sa = dsdb_attribute_by_lDAPDisplayName(ac->schema, e->name);
                if (!sa) {
@@ -1022,23 +1161,46 @@ static int replmd_add(struct ldb_module *module, struct ldb_request *req)
                        /* if the attribute is not replicated (0x00000001)
                         * or constructed (0x00000004) it has no metadata
                         */
+                       i++;
                        continue;
                }
 
                if (sa->linkID != 0 && functional_level > DS_DOMAIN_FUNCTION_2000) {
-                       ret = replmd_add_fix_la(module, e, ac->seq_num, our_invocation_id, t, &guid, sa, req);
+                       if (extended_dn == NULL) {
+                               ret = replmd_add_make_extended_dn(req,
+                                                                 guid_blob,
+                                                                 &extended_dn);
+                               if (ret != LDB_SUCCESS) {
+                                       talloc_free(ac);
+                                       return ret;
+                               }
+                       }                       
+
+                       /*
+                        * Prepare the context for the backlinks and
+                        * create metadata for the forward links.  The
+                        * backlinks are created in
+                        * replmd_op_callback() after the successful
+                        * ADD of the object.
+                        */
+                       ret = replmd_add_fix_la(module, msg->elements,
+                                               replmd_private, e,
+                                               ac, now,
+                                               extended_dn,
+                                               sa, req);
                        if (ret != LDB_SUCCESS) {
                                talloc_free(ac);
                                return ret;
                        }
                        /* linked attributes are not stored in
                           replPropertyMetaData in FL above w2k */
+                       i++;
                        continue;
                }
 
-               m->attid                        = sa->attributeID_id;
-               m->version                      = 1;
-               if (m->attid == 0x20030) {
+               m->attid   = dsdb_attribute_get_attid(sa, is_schema_nc);
+               m->version = 1;
+               if (m->attid == DRSUAPI_ATTID_isDeleted) {
                        const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
                        const char* rdn;
 
@@ -1061,19 +1223,33 @@ static int replmd_add(struct ldb_module *module, struct ldb_request *req)
                } else {
                        m->originating_change_time      = now;
                }
-               m->originating_invocation_id    = *our_invocation_id;
+               m->originating_invocation_id    = ac->our_invocation_id;
                m->originating_usn              = ac->seq_num;
                m->local_usn                    = ac->seq_num;
                ni++;
+
+               if (!(e->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) {
+                       i++;
+                       continue;
+               }
+
+               e->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
+
+               if (e->num_values != 0) {
+                       i++;
+                       continue;
+               }
+
+               ldb_msg_remove_element(msg, e);
        }
 
        /* fix meta data count */
        nmd.ctr.ctr1.count = ni;
 
        /*
-        * sort meta data array, and move the rdn attribute entry to the end
+        * sort meta data array
         */
-       ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, ac->schema, msg->dn);
+       ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, msg->dn);
        if (ret != LDB_SUCCESS) {
                ldb_asprintf_errstring(ldb, "%s: error during direct ADD: %s", __func__, ldb_errstring(ldb));
                talloc_free(ac);
@@ -1179,14 +1355,6 @@ static int replmd_add(struct ldb_module *module, struct ldb_request *req)
        if (control) {
                control->critical = 0;
        }
-       if (ldb_dn_compare_base(ac->schema->base_dn, req->op.add.message->dn) != 0) {
-
-               /* Update the usn in the SAMLDB_MSDS_INTID_OPAQUE opaque */
-               msds_intid_struct = (struct samldb_msds_intid_persistant *) ldb_get_opaque(ldb, SAMLDB_MSDS_INTID_OPAQUE);
-               if (msds_intid_struct) {
-                       msds_intid_struct->usn = ac->seq_num;
-               }
-       }
        /* go on with the call chain */
        return ldb_next_request(module, down_req);
 }
@@ -1204,12 +1372,14 @@ static int replmd_update_rpmd_element(struct ldb_context *ldb,
                                      uint64_t *seq_num,
                                      const struct GUID *our_invocation_id,
                                      NTTIME now,
+                                     bool is_schema_nc,
                                      struct ldb_request *req)
 {
        uint32_t i;
        const struct dsdb_attribute *a;
        struct replPropertyMetaData1 *md1;
        bool may_skip = false;
+       uint32_t attid;
 
        a = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
        if (a == NULL) {
@@ -1224,6 +1394,8 @@ static int replmd_update_rpmd_element(struct ldb_context *ldb,
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
+       attid = dsdb_attribute_get_attid(a, is_schema_nc);
+
        if ((a->systemFlags & DS_FLAG_ATTR_NOT_REPLICATED) || (a->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED)) {
                return LDB_SUCCESS;
        }
@@ -1253,6 +1425,23 @@ static int replmd_update_rpmd_element(struct ldb_context *ldb,
                } else if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE) {
                        may_skip = true;
                }
+       } else if (a->linkID != 0 && LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE &&
+                  ldb_request_get_control(req, DSDB_CONTROL_REPLMD_VANISH_LINKS) != NULL) {
+               /*
+                * We intentionally skip the version bump when attempting to
+                * vanish links.
+                *
+                * The control is set by dbcheck and expunge-tombstones which
+                * both attempt to be non-replicating. Otherwise, making an
+                * alteration to the replication state would trigger a
+                * broadcast of all expunged objects.
+                */
+               may_skip = true;
+       }
+
+       if (el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA) {
+               may_skip = false;
+               el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
        }
 
        if (may_skip) {
@@ -1267,10 +1456,25 @@ static int replmd_update_rpmd_element(struct ldb_context *ldb,
        }
 
        for (i=0; i<omd->ctr.ctr1.count; i++) {
-               if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) break;
-       }
-
-       if (a->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
+               /*
+                * First check if we find it under the msDS-IntID,
+                * then check if we find it under the OID and
+                * prefixMap ID.
+                *
+                * This allows the administrator to simply re-write
+                * the attributes and so restore replication, which is
+                * likely what they will try to do.
+                */
+               if (attid == omd->ctr.ctr1.array[i].attid) {
+                       break;
+               }
+
+               if (a->attributeID_id == omd->ctr.ctr1.array[i].attid) {
+                       break;
+               }
+       }
+
+       if (a->linkID != 0 && dsdb_functional_level(ldb) > DS_DOMAIN_FUNCTION_2000) {
                /* linked attributes are not stored in
                   replPropertyMetaData in FL above w2k, but we do
                   raise the seqnum for the object  */
@@ -1306,8 +1510,8 @@ static int replmd_update_rpmd_element(struct ldb_context *ldb,
 
        md1 = &omd->ctr.ctr1.array[i];
        md1->version++;
-       md1->attid                     = a->attributeID_id;
-       if (md1->attid == 0x20030) {
+       md1->attid = attid;
+       if (md1->attid == DRSUAPI_ATTID_isDeleted) {
                const struct ldb_val *rdn_val = ldb_dn_get_rdn_val(msg->dn);
                const char* rdn;
 
@@ -1336,6 +1540,58 @@ static int replmd_update_rpmd_element(struct ldb_context *ldb,
        return LDB_SUCCESS;
 }
 
+/*
+ * Bump the replPropertyMetaData version on an attribute, and if it
+ * has changed (or forced by leaving rdn_old NULL), update the value
+ * in the entry.
+ *
+ * This is important, as calling a modify operation may not change the
+ * version number if the values appear unchanged, but a rename between
+ * parents bumps this value.
+ *
+ */
+static int replmd_update_rpmd_rdn_attr(struct ldb_context *ldb,
+                                      struct ldb_message *msg,
+                                      const struct ldb_val *rdn_new,
+                                      const struct ldb_val *rdn_old,
+                                      struct replPropertyMetaDataBlob *omd,
+                                      struct replmd_replicated_request *ar,
+                                      NTTIME now,
+                                      bool is_schema_nc)
+{
+       const char *rdn_name = ldb_dn_get_rdn_name(msg->dn);
+       const struct dsdb_attribute *rdn_attr =
+               dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
+       const char *attr_name = rdn_attr != NULL ?
+                               rdn_attr->lDAPDisplayName :
+                               rdn_name;
+       struct ldb_message_element new_el = {
+               .flags = LDB_FLAG_MOD_REPLACE,
+               .name = attr_name,
+               .num_values = 1,
+               .values = discard_const_p(struct ldb_val, rdn_new)
+       };
+       struct ldb_message_element old_el = {
+               .flags = LDB_FLAG_MOD_REPLACE,
+               .name = attr_name,
+               .num_values = rdn_old ? 1 : 0,
+               .values = discard_const_p(struct ldb_val, rdn_old)
+       };
+
+       if (ldb_msg_element_equal_ordered(&new_el, &old_el) == false) {
+               int ret = ldb_msg_add(msg, &new_el, LDB_FLAG_MOD_REPLACE);
+               if (ret != LDB_SUCCESS) {
+                       return ldb_oom(ldb);
+               }
+       }
+
+       return replmd_update_rpmd_element(ldb, msg, &new_el, NULL,
+                                         omd, ar->schema, &ar->seq_num,
+                                         &ar->our_invocation_id,
+                                         now, is_schema_nc, ar->req);
+
+}
+
 static uint64_t find_max_local_usn(struct replPropertyMetaDataBlob omd)
 {
        uint32_t count = omd.ctr.ctr1.count;
@@ -1360,7 +1616,7 @@ static int replmd_update_rpmd(struct ldb_module *module,
                              struct ldb_request *req,
                              const char * const *rename_attrs,
                              struct ldb_message *msg, uint64_t *seq_num,
-                             time_t t,
+                             time_t t, bool is_schema_nc,
                              bool *is_urgent, bool *rodc)
 {
        const struct ldb_val *omd_value;
@@ -1371,18 +1627,26 @@ static int replmd_update_rpmd(struct ldb_module *module,
        const struct GUID *our_invocation_id;
        int ret;
        const char * const *attrs = NULL;
-       const char * const attrs1[] = { "replPropertyMetaData", "*", NULL };
        const char * const attrs2[] = { "uSNChanged", "objectClass", "instanceType", NULL };
        struct ldb_result *res;
        struct ldb_context *ldb;
        struct ldb_message_element *objectclass_el;
        enum urgent_situation situation;
        bool rmd_is_provided;
+       bool rmd_is_just_resorted = false;
+       const char *not_rename_attrs[4 + msg->num_elements];
 
        if (rename_attrs) {
                attrs = rename_attrs;
        } else {
-               attrs = attrs1;
+               for (i = 0; i < msg->num_elements; i++) {
+                       not_rename_attrs[i] = msg->elements[i].name;
+               }
+               not_rename_attrs[i] = "replPropertyMetaData";
+               not_rename_attrs[i+1] = "objectClass";
+               not_rename_attrs[i+2] = "instanceType";
+               not_rename_attrs[i+3] = NULL;
+               attrs = not_rename_attrs;
        }
 
        ldb = ldb_module_get_ctx(module);
@@ -1399,6 +1663,9 @@ static int replmd_update_rpmd(struct ldb_module *module,
 
        if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_OID)) {
                rmd_is_provided = true;
+               if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_RESORT_OID)) {
+                       rmd_is_just_resorted = true;
+               }
        } else {
                rmd_is_provided = false;
        }
@@ -1417,7 +1684,7 @@ static int replmd_update_rpmd(struct ldb_module *module,
                /* In this case the change_replmetadata control was supplied */
                /* We check that it's the only attribute that is provided
                 * (it's a rare case so it's better to keep the code simplier)
-                * We also check that the highest local_usn is bigger than
+                * We also check that the highest local_usn is bigger or the same as
                 * uSNChanged. */
                uint64_t db_seq;
                if( msg->num_elements != 1 ||
@@ -1444,7 +1711,6 @@ static int replmd_update_rpmd(struct ldb_module *module,
                                 ldb_dn_get_linearized(msg->dn)));
                        return LDB_ERR_OPERATIONS_ERROR;
                }
-               *seq_num = find_max_local_usn(omd);
 
                ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs2,
                                            DSDB_FLAG_NEXT_MODULE |
@@ -1457,12 +1723,22 @@ static int replmd_update_rpmd(struct ldb_module *module,
                        return ret;
                }
 
-               db_seq = ldb_msg_find_attr_as_uint64(res->msgs[0], "uSNChanged", 0);
-               if (*seq_num <= db_seq) {
-                       DEBUG(0,(__location__ ": changereplmetada control provided but max(local_usn)"\
-                                             " is less or equal to uSNChanged (max = %lld uSNChanged = %lld)\n",
-                                (long long)*seq_num, (long long)db_seq));
-                       return LDB_ERR_OPERATIONS_ERROR;
+               if (rmd_is_just_resorted == false) {
+                       *seq_num = find_max_local_usn(omd);
+
+                       db_seq = ldb_msg_find_attr_as_uint64(res->msgs[0], "uSNChanged", 0);
+
+                       /*
+                        * The test here now allows for a new
+                        * replPropertyMetaData with no change, if was
+                        * just dbcheck re-sorting the values.
+                        */
+                       if (*seq_num <= db_seq) {
+                               DEBUG(0,(__location__ ": changereplmetada control provided but max(local_usn)" \
+                                        " is less than uSNChanged (max = %lld uSNChanged = %lld)\n",
+                                        (long long)*seq_num, (long long)db_seq));
+                               return LDB_ERR_OPERATIONS_ERROR;
+                       }
                }
 
        } else {
@@ -1502,19 +1778,37 @@ static int replmd_update_rpmd(struct ldb_module *module,
                        return LDB_ERR_OPERATIONS_ERROR;
                }
 
-               for (i=0; i<msg->num_elements; i++) {
+               for (i=0; i<msg->num_elements;) {
+                       struct ldb_message_element *el = &msg->elements[i];
                        struct ldb_message_element *old_el;
-                       old_el = ldb_msg_find_element(res->msgs[0], msg->elements[i].name);
-                       ret = replmd_update_rpmd_element(ldb, msg, &msg->elements[i], old_el, &omd, schema, seq_num,
-                                                        our_invocation_id, now, req);
+
+                       old_el = ldb_msg_find_element(res->msgs[0], el->name);
+                       ret = replmd_update_rpmd_element(ldb, msg, el, old_el,
+                                                        &omd, schema, seq_num,
+                                                        our_invocation_id,
+                                                        now, is_schema_nc,
+                                                        req);
                        if (ret != LDB_SUCCESS) {
                                return ret;
                        }
 
                        if (!*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
-                               *is_urgent = replmd_check_urgent_attribute(&msg->elements[i]);
+                               *is_urgent = replmd_check_urgent_attribute(el);
+                       }
+
+                       if (!(el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) {
+                               i++;
+                               continue;
+                       }
+
+                       el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;
+
+                       if (el->num_values != 0) {
+                               i++;
+                               continue;
                        }
 
+                       ldb_msg_remove_element(msg, el);
                }
        }
 
@@ -1542,7 +1836,7 @@ static int replmd_update_rpmd(struct ldb_module *module,
         * replmd_update_rpmd_element has done an update if the
         * seq_num is set
         */
-       if (*seq_num != 0) {
+       if (*seq_num != 0 || rmd_is_just_resorted == true) {
                struct ldb_val *md_value;
                struct ldb_message_element *el;
 
@@ -1572,7 +1866,7 @@ static int replmd_update_rpmd(struct ldb_module *module,
                        return LDB_ERR_OPERATIONS_ERROR;
                }
 
-               ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &omd.ctr.ctr1, schema, msg->dn);
+               ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &omd.ctr.ctr1, msg->dn);
                if (ret != LDB_SUCCESS) {
                        ldb_asprintf_errstring(ldb, "%s: %s", __func__, ldb_errstring(ldb));
                        return ret;
@@ -1600,36 +1894,187 @@ static int replmd_update_rpmd(struct ldb_module *module,
        return LDB_SUCCESS;
 }
 
-struct parsed_dn {
-       struct dsdb_dn *dsdb_dn;
+struct compare_ctx {
        struct GUID *guid;
-       struct ldb_val *v;
+       struct ldb_context *ldb;
+       TALLOC_CTX *mem_ctx;
+       const char *ldap_oid;
+       int err;
+       const struct GUID *invocation_id;
 };
 
+/* When a parsed_dn comes from the database, sometimes it is not really parsed. */
+
+static int really_parse_trusted_dn(TALLOC_CTX *mem_ctx, struct ldb_context *ldb,
+                                  struct parsed_dn *pdn, const char *ldap_oid)
+{
+       NTSTATUS status;
+       struct dsdb_dn *dsdb_dn = dsdb_dn_parse_trusted(mem_ctx, ldb, pdn->v,
+                                                       ldap_oid);
+       if (dsdb_dn == NULL) {
+               return LDB_ERR_INVALID_DN_SYNTAX;
+       }
+
+       status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &pdn->guid, "GUID");
+       if (!NT_STATUS_IS_OK(status)) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       pdn->dsdb_dn = dsdb_dn;
+       return LDB_SUCCESS;
+}
+
+/*
+ * We choose, as the sort order, the same order as is used in DRS replication,
+ * which is the memcmp() order of the NDR GUID, not that obtained from
+ * GUID_compare().
+ *
+ * This means that sorted links will be in the same order as a new DC would
+ * see them.
+ */
+static int ndr_guid_compare(struct GUID *guid1, struct GUID *guid2)
+{
+       uint8_t v1_data[16];
+       struct ldb_val v1 = data_blob_const(v1_data, sizeof(v1_data));
+       uint8_t v2_data[16];
+       struct ldb_val v2 = data_blob_const(v2_data, sizeof(v2_data));
+
+       /* This can't fail */
+       ndr_push_struct_into_fixed_blob(&v1, guid1,
+                                       (ndr_push_flags_fn_t)ndr_push_GUID);
+       /* This can't fail */
+       ndr_push_struct_into_fixed_blob(&v2, guid2,
+                                       (ndr_push_flags_fn_t)ndr_push_GUID);
+       return data_blob_cmp(&v1, &v2);
+}
+
 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
 {
-       return GUID_compare(pdn1->guid, pdn2->guid);
+       int ret = ndr_guid_compare(&pdn1->guid, &pdn2->guid);
+       if (ret == 0) {
+               return data_blob_cmp(&pdn1->dsdb_dn->extra_part,
+                                    &pdn2->dsdb_dn->extra_part);
+       }
+       return ret;
 }
 
-static struct parsed_dn *parsed_dn_find(struct parsed_dn *pdn,
-                                       unsigned int count, struct GUID *guid,
-                                       struct ldb_dn *dn)
+static int la_guid_compare_with_trusted_dn(struct compare_ctx *ctx,
+                                          struct parsed_dn *p)
+{
+       /*
+        * This works like a standard compare function in its return values,
+        * but has an extra trick to deal with errors: zero is returned and
+        * ctx->err is set to the ldb error code.
+        *
+        * That is, if (as is expected in most cases) you get a non-zero
+        * result, you don't need to check for errors.
+        *
+        * We assume the second argument refers to a DN is from the database
+        * and has a GUID -- but this GUID might not have been parsed out yet.
+        */
+       if (p->dsdb_dn == NULL) {
+               int ret = really_parse_trusted_dn(ctx->mem_ctx, ctx->ldb, p,
+                                                 ctx->ldap_oid);
+               if (ret != LDB_SUCCESS) {
+                       ctx->err = ret;
+                       return 0;
+               }
+       }
+       return ndr_guid_compare(ctx->guid, &p->guid);
+}
+
+
+
+static int parsed_dn_find(struct ldb_context *ldb, struct parsed_dn *pdn,
+                         unsigned int count,
+                         struct GUID *guid,
+                         struct ldb_dn *target_dn,
+                         struct parsed_dn **exact,
+                         struct parsed_dn **next,
+                         const char *ldap_oid)
 {
-       struct parsed_dn *ret;
        unsigned int i;
-       if (dn && GUID_all_zero(guid)) {
-               /* when updating a link using DRS, we sometimes get a
-                  NULL GUID. We then need to try and match by DN */
-               for (i=0; i<count; i++) {
-                       if (ldb_dn_compare(pdn[i].dsdb_dn->dn, dn) == 0) {
-                               dsdb_get_extended_dn_guid(pdn[i].dsdb_dn->dn, guid, "GUID");
-                               return &pdn[i];
+       struct compare_ctx ctx;
+       if (pdn == NULL) {
+               *exact = NULL;
+               *next = NULL;
+               return LDB_SUCCESS;
+       }
+
+       if (unlikely(GUID_all_zero(guid))) {
+               /*
+                * When updating a link using DRS, we sometimes get a NULL
+                * GUID when a forward link has been deleted and its GUID has
+                * for some reason been forgotten. The best we can do is try
+                * and match by DN via a linear search. Note that this
+                * probably only happens in the ADD case, in which we only
+                * allow modification of link if it is already deleted, so
+                * this seems very close to an elaborate NO-OP, but we are not
+                * quite prepared to declare it so.
+                *
+                * If the DN is not in our list, we have to add it to the
+                * beginning of the list, where it would naturally sort.
+                */
+               struct parsed_dn *p;
+               if (target_dn == NULL) {
+                       /* We don't know the target DN, so we can't search for DN */
+                       DEBUG(1, ("parsed_dn_find has a NULL GUID for a linked "
+                                 "attribute but we don't have a DN to compare "
+                                 "it with\n"));
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+               *exact = NULL;
+               *next = NULL;
+
+               DEBUG(3, ("parsed_dn_find has a NULL GUID for a link to DN "
+                         "%s; searching through links for it",
+                         ldb_dn_get_linearized(target_dn)));
+
+               for (i = 0; i < count; i++) {
+                       int cmp;
+                       p = &pdn[i];
+                       if (p->dsdb_dn == NULL) {
+                               int ret = really_parse_trusted_dn(pdn, ldb, p, ldap_oid);
+                               if (ret != LDB_SUCCESS) {
+                                       return LDB_ERR_OPERATIONS_ERROR;
+                               }
+                       }
+
+                       cmp = ldb_dn_compare(p->dsdb_dn->dn, target_dn);
+                       if (cmp == 0) {
+                               *exact = p;
+                               return LDB_SUCCESS;
                        }
                }
-               return NULL;
+               /*
+                * Here we have a null guid which doesn't match any existing
+                * link. This is a bit unexpected because null guids occur
+                * when a forward link has been deleted and we are replicating
+                * that deletion.
+                *
+                * The best thing to do is weep into the logs and add the
+                * offending link to the beginning of the list which is
+                * at least the correct sort position.
+                */
+               DEBUG(1, ("parsed_dn_find has been given a NULL GUID for a "
+                         "link to unknown DN %s\n",
+                         ldb_dn_get_linearized(target_dn)));
+               *next = pdn;
+               return LDB_SUCCESS;
        }
-       BINARY_ARRAY_SEARCH(pdn, count, guid, guid, GUID_compare, ret);
-       return ret;
+
+       ctx.guid = guid;
+       ctx.ldb = ldb;
+       ctx.mem_ctx = pdn;
+       ctx.ldap_oid = ldap_oid;
+       ctx.err = 0;
+
+       BINARY_ARRAY_SEARCH_GTE(pdn, count, &ctx, la_guid_compare_with_trusted_dn,
+                               *exact, *next);
+
+       if (ctx.err != 0) {
+               return ctx.err;
+       }
+       return LDB_SUCCESS;
 }
 
 /*
@@ -1641,6 +2086,7 @@ static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
                          const char *ldap_oid, struct ldb_request *parent)
 {
        unsigned int i;
+       bool values_are_sorted = true;
        struct ldb_context *ldb = ldb_module_get_ctx(module);
 
        if (el == NULL) {
@@ -1669,16 +2115,11 @@ static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
 
                dn = p->dsdb_dn->dn;
 
-               p->guid = talloc(*pdn, struct GUID);
-               if (p->guid == NULL) {
-                       ldb_module_oom(module);
-                       return LDB_ERR_OPERATIONS_ERROR;
-               }
-
-               status = dsdb_get_extended_dn_guid(dn, p->guid, "GUID");
-               if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+               status = dsdb_get_extended_dn_guid(dn, &p->guid, "GUID");
+               if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) ||
+                   unlikely(GUID_all_zero(&p->guid))) {
                        /* we got a DN without a GUID - go find the GUID */
-                       int ret = dsdb_module_guid_by_dn(module, dn, p->guid, parent);
+                       int ret = dsdb_module_guid_by_dn(module, dn, &p->guid, parent);
                        if (ret != LDB_SUCCESS) {
                                ldb_asprintf_errstring(ldb, "Unable to find GUID for DN %s\n",
                                                       ldb_dn_get_linearized(dn));
@@ -1689,19 +2130,92 @@ static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
                                }
                                return ret;
                        }
-                       ret = dsdb_set_extended_dn_guid(dn, p->guid, "GUID");
+                       ret = dsdb_set_extended_dn_guid(dn, &p->guid, "GUID");
                        if (ret != LDB_SUCCESS) {
                                return ret;
                        }
                } else if (!NT_STATUS_IS_OK(status)) {
                        return LDB_ERR_OPERATIONS_ERROR;
                }
-
+               if (i > 0 && values_are_sorted) {
+                       int cmp = parsed_dn_compare(p, &(*pdn)[i - 1]);
+                       if (cmp < 0) {
+                               values_are_sorted = false;
+                       }
+               }
                /* keep a pointer to the original ldb_val */
                p->v = v;
        }
+       if (! values_are_sorted) {
+               TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
+       }
+       return LDB_SUCCESS;
+}
 
-       TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
+/*
+ * Get a series of trusted message element values. The result is sorted by
+ * GUID, even though the GUIDs might not be known. That works because we trust
+ * the database to give us the elements like that if the
+ * replmd_private->sorted_links flag is set.
+ *
+ * We also ensure that the links are in the Functional Level 2003
+ * linked attributes format.
+ */
+static int get_parsed_dns_trusted(struct ldb_module *module,
+                                 struct replmd_private *replmd_private,
+                                 TALLOC_CTX *mem_ctx,
+                                 struct ldb_message_element *el,
+                                 struct parsed_dn **pdn,
+                                 const char *ldap_oid,
+                                 struct ldb_request *parent)
+{
+       unsigned int i;
+       int ret;
+       if (el == NULL) {
+               *pdn = NULL;
+               return LDB_SUCCESS;
+       }
+
+       if (!replmd_private->sorted_links) {
+               /* We need to sort the list. This is the slow old path we want
+                  to avoid.
+                */
+               ret = get_parsed_dns(module, mem_ctx, el, pdn, ldap_oid,
+                                     parent);
+               if (ret != LDB_SUCCESS) {
+                       return ret;
+               }
+       } else {
+               /* Here we get a list of 'struct parsed_dns' without the parsing */
+               *pdn = talloc_zero_array(mem_ctx, struct parsed_dn,
+                                        el->num_values);
+               if (!*pdn) {
+                       ldb_module_oom(module);
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+
+               for (i = 0; i < el->num_values; i++) {
+                       (*pdn)[i].v = &el->values[i];
+               }
+       }
+
+       /*
+        * This upgrades links to FL2003 style, and sorts the result
+        * if that was needed.
+        *
+        * TODO: Add a database feature that asserts we have no FL2000
+        *       style links to avoid this check or add a feature that
+        *       uses a similar check to find sorted/unsorted links
+        *       for an on-the-fly upgrade.
+        */
+
+       ret = replmd_check_upgrade_links(ldb_module_get_ctx(module),
+                                        *pdn, el->num_values,
+                                        el,
+                                        ldap_oid);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
 
        return LDB_SUCCESS;
 }
@@ -1799,29 +2313,74 @@ static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct d
 
 /*
   check if any links need upgrading from w2k format
-
-  The parent_ctx is the ldb_message_element which contains the values array that dns[i].v points at, and which should be used for allocating any new value.
  */
-static int replmd_check_upgrade_links(struct parsed_dn *dns, uint32_t count, struct ldb_message_element *parent_ctx, const struct GUID *invocation_id)
+static int replmd_check_upgrade_links(struct ldb_context *ldb,
+                                     struct parsed_dn *dns, uint32_t count,
+                                     struct ldb_message_element *el,
+                                     const char *ldap_oid)
 {
        uint32_t i;
+       const struct GUID *invocation_id = NULL;
        for (i=0; i<count; i++) {
                NTSTATUS status;
                uint32_t version;
                int ret;
+               if (dns[i].dsdb_dn == NULL) {
+                       ret = really_parse_trusted_dn(dns, ldb, &dns[i],
+                                                     ldap_oid);
+                       if (ret != LDB_SUCCESS) {
+                               return LDB_ERR_INVALID_DN_SYNTAX;
+                       }
+               }
 
-               status = dsdb_get_extended_dn_uint32(dns[i].dsdb_dn->dn, &version, "RMD_VERSION");
+               status = dsdb_get_extended_dn_uint32(dns[i].dsdb_dn->dn,
+                                                    &version, "RMD_VERSION");
                if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+                       /*
+                        *  We optimistically assume they are all the same; if
+                        *  the first one is fixed, they are all fixed.
+                        *
+                        *  If the first one was *not* fixed and we find a
+                        *  later one that is, that is an occasion to shout
+                        *  with DEBUG(0).
+                        */
+                       if (i == 0) {
+                               return LDB_SUCCESS;
+                       }
+                       DEBUG(0, ("Mixed w2k and fixed format "
+                                 "linked attributes\n"));
                        continue;
                }
 
+               if (invocation_id == NULL) {
+                       invocation_id = samdb_ntds_invocation_id(ldb);
+                       if (invocation_id == NULL) {
+                               return LDB_ERR_OPERATIONS_ERROR;
+                       }
+               }
+
+
                /* it's an old one that needs upgrading */
-               ret = replmd_update_la_val(parent_ctx->values, dns[i].v, dns[i].dsdb_dn, dns[i].dsdb_dn, invocation_id,
-                                          1, 1, 0, 0, false);
+               ret = replmd_update_la_val(el->values, dns[i].v,
+                                          dns[i].dsdb_dn, dns[i].dsdb_dn,
+                                          invocation_id, 1, 1, 0, 0, false);
                if (ret != LDB_SUCCESS) {
                        return ret;
                }
        }
+
+       /*
+        * This sort() is critical for the operation of
+        * get_parsed_dns_trusted() because callers of this function
+        * expect a sorted list, and FL2000 style links are not
+        * sorted.  In particular, as well as the upgrade case,
+        * get_parsed_dns_trusted() is called from
+        * replmd_delete_remove_link() even in FL2000 mode
+        *
+        * We do not normally pay the cost of the qsort() due to the
+        * early return in the RMD_VERSION found case.
+        */
+       TYPESAFE_QSORT(dns, count, parsed_dn_compare);
        return LDB_SUCCESS;
 }
 
@@ -1832,7 +2391,7 @@ static int replmd_check_upgrade_links(struct parsed_dn *dns, uint32_t count, str
  */
 static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct dsdb_dn *dsdb_dn,
                                struct dsdb_dn *old_dsdb_dn, const struct GUID *invocation_id,
-                               uint64_t seq_num, uint64_t local_usn, NTTIME nttime,
+                               uint64_t usn, uint64_t local_usn, NTTIME nttime,
                                uint32_t version, bool deleted)
 {
        struct ldb_dn *dn = dsdb_dn->dn;
@@ -1855,7 +2414,7 @@ static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct d
        }
        tval = data_blob_string_const(tstring);
 
-       usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)seq_num);
+       usn_string = talloc_asprintf(mem_ctx, "%llu", (unsigned long long)usn);
        if (!usn_string) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
@@ -1930,6 +2489,7 @@ static int replmd_update_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct d
   handle adding a linked attribute
  */
 static int replmd_modify_la_add(struct ldb_module *module,
+                               struct replmd_private *replmd_private,
                                const struct dsdb_schema *schema,
                                struct ldb_message *msg,
                                struct ldb_message_element *el,
@@ -1937,72 +2497,108 @@ static int replmd_modify_la_add(struct ldb_module *module,
                                const struct dsdb_attribute *schema_attr,
                                uint64_t seq_num,
                                time_t t,
-                               struct GUID *msg_guid,
+                               struct ldb_dn *msg_dn,
                                struct ldb_request *parent)
 {
-       unsigned int i;
+       unsigned int i, j;
        struct parsed_dn *dns, *old_dns;
        TALLOC_CTX *tmp_ctx = talloc_new(msg);
        int ret;
        struct ldb_val *new_values = NULL;
-       unsigned int num_new_values = 0;
-       unsigned old_num_values = old_el?old_el->num_values:0;
+       unsigned old_num_values = old_el ? old_el->num_values : 0;
+       unsigned num_values = 0;
+       unsigned max_num_values;
        const struct GUID *invocation_id;
        struct ldb_context *ldb = ldb_module_get_ctx(module);
        NTTIME now;
-
        unix_to_nt_time(&now, t);
 
-       ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
+       invocation_id = samdb_ntds_invocation_id(ldb);
+       if (!invocation_id) {
+               talloc_free(tmp_ctx);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       /* get the DNs to be added, fully parsed.
+        *
+        * We need full parsing because they came off the wire and we don't
+        * trust them, besides which we need their details to know where to put
+        * them.
+        */
+       ret = get_parsed_dns(module, tmp_ctx, el, &dns,
+                            schema_attr->syntax->ldap_oid, parent);
        if (ret != LDB_SUCCESS) {
                talloc_free(tmp_ctx);
                return ret;
        }
 
-       ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
+       /* get the existing DNs, lazily parsed */
+       ret = get_parsed_dns_trusted(module, replmd_private,
+                                    tmp_ctx, old_el, &old_dns,
+                                    schema_attr->syntax->ldap_oid, parent);
+
        if (ret != LDB_SUCCESS) {
                talloc_free(tmp_ctx);
                return ret;
        }
 
-       invocation_id = samdb_ntds_invocation_id(ldb);
-       if (!invocation_id) {
+       max_num_values = old_num_values + el->num_values;
+       if (max_num_values < old_num_values) {
+               DEBUG(0, ("we seem to have overflow in replmd_modify_la_add. "
+                         "old values: %u, new values: %u, sum: %u",
+                         old_num_values, el->num_values, max_num_values));
                talloc_free(tmp_ctx);
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       ret = replmd_check_upgrade_links(old_dns, old_num_values, old_el, invocation_id);
-       if (ret != LDB_SUCCESS) {
+       new_values = talloc_zero_array(tmp_ctx, struct ldb_val, max_num_values);
+
+       if (new_values == NULL) {
+               ldb_module_oom(module);
                talloc_free(tmp_ctx);
-               return ret;
+               return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       /* for each new value, see if it exists already with the same GUID */
-       for (i=0; i<el->num_values; i++) {
-               struct parsed_dn *p = parsed_dn_find(old_dns, old_num_values, dns[i].guid, NULL);
-               if (p == NULL) {
-                       /* this is a new linked attribute value */
-                       new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val, num_new_values+1);
-                       if (new_values == NULL) {
-                               ldb_module_oom(module);
-                               talloc_free(tmp_ctx);
-                               return LDB_ERR_OPERATIONS_ERROR;
-                       }
-                       ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
-                                                 invocation_id, seq_num, seq_num, now, 0, false);
-                       if (ret != LDB_SUCCESS) {
-                               talloc_free(tmp_ctx);
-                               return ret;
-                       }
-                       num_new_values++;
-               } else {
-                       /* this is only allowed if the GUID was
-                          previously deleted. */
-                       uint32_t rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
+       /*
+        * For each new value, find where it would go in the list. If there is
+        * a matching GUID there, we update the existing value; otherwise we
+        * put it in place.
+        */
+       j = 0;
+       for (i = 0; i < el->num_values; i++) {
+               struct parsed_dn *exact;
+               struct parsed_dn *next;
+               unsigned offset;
+               int err = parsed_dn_find(ldb, old_dns, old_num_values,
+                                        &dns[i].guid,
+                                        dns[i].dsdb_dn->dn,
+                                        &exact, &next,
+                                        schema_attr->syntax->ldap_oid);
+               if (err != LDB_SUCCESS) {
+                       talloc_free(tmp_ctx);
+                       return err;
+               }
+
+               if (exact != NULL) {
+                       /*
+                        * We are trying to add one that exists, which is only
+                        * allowed if it was previously deleted.
+                        *
+                        * When we do undelete a link we change it in place.
+                        * It will be copied across into the right spot in due
+                        * course.
+                        */
+                       uint32_t rmd_flags;
+                       rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
 
                        if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
-                               ldb_asprintf_errstring(ldb, "Attribute %s already exists for target GUID %s",
-                                                      el->name, GUID_string(tmp_ctx, p->guid));
+                               struct GUID_txt_buf guid_str;
+                               ldb_asprintf_errstring(ldb,
+                                                      "Attribute %s already "
+                                                      "exists for target GUID %s",
+                                                      el->name,
+                                                      GUID_buf_string(&exact->guid,
+                                                                      &guid_str));
                                talloc_free(tmp_ctx);
                                /* error codes for 'member' need to be
                                   special cased */
@@ -2012,35 +2608,91 @@ static int replmd_modify_la_add(struct ldb_module *module,
                                        return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
                                }
                        }
-                       ret = replmd_update_la_val(old_el->values, p->v, dns[i].dsdb_dn, p->dsdb_dn,
-                                                  invocation_id, seq_num, seq_num, now, 0, false);
+
+                       ret = replmd_update_la_val(new_values, exact->v,
+                                                  dns[i].dsdb_dn,
+                                                  exact->dsdb_dn,
+                                                  invocation_id, seq_num,
+                                                  seq_num, now, 0, false);
+                       if (ret != LDB_SUCCESS) {
+                               talloc_free(tmp_ctx);
+                               return ret;
+                       }
+
+                       ret = replmd_add_backlink(module, replmd_private,
+                                                 schema,
+                                                 msg_dn,
+                                                 &dns[i].guid, 
+                                                 true,
+                                                 schema_attr,
+                                                 parent);
                        if (ret != LDB_SUCCESS) {
                                talloc_free(tmp_ctx);
                                return ret;
+                               }
+                       continue;
+               }
+               /*
+                * Here we don't have an exact match.
+                *
+                * If next is NULL, this one goes beyond the end of the
+                * existing list, so we need to add all of those ones first.
+                *
+                * If next is not NULL, we need to add all the ones before
+                * next.
+                */
+               if (next == NULL) {
+                       offset = old_num_values;
+               } else {
+                       /* next should have been parsed, but let's make sure */
+                       if (next->dsdb_dn == NULL) {
+                               ret = really_parse_trusted_dn(tmp_ctx, ldb, next,
+                                                             schema_attr->syntax->ldap_oid);
+                               if (ret != LDB_SUCCESS) {
+                                       return ret;
+                               }
                        }
+                       offset = MIN(next - old_dns, old_num_values);
+               }
+
+               /* put all the old ones before next on the list */
+               for (; j < offset; j++) {
+                       new_values[num_values] = *old_dns[j].v;
+                       num_values++;
                }
 
-               ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, true);
+               ret = replmd_add_backlink(module, replmd_private,
+                                         schema, msg_dn,
+                                         &dns[i].guid,
+                                         true, schema_attr,
+                                         parent);
+               /* Make the new linked attribute ldb_val. */
+               ret = replmd_build_la_val(new_values, &new_values[num_values],
+                                         dns[i].dsdb_dn, invocation_id,
+                                         seq_num, seq_num,
+                                         now, 0, false);
+               if (ret != LDB_SUCCESS) {
+                       talloc_free(tmp_ctx);
+                       return ret;
+               }
+               num_values++;
                if (ret != LDB_SUCCESS) {
                        talloc_free(tmp_ctx);
                        return ret;
                }
        }
-
-       /* add the new ones on to the end of the old values, constructing a new el->values */
-       el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
-                                   struct ldb_val,
-                                   old_num_values+num_new_values);
-       if (el->values == NULL) {
-               ldb_module_oom(module);
-               return LDB_ERR_OPERATIONS_ERROR;
+       /* copy the rest of the old ones (if any) */
+       for (; j < old_num_values; j++) {
+               new_values[num_values] = *old_dns[j].v;
+               num_values++;
        }
 
-       memcpy(&el->values[old_num_values], new_values, num_new_values*sizeof(struct ldb_val));
-       el->num_values = old_num_values + num_new_values;
-
-       talloc_steal(msg->elements, el->values);
-       talloc_steal(el->values, new_values);
+       talloc_steal(msg->elements, new_values);
+       if (old_el != NULL) {
+               talloc_steal(msg->elements, old_el->values);
+       }
+       el->values = new_values;
+       el->num_values = num_values;
 
        talloc_free(tmp_ctx);
 
@@ -2056,6 +2708,7 @@ static int replmd_modify_la_add(struct ldb_module *module,
   handle deleting all active linked attributes
  */
 static int replmd_modify_la_delete(struct ldb_module *module,
+                                  struct replmd_private *replmd_private,
                                   const struct dsdb_schema *schema,
                                   struct ldb_message *msg,
                                   struct ldb_message_element *el,
@@ -2063,111 +2716,232 @@ static int replmd_modify_la_delete(struct ldb_module *module,
                                   const struct dsdb_attribute *schema_attr,
                                   uint64_t seq_num,
                                   time_t t,
-                                  struct GUID *msg_guid,
+                                  struct ldb_dn *msg_dn,
                                   struct ldb_request *parent)
 {
        unsigned int i;
        struct parsed_dn *dns, *old_dns;
-       TALLOC_CTX *tmp_ctx = talloc_new(msg);
+       TALLOC_CTX *tmp_ctx = NULL;
        int ret;
-       const struct GUID *invocation_id;
        struct ldb_context *ldb = ldb_module_get_ctx(module);
+       struct ldb_control *vanish_links_ctrl = NULL;
+       bool vanish_links = false;
+       unsigned int num_to_delete = el->num_values;
+       uint32_t rmd_flags;
+       const struct GUID *invocation_id;
        NTTIME now;
 
        unix_to_nt_time(&now, t);
 
-       /* check if there is nothing to delete */
-       if ((!old_el || old_el->num_values == 0) &&
-           el->num_values == 0) {
-               return LDB_SUCCESS;
+       invocation_id = samdb_ntds_invocation_id(ldb);
+       if (!invocation_id) {
+               return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       if (!old_el || old_el->num_values == 0) {
+       if (old_el == NULL || old_el->num_values == 0) {
+               /* there is nothing to delete... */
+               if (num_to_delete == 0) {
+                       /* and we're deleting nothing, so that's OK */
+                       return LDB_SUCCESS;
+               }
                return LDB_ERR_NO_SUCH_ATTRIBUTE;
        }
 
-       ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
-       if (ret != LDB_SUCCESS) {
-               talloc_free(tmp_ctx);
-               return ret;
+       tmp_ctx = talloc_new(msg);
+       if (tmp_ctx == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
+       ret = get_parsed_dns(module, tmp_ctx, el, &dns,
+                            schema_attr->syntax->ldap_oid, parent);
        if (ret != LDB_SUCCESS) {
                talloc_free(tmp_ctx);
                return ret;
        }
 
-       invocation_id = samdb_ntds_invocation_id(ldb);
-       if (!invocation_id) {
-               return LDB_ERR_OPERATIONS_ERROR;
-       }
+       ret = get_parsed_dns_trusted(module, replmd_private,
+                                    tmp_ctx, old_el, &old_dns,
+                                    schema_attr->syntax->ldap_oid, parent);
 
-       ret = replmd_check_upgrade_links(old_dns, old_el->num_values, old_el, invocation_id);
        if (ret != LDB_SUCCESS) {
                talloc_free(tmp_ctx);
                return ret;
        }
 
+       if (parent) {
+               vanish_links_ctrl = ldb_request_get_control(parent, DSDB_CONTROL_REPLMD_VANISH_LINKS);
+               if (vanish_links_ctrl) {
+                       vanish_links = true;
+                       vanish_links_ctrl->critical = false;
+               }
+       }
+
+       /* we empty out el->values here to avoid damage if we return early. */
+       el->num_values = 0;
        el->values = NULL;
 
-       /* see if we are being asked to delete any links that
-          don't exist or are already deleted */
-       for (i=0; i<el->num_values; i++) {
-               struct parsed_dn *p = &dns[i];
-               struct parsed_dn *p2;
-               uint32_t rmd_flags;
+       /*
+        * If vanish links is set, we are actually removing members of
+        *  old_el->values; otherwise we are just marking them deleted.
+        *
+        * There is a special case when no values are given: we remove them
+        * all. When we have the vanish_links control we just have to remove
+        * the backlinks and change our element to replace the existing values
+        * with the empty list.
+        */
 
-               p2 = parsed_dn_find(old_dns, old_el->num_values, p->guid, NULL);
-               if (!p2) {
-                       ldb_asprintf_errstring(ldb, "Attribute %s doesn't exist for target GUID %s",
-                                              el->name, GUID_string(tmp_ctx, p->guid));
-                       if (ldb_attr_cmp(el->name, "member") == 0) {
-                               return LDB_ERR_UNWILLING_TO_PERFORM;
-                       } else {
-                               return LDB_ERR_NO_SUCH_ATTRIBUTE;
+       if (num_to_delete == 0) {
+               for (i = 0; i < old_el->num_values; i++) {
+                       struct parsed_dn *p = &old_dns[i];
+                       if (p->dsdb_dn == NULL) {
+                               ret = really_parse_trusted_dn(tmp_ctx, ldb, p,
+                                                             schema_attr->syntax->ldap_oid);
+                               if (ret != LDB_SUCCESS) {
+                                       return ret;
+                               }
+                       }
+                       ret = replmd_add_backlink(module, replmd_private,
+                                                 schema, msg_dn, &p->guid,
+                                                 false, schema_attr,
+                                                 parent);
+                       if (ret != LDB_SUCCESS) {
+                               talloc_free(tmp_ctx);
+                               return ret;
+                       }
+                       if (vanish_links) {
+                               continue;
+                       }
+
+                       rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
+                       if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
+                               continue;
+                       }
+
+                       ret = replmd_update_la_val(old_el->values, p->v,
+                                                  p->dsdb_dn, p->dsdb_dn,
+                                                  invocation_id, seq_num,
+                                                  seq_num, now, 0, true);
+                       if (ret != LDB_SUCCESS) {
+                               talloc_free(tmp_ctx);
+                               return ret;
                        }
                }
-               rmd_flags = dsdb_dn_rmd_flags(p2->dsdb_dn->dn);
-               if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
-                       ldb_asprintf_errstring(ldb, "Attribute %s already deleted for target GUID %s",
-                                              el->name, GUID_string(tmp_ctx, p->guid));
+
+               if (vanish_links) {
+                       el->flags = LDB_FLAG_MOD_REPLACE;
+                       talloc_free(tmp_ctx);
+                       return LDB_SUCCESS;
+               }
+       }
+
+
+       for (i = 0; i < num_to_delete; i++) {
+               struct parsed_dn *p = &dns[i];
+               struct parsed_dn *exact = NULL;
+               struct parsed_dn *next = NULL;
+               ret = parsed_dn_find(ldb, old_dns, old_el->num_values,
+                                    &p->guid,
+                                    NULL,
+                                    &exact, &next,
+                                    schema_attr->syntax->ldap_oid);
+               if (ret != LDB_SUCCESS) {
+                       talloc_free(tmp_ctx);
+                       return ret;
+               }
+               if (exact == NULL) {
+                       struct GUID_txt_buf buf;
+                       ldb_asprintf_errstring(ldb, "Attribute %s doesn't "
+                                              "exist for target GUID %s",
+                                              el->name,
+                                              GUID_buf_string(&p->guid, &buf));
                        if (ldb_attr_cmp(el->name, "member") == 0) {
+                               talloc_free(tmp_ctx);
                                return LDB_ERR_UNWILLING_TO_PERFORM;
                        } else {
+                               talloc_free(tmp_ctx);
                                return LDB_ERR_NO_SUCH_ATTRIBUTE;
                        }
                }
-       }
 
-       /* for each new value, see if it exists already with the same GUID
-          if it is not already deleted and matches the delete list then delete it
-       */
-       for (i=0; i<old_el->num_values; i++) {
-               struct parsed_dn *p = &old_dns[i];
-               uint32_t rmd_flags;
+               if (vanish_links) {
+                       if (CHECK_DEBUGLVL(5)) {
+                               rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
+                               if ((rmd_flags & DSDB_RMD_FLAG_DELETED)) {
+                                       struct GUID_txt_buf buf;
+                                       const char *guid_str = \
+                                               GUID_buf_string(&p->guid, &buf);
+                                       DEBUG(5, ("Deleting deleted linked "
+                                                 "attribute %s to %s, because "
+                                                 "vanish_links control is set\n",
+                                                 el->name, guid_str));
+                               }
+                       }
 
-               if (el->num_values && parsed_dn_find(dns, el->num_values, p->guid, NULL) == NULL) {
+                       /* remove the backlink */
+                       ret = replmd_add_backlink(module,
+                                                 replmd_private,
+                                                 schema, 
+                                                 msg_dn,
+                                                 &p->guid,
+                                                 false, schema_attr,
+                                                 parent);
+                       if (ret != LDB_SUCCESS) {
+                               talloc_free(tmp_ctx);
+                               return ret;
+                       }
+
+                       /* We flag the deletion and tidy it up later. */
+                       exact->v = NULL;
                        continue;
                }
 
-               rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
-               if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
+               rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);
 
-               ret = replmd_update_la_val(old_el->values, p->v, p->dsdb_dn, p->dsdb_dn,
-                                          invocation_id, seq_num, seq_num, now, 0, true);
+               if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
+                       struct GUID_txt_buf buf;
+                       const char *guid_str = GUID_buf_string(&p->guid, &buf);
+                       ldb_asprintf_errstring(ldb, "Attribute %s already "
+                                              "deleted for target GUID %s",
+                                              el->name, guid_str);
+                       if (ldb_attr_cmp(el->name, "member") == 0) {
+                               talloc_free(tmp_ctx);
+                               return LDB_ERR_UNWILLING_TO_PERFORM;
+                       } else {
+                               talloc_free(tmp_ctx);
+                               return LDB_ERR_NO_SUCH_ATTRIBUTE;
+                       }
+               }
+
+               ret = replmd_update_la_val(old_el->values, exact->v,
+                                          exact->dsdb_dn, exact->dsdb_dn,
+                                          invocation_id, seq_num, seq_num,
+                                          now, 0, true);
                if (ret != LDB_SUCCESS) {
                        talloc_free(tmp_ctx);
                        return ret;
                }
-
-               ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, true);
+               ret = replmd_add_backlink(module, replmd_private,
+                                         schema, msg_dn,
+                                         &p->guid,
+                                         false, schema_attr,
+                                         parent);
                if (ret != LDB_SUCCESS) {
                        talloc_free(tmp_ctx);
                        return ret;
                }
        }
 
+       if (vanish_links) {
+               unsigned j = 0;
+               for (i = 0; i < old_el->num_values; i++) {
+                       if (old_dns[i].v != NULL) {
+                               old_el->values[j] = *old_dns[i].v;
+                               j++;
+                       }
+               }
+               old_el->num_values = j;
+       }
+
        el->values = talloc_steal(msg->elements, old_el->values);
        el->num_values = old_el->num_values;
 
@@ -2184,6 +2958,7 @@ static int replmd_modify_la_delete(struct ldb_module *module,
   handle replacing a linked attribute
  */
 static int replmd_modify_la_replace(struct ldb_module *module,
+                                   struct replmd_private *replmd_private,
                                    const struct dsdb_schema *schema,
                                    struct ldb_message *msg,
                                    struct ldb_message_element *el,
@@ -2191,143 +2966,217 @@ static int replmd_modify_la_replace(struct ldb_module *module,
                                    const struct dsdb_attribute *schema_attr,
                                    uint64_t seq_num,
                                    time_t t,
-                                   struct GUID *msg_guid,
+                                   struct ldb_dn *msg_dn,
                                    struct ldb_request *parent)
 {
-       unsigned int i;
+       unsigned int i, old_i, new_i;
        struct parsed_dn *dns, *old_dns;
        TALLOC_CTX *tmp_ctx = talloc_new(msg);
        int ret;
        const struct GUID *invocation_id;
        struct ldb_context *ldb = ldb_module_get_ctx(module);
        struct ldb_val *new_values = NULL;
-       unsigned int num_new_values = 0;
-       unsigned int old_num_values = old_el?old_el->num_values:0;
+       const char *ldap_oid = schema_attr->syntax->ldap_oid;
+       unsigned int old_num_values;
+       unsigned int repl_num_values;
+       unsigned int max_num_values;
        NTTIME now;
 
        unix_to_nt_time(&now, t);
 
-       /* check if there is nothing to replace */
-       if ((!old_el || old_el->num_values == 0) &&
-           el->num_values == 0) {
+       invocation_id = samdb_ntds_invocation_id(ldb);
+       if (!invocation_id) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       /*
+        * The replace operation is unlike the replace and delete cases in that
+        * we need to look at every existing link to see whether it is being
+        * retained or deleted. In other words, we can't avoid parsing the GUIDs.
+        *
+        * As we are trying to combine two sorted lists, the algorithm we use
+        * is akin to the merge phase of a merge sort. We interleave the two
+        * lists, doing different things depending on which side the current
+        * item came from.
+        *
+        * There are three main cases, with some sub-cases.
+        *
+        *  - a DN is in the old list but not the new one. It needs to be
+        *    marked as deleted (but left in the list).
+        *     - maybe it is already deleted, and we have less to do.
+        *
+        *  - a DN is in both lists. The old data gets replaced by the new,
+        *    and the list doesn't grow. The old link may have been marked as
+        *    deleted, in which case we undelete it.
+        *
+        *  - a DN is in the new list only. We add it in the right place.
+        */
+
+       old_num_values = old_el ? old_el->num_values : 0;
+       repl_num_values = el->num_values;
+       max_num_values = old_num_values + repl_num_values;
+
+       if (max_num_values == 0) {
+               /* There is nothing to do! */
                return LDB_SUCCESS;
        }
 
-       ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
+       ret = get_parsed_dns(module, tmp_ctx, el, &dns, ldap_oid, parent);
        if (ret != LDB_SUCCESS) {
                talloc_free(tmp_ctx);
                return ret;
        }
 
-       ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns, schema_attr->syntax->ldap_oid, parent);
+       ret = get_parsed_dns(module, tmp_ctx, old_el, &old_dns,
+                            ldap_oid, parent);
        if (ret != LDB_SUCCESS) {
                talloc_free(tmp_ctx);
                return ret;
        }
 
-       invocation_id = samdb_ntds_invocation_id(ldb);
-       if (!invocation_id) {
-               return LDB_ERR_OPERATIONS_ERROR;
-       }
-
-       ret = replmd_check_upgrade_links(old_dns, old_num_values, old_el, invocation_id);
+       ret = replmd_check_upgrade_links(ldb, old_dns, old_num_values,
+                                        old_el, ldap_oid);
        if (ret != LDB_SUCCESS) {
                talloc_free(tmp_ctx);
                return ret;
        }
 
-       /* mark all the old ones as deleted */
-       for (i=0; i<old_num_values; i++) {
-               struct parsed_dn *old_p = &old_dns[i];
-               struct parsed_dn *p;
-               uint32_t rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
-
-               if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
-
-               ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, false);
-               if (ret != LDB_SUCCESS) {
-                       talloc_free(tmp_ctx);
-                       return ret;
-               }
+       new_values = talloc_array(tmp_ctx, struct ldb_val, max_num_values);
+       if (new_values == NULL) {
+               ldb_module_oom(module);
+               talloc_free(tmp_ctx);
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
 
-               p = parsed_dn_find(dns, el->num_values, old_p->guid, NULL);
-               if (p) {
-                       /* we don't delete it if we are re-adding it */
-                       continue;
+       old_i = 0;
+       new_i = 0;
+       for (i = 0; i < max_num_values; i++) {
+               int cmp;
+               struct parsed_dn *old_p, *new_p;
+               if (old_i < old_num_values && new_i < repl_num_values) {
+                       old_p = &old_dns[old_i];
+                       new_p = &dns[new_i];
+                       cmp = parsed_dn_compare(old_p, new_p);
+               } else if (old_i < old_num_values) {
+                       /* the new list is empty, read the old list */
+                       old_p = &old_dns[old_i];
+                       new_p = NULL;
+                       cmp = -1;
+               } else if (new_i < repl_num_values) {
+                       /* the old list is empty, read new list */
+                       old_p = NULL;
+                       new_p = &dns[new_i];
+                       cmp = 1;
+               } else {
+                       break;
                }
 
-               ret = replmd_update_la_val(old_el->values, old_p->v, old_p->dsdb_dn, old_p->dsdb_dn,
-                                          invocation_id, seq_num, seq_num, now, 0, true);
-               if (ret != LDB_SUCCESS) {
-                       talloc_free(tmp_ctx);
-                       return ret;
-               }
-       }
+               if (cmp < 0) {
+                       /*
+                        * An old ones that come before the next replacement
+                        * (if any). We mark it as deleted and add it to the
+                        * final list.
+                        */
+                       uint32_t rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
+                       if ((rmd_flags & DSDB_RMD_FLAG_DELETED) == 0) {
+                               ret = replmd_update_la_val(new_values, old_p->v,
+                                                          old_p->dsdb_dn,
+                                                          old_p->dsdb_dn,
+                                                          invocation_id,
+                                                          seq_num, seq_num,
+                                                          now, 0, true);
+                               if (ret != LDB_SUCCESS) {
+                                       talloc_free(tmp_ctx);
+                                       return ret;
+                               }
 
-       /* for each new value, either update its meta-data, or add it
-        * to old_el
-       */
-       for (i=0; i<el->num_values; i++) {
-               struct parsed_dn *p = &dns[i], *old_p;
-
-               if (old_dns &&
-                   (old_p = parsed_dn_find(old_dns,
-                                           old_num_values, p->guid, NULL)) != NULL) {
-                       /* update in place */
-                       ret = replmd_update_la_val(old_el->values, old_p->v, p->dsdb_dn,
-                                                  old_p->dsdb_dn, invocation_id,
-                                                  seq_num, seq_num, now, 0, false);
+                               ret = replmd_add_backlink(module, replmd_private,
+                                                         schema, 
+                                                         msg_dn,
+                                                         &old_p->guid, false,
+                                                         schema_attr,
+                                                         parent);
+                               if (ret != LDB_SUCCESS) {
+                                       talloc_free(tmp_ctx);
+                                       return ret;
+                               }
+                       }
+                       new_values[i] = *old_p->v;
+                       old_i++;
+               } else if (cmp == 0) {
+                       /*
+                        * We are overwriting one. If it was previously
+                        * deleted, we need to add a backlink.
+                        *
+                        * Note that if any RMD_FLAGs in an extended new DN
+                        * will be ignored.
+                        */
+                       uint32_t rmd_flags;
+
+                       ret = replmd_update_la_val(new_values, old_p->v,
+                                                  new_p->dsdb_dn,
+                                                  old_p->dsdb_dn,
+                                                  invocation_id,
+                                                  seq_num, seq_num,
+                                                  now, 0, false);
                        if (ret != LDB_SUCCESS) {
                                talloc_free(tmp_ctx);
                                return ret;
                        }
+
+                       rmd_flags = dsdb_dn_rmd_flags(old_p->dsdb_dn->dn);
+                       if ((rmd_flags & DSDB_RMD_FLAG_DELETED) != 0) {
+                               ret = replmd_add_backlink(module, replmd_private,
+                                                         schema, 
+                                                         msg_dn,
+                                                         &new_p->guid, true,
+                                                         schema_attr,
+                                                         parent);
+                               if (ret != LDB_SUCCESS) {
+                                       talloc_free(tmp_ctx);
+                                       return ret;
+                               }
+                       }
+
+                       new_values[i] = *old_p->v;
+                       old_i++;
+                       new_i++;
                } else {
-                       /* add a new one */
-                       new_values = talloc_realloc(tmp_ctx, new_values, struct ldb_val,
-                                                   num_new_values+1);
-                       if (new_values == NULL) {
-                               ldb_module_oom(module);
+                       /*
+                        * Replacements that don't match an existing one. We
+                        * just add them to the final list.
+                        */
+                       ret = replmd_build_la_val(new_values,
+                                                 new_p->v,
+                                                 new_p->dsdb_dn,
+                                                 invocation_id,
+                                                 seq_num, seq_num,
+                                                 now, 0, false);
+                       if (ret != LDB_SUCCESS) {
                                talloc_free(tmp_ctx);
-                               return LDB_ERR_OPERATIONS_ERROR;
+                               return ret;
                        }
-                       ret = replmd_build_la_val(new_values, &new_values[num_new_values], dns[i].dsdb_dn,
-                                                 invocation_id, seq_num, seq_num, now, 0, false);
+                       ret = replmd_add_backlink(module, replmd_private,
+                                                 schema,
+                                                 msg_dn,
+                                                 &new_p->guid, true,
+                                                 schema_attr,
+                                                 parent);
                        if (ret != LDB_SUCCESS) {
                                talloc_free(tmp_ctx);
                                return ret;
                        }
-                       num_new_values++;
-               }
-
-               ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, false);
-               if (ret != LDB_SUCCESS) {
-                       talloc_free(tmp_ctx);
-                       return ret;
+                       new_values[i] = *new_p->v;
+                       new_i++;
                }
        }
-
-       /* add the new values to the end of old_el */
-       if (num_new_values != 0) {
-               el->values = talloc_realloc(msg->elements, old_el?old_el->values:NULL,
-                                           struct ldb_val, old_num_values+num_new_values);
-               if (el->values == NULL) {
-                       ldb_module_oom(module);
-                       return LDB_ERR_OPERATIONS_ERROR;
-               }
-               memcpy(&el->values[old_num_values], &new_values[0],
-                      sizeof(struct ldb_val)*num_new_values);
-               el->num_values = old_num_values + num_new_values;
-               talloc_steal(msg->elements, new_values);
-       } else {
-               el->values = old_el->values;
-               el->num_values = old_el->num_values;
-               talloc_steal(msg->elements, el->values);
+       if (old_el != NULL) {
+               talloc_steal(msg->elements, old_el->values);
        }
-
+       el->values = talloc_steal(msg->elements, new_values);
+       el->num_values = i;
        talloc_free(tmp_ctx);
 
-       /* we now tell the backend to replace all existing values
-          with the one we have constructed */
        el->flags = LDB_FLAG_MOD_REPLACE;
 
        return LDB_SUCCESS;
@@ -2338,6 +3187,7 @@ static int replmd_modify_la_replace(struct ldb_module *module,
   handle linked attributes in modify requests
  */
 static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
+                                              struct replmd_private *replmd_private,
                                               struct ldb_message *msg,
                                               uint64_t seq_num, time_t t,
                                               struct ldb_request *parent)
@@ -2349,20 +3199,31 @@ static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
        struct ldb_message *old_msg;
 
        const struct dsdb_schema *schema;
-       struct GUID old_guid;
-
-       if (seq_num == 0) {
-               /* there the replmd_update_rpmd code has already
-                * checked and saw that there are no linked
-                * attributes */
-               return LDB_SUCCESS;
-       }
 
        if (dsdb_functional_level(ldb) == DS_DOMAIN_FUNCTION_2000) {
-               /* don't do anything special for linked attributes */
+               /*
+                * Nothing special is required for modifying or vanishing links
+                * in fl2000 since they are just strings in a multi-valued
+                * attribute.
+                */
+               struct ldb_control *ctrl = ldb_request_get_control(parent,
+                                                                  DSDB_CONTROL_REPLMD_VANISH_LINKS);
+               if (ctrl) {
+                       ctrl->critical = false;
+               }
                return LDB_SUCCESS;
        }
 
+       /*
+        * TODO:
+        *
+        * We should restrict this to the intersection of the list of
+        * linked attributes in the schema and the list of attributes
+        * being modified.
+        *
+        * This will help performance a little, as otherwise we have
+        * to allocate the entire object value-by-value.
+        */
        ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
                                    DSDB_FLAG_NEXT_MODULE |
                                    DSDB_SEARCH_SHOW_RECYCLED |
@@ -2379,8 +3240,6 @@ static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
 
        old_msg = res->msgs[0];
 
-       old_guid = samdb_result_guid(old_msg, "objectGUID");
-
        for (i=0; i<msg->num_elements; i++) {
                struct ldb_message_element *el = &msg->elements[i];
                struct ldb_message_element *old_el, *new_el;
@@ -2407,13 +3266,25 @@ static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
                old_el = ldb_msg_find_element(old_msg, el->name);
                switch (el->flags & LDB_FLAG_MOD_MASK) {
                case LDB_FLAG_MOD_REPLACE:
-                       ret = replmd_modify_la_replace(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
+                       ret = replmd_modify_la_replace(module, replmd_private,
+                                                      schema, msg, el, old_el,
+                                                      schema_attr, seq_num, t,
+                                                      old_msg->dn,
+                                                      parent);
                        break;
                case LDB_FLAG_MOD_DELETE:
-                       ret = replmd_modify_la_delete(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
+                       ret = replmd_modify_la_delete(module, replmd_private,
+                                                     schema, msg, el, old_el,
+                                                     schema_attr, seq_num, t,
+                                                     old_msg->dn,
+                                                     parent);
                        break;
                case LDB_FLAG_MOD_ADD:
-                       ret = replmd_modify_la_add(module, schema, msg, el, old_el, schema_attr, seq_num, t, &old_guid, parent);
+                       ret = replmd_modify_la_add(module, replmd_private,
+                                                  schema, msg, el, old_el,
+                                                  schema_attr, seq_num, t,
+                                                  old_msg->dn,
+                                                  parent);
                        break;
                default:
                        ldb_asprintf_errstring(ldb,
@@ -2460,7 +3331,6 @@ static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
 
 static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
 {
-       struct samldb_msds_intid_persistant *msds_intid_struct;
        struct ldb_context *ldb;
        struct replmd_replicated_request *ac;
        struct ldb_request *down_req;
@@ -2468,9 +3338,12 @@ static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
        time_t t = time(NULL);
        int ret;
        bool is_urgent = false, rodc = false;
+       bool is_schema_nc = false;
        unsigned int functional_level;
-       const DATA_BLOB *guid_blob;
+       const struct ldb_message_element *guid_el = NULL;
        struct ldb_control *sd_propagation_control;
+       struct replmd_private *replmd_private =
+               talloc_get_type(ldb_module_get_private(module), struct replmd_private);
 
        /* do not manipulate our control entries */
        if (ldb_dn_is_special(req->op.mod.message->dn)) {
@@ -2496,8 +3369,8 @@ static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
 
        ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");
 
-       guid_blob = ldb_msg_find_ldb_val(req->op.mod.message, "objectGUID");
-       if ( guid_blob != NULL ) {
+       guid_el = ldb_msg_find_element(req->op.mod.message, "objectGUID");
+       if (guid_el != NULL) {
                ldb_set_errstring(ldb,
                                  "replmd_modify: it's not allowed to change the objectGUID!");
                return LDB_ERR_CONSTRAINT_VIOLATION;
@@ -2521,8 +3394,11 @@ static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
        ldb_msg_remove_attr(msg, "whenChanged");
        ldb_msg_remove_attr(msg, "uSNChanged");
 
+       is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
+
        ret = replmd_update_rpmd(module, ac->schema, req, NULL,
-                                msg, &ac->seq_num, t, &is_urgent, &rodc);
+                                msg, &ac->seq_num, t, is_schema_nc,
+                                &is_urgent, &rodc);
        if (rodc && (ret == LDB_ERR_REFERRAL)) {
                struct loadparm_context *lp_ctx;
                char *referral;
@@ -2544,7 +3420,8 @@ static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
                return ret;
        }
 
-       ret = replmd_modify_handle_linked_attribs(module, msg, ac->seq_num, t, req);
+       ret = replmd_modify_handle_linked_attribs(module, replmd_private,
+                                                 msg, ac->seq_num, t, req);
        if (ret != LDB_SUCCESS) {
                talloc_free(ac);
                return ret;
@@ -2609,14 +3486,6 @@ static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
                }
        }
 
-       if (!ldb_dn_compare_base(ac->schema->base_dn, msg->dn)) {
-               /* Update the usn in the SAMLDB_MSDS_INTID_OPAQUE opaque */
-               msds_intid_struct = (struct samldb_msds_intid_persistant *) ldb_get_opaque(ldb, SAMLDB_MSDS_INTID_OPAQUE);
-               if (msds_intid_struct) {
-                       msds_intid_struct->usn = ac->seq_num;
-               }
-       }
-
        /* go on with the call chain */
        return ldb_next_request(module, down_req);
 }
@@ -2670,7 +3539,6 @@ static int replmd_rename(struct ldb_module *module, struct ldb_request *req)
 static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
 {
        struct ldb_context *ldb;
-       struct replmd_replicated_request *ac;
        struct ldb_request *down_req;
        struct ldb_message *msg;
        const struct dsdb_attribute *rdn_attr;
@@ -2680,8 +3548,13 @@ static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *are
        time_t t = time(NULL);
        int ret;
        bool is_urgent = false, rodc = false;
+       bool is_schema_nc;
+       struct replmd_replicated_request *ac =
+               talloc_get_type(req->context, struct replmd_replicated_request);
+       struct replmd_private *replmd_private =
+               talloc_get_type(ldb_module_get_private(ac->module),
+                               struct replmd_private);
 
-       ac = talloc_get_type(req->context, struct replmd_replicated_request);
        ldb = ldb_module_get_ctx(ac->module);
 
        if (ares->error != LDB_SUCCESS) {
@@ -2709,6 +3582,8 @@ static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *are
 
        msg->dn = ac->req->op.rename.newdn;
 
+       is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
+
        rdn_name = ldb_dn_get_rdn_name(msg->dn);
        if (rdn_name == NULL) {
                talloc_free(ares);
@@ -2794,7 +3669,8 @@ static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *are
        attrs[4] = NULL;
 
        ret = replmd_update_rpmd(ac->module, ac->schema, req, attrs,
-                                msg, &ac->seq_num, t, &is_urgent, &rodc);
+                                msg, &ac->seq_num, t,
+                                is_schema_nc, &is_urgent, &rodc);
        if (rodc && (ret == LDB_ERR_REFERRAL)) {
                struct ldb_dn *olddn = ac->req->op.rename.olddn;
                struct loadparm_context *lp_ctx;
@@ -2876,7 +3752,9 @@ static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *are
  */
 static int replmd_delete_remove_link(struct ldb_module *module,
                                     const struct dsdb_schema *schema,
+                                    struct replmd_private *replmd_private,
                                     struct ldb_dn *dn,
+                                    struct GUID *guid,
                                     struct ldb_message_element *el,
                                     const struct dsdb_attribute *sa,
                                     struct ldb_request *parent)
@@ -2887,13 +3765,19 @@ static int replmd_delete_remove_link(struct ldb_module *module,
 
        for (i=0; i<el->num_values; i++) {
                struct dsdb_dn *dsdb_dn;
-               NTSTATUS status;
                int ret;
-               struct GUID guid2;
                struct ldb_message *msg;
                const struct dsdb_attribute *target_attr;
                struct ldb_message_element *el2;
+               const char *dn_str;
                struct ldb_val dn_val;
+               uint32_t dsdb_flags = 0;
+               const char *attrs[] = { NULL, NULL };
+               struct ldb_result *link_res;
+               struct ldb_message *link_msg;
+               struct ldb_message_element *link_el;
+               struct parsed_dn *link_dns;
+               struct parsed_dn *p = NULL, *unused = NULL;
 
                if (dsdb_dn_is_deleted_val(&el->values[i])) {
                        continue;
@@ -2905,12 +3789,6 @@ static int replmd_delete_remove_link(struct ldb_module *module,
                        return LDB_ERR_OPERATIONS_ERROR;
                }
 
-               status = dsdb_get_extended_dn_guid(dsdb_dn->dn, &guid2, "GUID");
-               if (!NT_STATUS_IS_OK(status)) {
-                       talloc_free(tmp_ctx);
-                       return LDB_ERR_OPERATIONS_ERROR;
-               }
-
                /* remove the link */
                msg = ldb_msg_new(tmp_ctx);
                if (!msg) {
@@ -2926,18 +3804,84 @@ static int replmd_delete_remove_link(struct ldb_module *module,
                if (target_attr == NULL) {
                        continue;
                }
+               attrs[0] = target_attr->lDAPDisplayName;
 
-               ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName, LDB_FLAG_MOD_DELETE, &el2);
+               ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName,
+                                       LDB_FLAG_MOD_DELETE, &el2);
                if (ret != LDB_SUCCESS) {
                        ldb_module_oom(module);
                        talloc_free(tmp_ctx);
                        return LDB_ERR_OPERATIONS_ERROR;
                }
-               dn_val = data_blob_string_const(ldb_dn_get_linearized(dn));
+
+               ret = dsdb_module_search_dn(module, tmp_ctx, &link_res,
+                                           msg->dn, attrs,
+                                           DSDB_FLAG_NEXT_MODULE |
+                                           DSDB_SEARCH_SHOW_EXTENDED_DN,
+                                           parent);
+
+               if (ret != LDB_SUCCESS) {
+                       talloc_free(tmp_ctx);
+                       return ret;
+               }
+
+               link_msg = link_res->msgs[0];
+               link_el = ldb_msg_find_element(link_msg,
+                                              target_attr->lDAPDisplayName);
+               if (link_el == NULL) {
+                       talloc_free(tmp_ctx);
+                       return LDB_ERR_NO_SUCH_ATTRIBUTE;
+               }
+
+               /*
+                * This call 'upgrades' the links in link_dns, but we
+                * do not commit the result back into the database, so
+                * this is safe to call in FL2000 or on databases that
+                * have been run at that level in the past.
+                */
+               ret = get_parsed_dns_trusted(module, replmd_private, tmp_ctx,
+                                            link_el, &link_dns,
+                                            target_attr->syntax->ldap_oid, parent);
+               if (ret != LDB_SUCCESS) {
+                       talloc_free(tmp_ctx);
+                       return ret;
+               }
+
+               ret = parsed_dn_find(ldb, link_dns, link_el->num_values,
+                                    guid, dn, &p, &unused,
+                                    target_attr->syntax->ldap_oid);
+               if (ret != LDB_SUCCESS) {
+                       talloc_free(tmp_ctx);
+                       return ret;
+               }
+
+               if (p == NULL) {
+                       ldb_asprintf_errstring(ldb_module_get_ctx(module),
+                                              "Failed to find forward link on %s "
+                                              "as %s to remove backlink %s on %s",
+                                              ldb_dn_get_linearized(msg->dn),
+                                              target_attr->lDAPDisplayName,
+                                              sa->lDAPDisplayName,
+                                              ldb_dn_get_linearized(dn));
+                       talloc_free(tmp_ctx);
+                       return LDB_ERR_NO_SUCH_ATTRIBUTE;
+               }
+
+
+               /* This needs to get the Binary DN, by first searching */
+               dn_str = dsdb_dn_get_linearized(tmp_ctx,
+                                               p->dsdb_dn);
+               dn_val = data_blob_string_const(dn_str);
                el2->values = &dn_val;
                el2->num_values = 1;
 
-               ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE, parent);
+               /*
+                * Ensure that we tell the modification to vanish any linked
+                * attributes (not simply mark them as isDeleted = TRUE)
+                */
+               dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
+
+               ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, parent);
                if (ret != LDB_SUCCESS) {
                        talloc_free(tmp_ctx);
                        return ret;
@@ -2973,24 +3917,80 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request
        struct ldb_message_element *el;
        TALLOC_CTX *tmp_ctx;
        struct ldb_result *res, *parent_res;
-       const char *preserved_attrs[] = {
+       static const char * const preserved_attrs[] = {
                /* yes, this really is a hard coded list. See MS-ADTS
                   section 3.1.1.5.5.1.1 */
-               "nTSecurityDescriptor", "attributeID", "attributeSyntax", "dNReferenceUpdate", "dNSHostName",
-               "flatName", "governsID", "groupType", "instanceType", "lDAPDisplayName", "legacyExchangeDN",
-               "isDeleted", "isRecycled", "lastKnownParent", "msDS-LastKnownRDN", "mS-DS-CreatorSID",
-               "mSMQOwnerID", "nCName", "objectClass", "distinguishedName", "objectGUID", "objectSid",
-               "oMSyntax", "proxiedObjectName", "name", "replPropertyMetaData", "sAMAccountName",
-               "securityIdentifier", "sIDHistory", "subClassOf", "systemFlags", "trustPartner", "trustDirection",
-               "trustType", "trustAttributes", "userAccountControl", "uSNChanged", "uSNCreated", "whenCreated",
-               "whenChanged", NULL};
+               "attributeID",
+               "attributeSyntax",
+               "dNReferenceUpdate",
+               "dNSHostName",
+               "flatName",
+               "governsID",
+               "groupType",
+               "instanceType",
+               "lDAPDisplayName",
+               "legacyExchangeDN",
+               "isDeleted",
+               "isRecycled",
+               "lastKnownParent",
+               "msDS-LastKnownRDN",
+               "msDS-PortLDAP",
+               "mS-DS-CreatorSID",
+               "mSMQOwnerID",
+               "nCName",
+               "objectClass",
+               "distinguishedName",
+               "objectGUID",
+               "objectSid",
+               "oMSyntax",
+               "proxiedObjectName",
+               "name",
+               "nTSecurityDescriptor",
+               "replPropertyMetaData",
+               "sAMAccountName",
+               "securityIdentifier",
+               "sIDHistory",
+               "subClassOf",
+               "systemFlags",
+               "trustPartner",
+               "trustDirection",
+               "trustType",
+               "trustAttributes",
+               "userAccountControl",
+               "uSNChanged",
+               "uSNCreated",
+               "whenCreated",
+               "whenChanged",
+               NULL
+       };
+       static const char * const all_attrs[] = {
+               DSDB_SECRET_ATTRIBUTES,
+               "*",
+               NULL
+       };
        unsigned int i, el_count = 0;
+       uint32_t dsdb_flags = 0;
+       struct replmd_private *replmd_private;
        enum deletion_state deletion_state, next_deletion_state;
 
        if (ldb_dn_is_special(req->op.del.dn)) {
                return ldb_next_request(module, req);
        }
 
+       /*
+        * We have to allow dbcheck to remove an object that
+        * is beyond repair, and to do so totally.  This could
+        * mean we we can get a partial object from the other
+        * DC, causing havoc, so dbcheck suggests
+        * re-replication first.  dbcheck sets both DBCHECK
+        * and RELAX in this situation.
+        */
+       if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)
+           && ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
+               /* really, really remove it */
+               return ldb_next_request(module, req);
+       }
+
        tmp_ctx = talloc_new(ldb);
        if (!tmp_ctx) {
                ldb_oom(ldb);
@@ -3007,7 +4007,7 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request
 
        /* we need the complete msg off disk, so we can work out which
           attributes need to be removed */
-       ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, NULL,
+       ret = dsdb_module_search_dn(module, tmp_ctx, &res, old_dn, all_attrs,
                                    DSDB_FLAG_NEXT_MODULE |
                                    DSDB_SEARCH_SHOW_RECYCLED |
                                    DSDB_SEARCH_REVEAL_INTERNALS |
@@ -3034,17 +4034,25 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request
        }
 
        if (next_deletion_state == OBJECT_REMOVED) {
-               struct auth_session_info *session_info =
-                               (struct auth_session_info *)ldb_get_opaque(ldb, "sessionInfo");
-               if (security_session_user_level(session_info, NULL) != SECURITY_SYSTEM) {
-                       ldb_asprintf_errstring(ldb, "Refusing to delete deleted object %s",
-                                       ldb_dn_get_linearized(old_msg->dn));
-                       return LDB_ERR_UNWILLING_TO_PERFORM;
+               /*
+                * We have to prevent objects being deleted, even if
+                * the administrator really wants them gone, as
+                * without the tombstone, we can get a partial object
+                * from the other DC, causing havoc.
+                *
+                * The only other valid case is when the 180 day
+                * timeout has expired, when relax is specified.
+                */
+               if (ldb_request_get_control(req, LDB_CONTROL_RELAX_OID)) {
+                       /* it is already deleted - really remove it this time */
+                       talloc_free(tmp_ctx);
+                       return ldb_next_request(module, req);
                }
 
-               /* it is already deleted - really remove it this time */
-               talloc_free(tmp_ctx);
-               return ldb_next_request(module, req);
+               ldb_asprintf_errstring(ldb, "Refusing to delete tombstone object %s.  "
+                                      "This check is to prevent corruption of the replicated state.",
+                                      ldb_dn_get_linearized(old_msg->dn));
+               return LDB_ERR_UNWILLING_TO_PERFORM;
        }
 
        rdn_name = ldb_dn_get_rdn_name(old_dn);
@@ -3107,26 +4115,27 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request
                }
        }
 
-       if (deletion_state == OBJECT_NOT_DELETED) {
-               /* get the objects GUID from the search we just did */
-               guid = samdb_result_guid(old_msg, "objectGUID");
+       /* get the objects GUID from the search we just did */
+       guid = samdb_result_guid(old_msg, "objectGUID");
 
+       if (deletion_state == OBJECT_NOT_DELETED) {
                /* Add a formatted child */
                retb = ldb_dn_add_child_fmt(new_dn, "%s=%s\\0ADEL:%s",
                                            rdn_name,
                                            ldb_dn_escape_value(tmp_ctx, *rdn_value),
                                            GUID_string(tmp_ctx, &guid));
                if (!retb) {
-                       DEBUG(0,(__location__ ": Unable to add a formatted child to dn: %s",
-                                ldb_dn_get_linearized(new_dn)));
+                       ldb_asprintf_errstring(ldb, __location__
+                                              ": Unable to add a formatted child to dn: %s",
+                                              ldb_dn_get_linearized(new_dn));
                        talloc_free(tmp_ctx);
                        return LDB_ERR_OPERATIONS_ERROR;
                }
 
                ret = ldb_msg_add_string(msg, "isDeleted", "TRUE");
                if (ret != LDB_SUCCESS) {
-                       DEBUG(0,(__location__ ": Failed to add isDeleted string to the msg\n"));
-                       ldb_module_oom(module);
+                       ldb_asprintf_errstring(ldb, __location__
+                                              ": Failed to add isDeleted string to the msg");
                        talloc_free(tmp_ctx);
                        return ret;
                }
@@ -3140,8 +4149,9 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request
                struct ldb_dn *rdn = ldb_dn_copy(tmp_ctx, old_dn);
                retb = ldb_dn_remove_base_components(rdn, ldb_dn_get_comp_num(rdn) - 1);
                if (!retb) {
-                       DEBUG(0,(__location__ ": Unable to add a prepare rdn of %s",
-                                ldb_dn_get_linearized(rdn)));
+                       ldb_asprintf_errstring(ldb, __location__
+                                              ": Unable to add a prepare rdn of %s",
+                                              ldb_dn_get_linearized(rdn));
                        talloc_free(tmp_ctx);
                        return LDB_ERR_OPERATIONS_ERROR;
                }
@@ -3149,9 +4159,10 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request
 
                retb = ldb_dn_add_child(new_dn, rdn);
                if (!retb) {
-                       DEBUG(0,(__location__ ": Unable to add rdn %s to base dn: %s",
-                                ldb_dn_get_linearized(rdn),
-                                ldb_dn_get_linearized(new_dn)));
+                       ldb_asprintf_errstring(ldb, __location__
+                                              ": Unable to add rdn %s to base dn: %s",
+                                              ldb_dn_get_linearized(rdn),
+                                              ldb_dn_get_linearized(new_dn));
                        talloc_free(tmp_ctx);
                        return LDB_ERR_OPERATIONS_ERROR;
                }
@@ -3175,29 +4186,48 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request
         */
 
        if (deletion_state == OBJECT_NOT_DELETED) {
+               struct ldb_dn *parent_dn = ldb_dn_get_parent(tmp_ctx, old_dn);
+               char *parent_dn_str = NULL;
+
                /* we need the storage form of the parent GUID */
                ret = dsdb_module_search_dn(module, tmp_ctx, &parent_res,
-                                           ldb_dn_get_parent(tmp_ctx, old_dn), NULL,
+                                           parent_dn, NULL,
                                            DSDB_FLAG_NEXT_MODULE |
                                            DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
                                            DSDB_SEARCH_REVEAL_INTERNALS|
                                            DSDB_SEARCH_SHOW_RECYCLED, req);
                if (ret != LDB_SUCCESS) {
                        ldb_asprintf_errstring(ldb_module_get_ctx(module),
-                                              "repmd_delete: Failed to %s %s, because we failed to find it's parent (%s): %s",
+                                              "repmd_delete: Failed to %s %s, "
+                                              "because we failed to find it's parent (%s): %s",
                                               re_delete ? "re-delete" : "delete",
                                               ldb_dn_get_linearized(old_dn),
-                                              ldb_dn_get_linearized(ldb_dn_get_parent(tmp_ctx, old_dn)),
+                                              ldb_dn_get_linearized(parent_dn),
                                               ldb_errstring(ldb_module_get_ctx(module)));
                        talloc_free(tmp_ctx);
                        return ret;
                }
 
+               /*
+                * Now we can use the DB version,
+                * it will have the extended DN info in it
+                */
+               parent_dn = parent_res->msgs[0]->dn;
+               parent_dn_str = ldb_dn_get_extended_linearized(tmp_ctx,
+                                                              parent_dn,
+                                                              1);
+               if (parent_dn_str == NULL) {
+                       talloc_free(tmp_ctx);
+                       return ldb_module_oom(module);
+               }
+
                ret = ldb_msg_add_steal_string(msg, "lastKnownParent",
-                                                  ldb_dn_get_extended_linearized(tmp_ctx, parent_res->msgs[0]->dn, 1));
+                                              parent_dn_str);
                if (ret != LDB_SUCCESS) {
-                       DEBUG(0,(__location__ ": Failed to add lastKnownParent string to the msg\n"));
-                       ldb_module_oom(module);
+                       ldb_asprintf_errstring(ldb, __location__
+                                              ": Failed to add lastKnownParent "
+                                              "string when deleting %s",
+                                              ldb_dn_get_linearized(old_dn));
                        talloc_free(tmp_ctx);
                        return ret;
                }
@@ -3206,8 +4236,10 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request
                if (next_deletion_state == OBJECT_DELETED) {
                        ret = ldb_msg_add_value(msg, "msDS-LastKnownRDN", rdn_value, NULL);
                        if (ret != LDB_SUCCESS) {
-                               DEBUG(0,(__location__ ": Failed to add msDS-LastKnownRDN string to the msg\n"));
-                               ldb_module_oom(module);
+                               ldb_asprintf_errstring(ldb, __location__
+                                                      ": Failed to add msDS-LastKnownRDN "
+                                                      "string when deleting %s",
+                                                      ldb_dn_get_linearized(old_dn));
                                talloc_free(tmp_ctx);
                                return ret;
                        }
@@ -3250,6 +4282,8 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request
                        msg->elements[el_count++].flags = LDB_FLAG_MOD_REPLACE;
                }
 
+               replmd_private = talloc_get_type(ldb_module_get_private(module),
+                                                struct replmd_private);
                /* work out which of the old attributes we will be removing */
                for (i=0; i<old_msg->num_elements; i++) {
                        const struct dsdb_attribute *sa;
@@ -3263,7 +4297,7 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request
                                /* don't remove the rDN */
                                continue;
                        }
-                       if (sa->linkID && (sa->linkID & 1)) {
+                       if (sa->linkID & 1) {
                                /*
                                  we have a backlink in this object
                                  that needs to be removed. We're not
@@ -3272,8 +4306,20 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request
                                  modify to delete the corresponding
                                  forward link
                                 */
-                               ret = replmd_delete_remove_link(module, schema, old_dn, el, sa, req);
+                               ret = replmd_delete_remove_link(module, schema,
+                                                               replmd_private,
+                                                               old_dn, &guid,
+                                                               el, sa, req);
                                if (ret != LDB_SUCCESS) {
+                                       const char *old_dn_str
+                                               = ldb_dn_get_linearized(old_dn);
+                                       ldb_asprintf_errstring(ldb,
+                                                              __location__
+                                                              ": Failed to remove backlink of "
+                                                              "%s when deleting %s: %s",
+                                                              el->name,
+                                                              old_dn_str,
+                                                              ldb_errstring(ldb));
                                        talloc_free(tmp_ctx);
                                        return LDB_ERR_OPERATIONS_ERROR;
                                }
@@ -3282,32 +4328,26 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request
                                   directly
                                */
                                continue;
-                       }
-                       if (!sa->linkID) {
+                       } else if (sa->linkID == 0) {
                                if (ldb_attr_in_list(preserved_attrs, el->name)) {
                                        continue;
                                }
                                if (sa->searchFlags & SEARCH_FLAG_PRESERVEONDELETE) {
                                        continue;
                                }
+                       } else {
+                               /*
+                                * Ensure that we tell the modification to vanish any linked
+                                * attributes (not simply mark them as isDeleted = TRUE)
+                                */
+                               dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
                        }
                        ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
                        if (ret != LDB_SUCCESS) {
                                talloc_free(tmp_ctx);
                                ldb_module_oom(module);
                                return ret;
-                       }
-               }
-
-               /* Duplicate with the below - we remove the
-                * samAccountType as an originating update, in case it
-                * somehow came back.  The objectCategory will have
-                * gone in the above */
-               ret = ldb_msg_add_empty(msg, "sAMAccountType", LDB_FLAG_MOD_REPLACE, NULL);
-               if (ret != LDB_SUCCESS) {
-                       talloc_free(tmp_ctx);
-                       ldb_module_oom(module);
-                       return ret;
+                       }
                }
 
                break;
@@ -3380,14 +4420,6 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request
         *
         */
 
-       ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE, req);
-       if (ret != LDB_SUCCESS) {
-               ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
-                                      ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
-               talloc_free(tmp_ctx);
-               return ret;
-       }
-
        /*
         * No matter what has happned with other renames, try again to
         * get this to be under the deleted DN.
@@ -3403,6 +4435,15 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request
                        talloc_free(tmp_ctx);
                        return ret;
                }
+               msg->dn = new_dn;
+       }
+
+       ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, req);
+       if (ret != LDB_SUCCESS) {
+               ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
+                                      ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
+               talloc_free(tmp_ctx);
+               return ret;
        }
 
        talloc_free(tmp_ctx);
@@ -3425,6 +4466,10 @@ static int replmd_replicated_request_werror(struct replmd_replicated_request *ar
 {
        int ret = LDB_ERR_OTHER;
        /* TODO: do some error mapping */
+
+       /* Let the caller know the full WERROR */
+       ar->objs->error = status;
+
        return ret;
 }
 
@@ -3476,6 +4521,47 @@ static bool replmd_replPropertyMetaData1_is_newer(struct replPropertyMetaData1 *
                                      new_m->originating_change_time);
 }
 
+static bool replmd_replPropertyMetaData1_new_should_be_taken(uint32_t dsdb_repl_flags,
+                                                            struct replPropertyMetaData1 *cur_m,
+                                                            struct replPropertyMetaData1 *new_m)
+{
+       bool cmp;
+
+       /*
+        * If the new replPropertyMetaData entry for this attribute is
+        * not provided (this happens in the case where we look for
+        * ATTID_name, but the name was not changed), then the local
+        * state is clearly still current, as the remote
+        * server didn't send it due to being older the high watermark
+        * USN we sent.
+        */
+       if (new_m == NULL) {
+               return false;
+       }
+
+       if (dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) {
+               /*
+                * if we compare equal then do an
+                * update. This is used when a client
+                * asks for a FULL_SYNC, and can be
+                * used to recover a corrupt
+                * replica.
+                *
+                * This call is a bit tricky, what we
+                * are doing it turning the 'is_newer'
+                * call into a 'not is older' by
+                * swapping cur_m and new_m, and negating the
+                * outcome.
+                */
+               cmp = !replmd_replPropertyMetaData1_is_newer(new_m,
+                                                            cur_m);
+       } else {
+               cmp = replmd_replPropertyMetaData1_is_newer(cur_m,
+                                                           new_m);
+       }
+       return cmp;
+}
+
 
 /*
   form a conflict DN
@@ -3595,7 +4681,7 @@ static int replmd_op_name_modify_callback(struct ldb_request *req, struct ldb_re
 {
        struct replmd_replicated_request *ar =
                talloc_get_type_abort(req->context, struct replmd_replicated_request);
-       struct ldb_dn *conflict_dn;
+       struct ldb_dn *conflict_dn = NULL;
        int ret;
 
        if (ares->error != LDB_SUCCESS) {
@@ -3653,7 +4739,7 @@ static int replmd_op_name_modify_callback(struct ldb_request *req, struct ldb_re
 }
 
 /*
-  callback for replmd_replicated_apply_add() and replmd_replicated_handle_rename()
+  callback for replmd_replicated_apply_add()
   This copes with the creation of conflict records in the case where
   the DN exists, but with a different objectGUID
  */
@@ -3671,36 +4757,37 @@ static int replmd_op_possible_conflict_callback(struct ldb_request *req, struct
        bool rename_incoming_record, rodc;
        struct replPropertyMetaData1 *rmd_name, *omd_name;
        struct ldb_message *msg;
+       struct ldb_request *down_req = NULL;
 
-       req->callback = callback;
-
-       if (ares->error != LDB_ERR_ENTRY_ALREADY_EXISTS) {
-               /* call the normal callback for everything except
-                  conflicts */
-               return ldb_module_done(req, ares->controls, ares->response, ares->error);
+       /* call the normal callback for success */
+       if (ares->error == LDB_SUCCESS) {
+               return callback(req, ares);
        }
 
-       ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
-       if (ret != LDB_SUCCESS) {
-               ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to determine if we are an RODC when attempting to form conflict DN: %s", ldb_errstring(ldb_module_get_ctx(ar->module)));
-               return ldb_module_done(req, ares->controls, ares->response, LDB_ERR_OPERATIONS_ERROR);
-       }
        /*
         * we have a conflict, and need to decide if we will keep the
         * new record or the old record
         */
 
        msg = ar->objs->objects[ar->index_current].msg;
+       conflict_dn = msg->dn;
+
+       /* For failures other than conflicts, fail the whole operation here */
+       if (ares->error != LDB_ERR_ENTRY_ALREADY_EXISTS) {
+               ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote add of %s: %s",
+                                      ldb_dn_get_linearized(conflict_dn),
+                                      ldb_errstring(ldb_module_get_ctx(ar->module)));
+
+               return ldb_module_done(ar->req, NULL, NULL,
+                                      LDB_ERR_OPERATIONS_ERROR);
+       }
+
+       ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
+       if (ret != LDB_SUCCESS) {
+               ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to determine if we are an RODC when attempting to form conflict DN: %s", ldb_errstring(ldb_module_get_ctx(ar->module)));
+               return ldb_module_done(ar->req, NULL, NULL,
+                                      LDB_ERR_OPERATIONS_ERROR);
 
-       switch (req->operation) {
-       case LDB_ADD:
-               conflict_dn = msg->dn;
-               break;
-       case LDB_RENAME:
-               conflict_dn = req->op.rename.newdn;
-               break;
-       default:
-               return ldb_module_done(req, ares->controls, ares->response, ldb_module_operr(ar->module));
        }
 
        if (rodc) {
@@ -3708,9 +4795,9 @@ static int replmd_op_possible_conflict_callback(struct ldb_request *req, struct
                 * We are on an RODC, or were a GC for this
                 * partition, so we have to fail this until
                 * someone who owns the partition sorts it
-                * out 
+                * out
                 */
-               ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), 
+               ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
                                       "Conflict adding object '%s' from incoming replication as we are read only for the partition.  \n"
                                       " - We must fail the operation until a master for this partition resolves the conflict",
                                       ldb_dn_get_linearized(conflict_dn));
@@ -3719,7 +4806,7 @@ static int replmd_op_possible_conflict_callback(struct ldb_request *req, struct
 
        /*
         * first we need the replPropertyMetaData attribute from the
-        * old record
+        * local, conflicting record
         */
        ret = dsdb_module_search_dn(ar->module, req, &res, conflict_dn,
                                    attrs,
@@ -3749,40 +4836,36 @@ static int replmd_op_possible_conflict_callback(struct ldb_request *req, struct
 
        rmd = ar->objs->objects[ar->index_current].meta_data;
 
-       /* we decide which is newer based on the RPMD on the name
-          attribute.  See [MS-DRSR] ResolveNameConflict */
+       /*
+        * we decide which is newer based on the RPMD on the name
+        * attribute.  See [MS-DRSR] ResolveNameConflict.
+        *
+        * We expect omd_name to be present, as this is from a local
+        * search, but while rmd_name should have been given to us by
+        * the remote server, if it is missing we just prefer the
+        * local name in
+        * replmd_replPropertyMetaData1_new_should_be_taken()
+        */
        rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
        omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
-       if (!rmd_name || !omd_name) {
-               DEBUG(0,(__location__ ": Failed to find name attribute in replPropertyMetaData for %s\n",
+       if (!omd_name) {
+               DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
                         ldb_dn_get_linearized(conflict_dn)));
                goto failed;
        }
 
-       rename_incoming_record = !(ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) &&
-               !replmd_replPropertyMetaData1_is_newer(omd_name, rmd_name);
+       /*
+        * Should we preserve the current record, and so rename the
+        * incoming record to be a conflict?
+        */
+       rename_incoming_record
+               = !replmd_replPropertyMetaData1_new_should_be_taken(ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
+                                                                   omd_name, rmd_name);
 
        if (rename_incoming_record) {
                struct GUID guid;
                struct ldb_dn *new_dn;
 
-               /*
-                * We want to run the original callback here, which
-                * will return LDB_ERR_ENTRY_ALREADY_EXISTS to the
-                * caller, which will in turn know to rename the
-                * incoming record.  The error string is set in case
-                * this isn't handled properly at some point in the
-                * future.
-                */
-               if (req->operation == LDB_RENAME) {
-                       ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
-                                              "Unable to handle incoming renames where this would "
-                                              "create a conflict. Incoming record is %s (caller to handle)\n",
-                                              ldb_dn_get_extended_linearized(req, conflict_dn, 1));
-
-                       goto failed;
-               }
-
                guid = samdb_result_guid(msg, "objectGUID");
                if (GUID_all_zero(&guid)) {
                        DEBUG(0,(__location__ ": Failed to find objectGUID for conflicting incoming record %s\n",
@@ -3799,12 +4882,9 @@ static int replmd_op_possible_conflict_callback(struct ldb_request *req, struct
                DEBUG(2,(__location__ ": Resolving conflict record via incoming rename '%s' -> '%s'\n",
                         ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
 
-               /* re-submit the request, but with a different
-                  callback, so we don't loop forever. */
+               /* re-submit the request, but with the new DN */
+               callback = replmd_op_name_modify_callback;
                msg->dn = new_dn;
-               req->callback = replmd_op_name_modify_callback;
-
-               return ldb_next_request(ar->module, req);
        } else {
                /* we are renaming the existing record */
                struct GUID guid;
@@ -3824,7 +4904,7 @@ static int replmd_op_possible_conflict_callback(struct ldb_request *req, struct
                        goto failed;
                }
 
-               DEBUG(2,(__location__ ": Resolving conflict record via existing rename '%s' -> '%s'\n",
+               DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
                         ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
 
                ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
@@ -3846,15 +4926,55 @@ static int replmd_op_possible_conflict_callback(struct ldb_request *req, struct
                        goto failed;
                }
 
-               return ldb_next_request(ar->module, req);
+               DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated creation of '%s'\n",
+                        ldb_dn_get_linearized(req->op.add.message->dn)));
+       }
+
+       ret = ldb_build_add_req(&down_req,
+                               ldb_module_get_ctx(ar->module),
+                               req,
+                               msg,
+                               ar->controls,
+                               ar,
+                               callback,
+                               req);
+       if (ret != LDB_SUCCESS) {
+               goto failed;
+       }
+       LDB_REQ_SET_LOCATION(down_req);
+
+       /* current partition control needed by "repmd_op_callback" */
+       ret = ldb_request_add_control(down_req,
+                                     DSDB_CONTROL_CURRENT_PARTITION_OID,
+                                     false, NULL);
+       if (ret != LDB_SUCCESS) {
+               return replmd_replicated_request_error(ar, ret);
+       }
+
+       if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PARTIAL_REPLICA) {
+               /* this tells the partition module to make it a
+                  partial replica if creating an NC */
+               ret = ldb_request_add_control(down_req,
+                                             DSDB_CONTROL_PARTIAL_REPLICA,
+                                             false, NULL);
+               if (ret != LDB_SUCCESS) {
+                       return replmd_replicated_request_error(ar, ret);
+               }
        }
 
+       /*
+        * Finally we re-run the add, otherwise the new record won't
+        * exist, as we are here because of that exact failure!
+        */
+       return ldb_next_request(ar->module, down_req);
 failed:
-       /* on failure do the original callback. This means replication
-        * will stop with an error, but there is not much else we can
-        * do
+
+       /* on failure make the caller get the error. This means
+        * replication will stop with an error, but there is not much
+        * else we can do.
         */
-       return ldb_module_done(req, ares->controls, ares->response, ares->error);
+       return ldb_module_done(ar->req, NULL, NULL,
+                              ret);
 }
 
 /*
@@ -3876,16 +4996,6 @@ static int replmd_op_add_callback(struct ldb_request *req, struct ldb_reply *are
        return replmd_op_possible_conflict_callback(req, ares, replmd_op_callback);
 }
 
-/*
-  callback for replmd_replicated_handle_rename()
-  This copes with the creation of conflict records in the case where
-  the DN exists, but with a different objectGUID
- */
-static int replmd_op_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
-{
-       return replmd_op_possible_conflict_callback(req, ares, ldb_modify_default_callback);
-}
-
 /*
   this is called when a new object comes in over DRS
  */
@@ -3900,19 +5010,28 @@ static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
        unsigned int i;
        int ret;
        bool remote_isDeleted = false;
-       const struct dsdb_attribute *rdn_sa;
-       const char *rdn_name;
+       bool is_schema_nc;
+       NTTIME now;
+       time_t t = time(NULL);
+       const struct ldb_val *rdn_val;
+       struct replmd_private *replmd_private =
+               talloc_get_type(ldb_module_get_private(ar->module),
+                               struct replmd_private);
+       unix_to_nt_time(&now, t);
 
        ldb = ldb_module_get_ctx(ar->module);
        msg = ar->objs->objects[ar->index_current].msg;
        md = ar->objs->objects[ar->index_current].meta_data;
+       is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
 
        ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &ar->seq_num);
        if (ret != LDB_SUCCESS) {
                return replmd_replicated_request_error(ar, ret);
        }
 
-       ret = ldb_msg_add_value(msg, "objectGUID", &ar->objs->objects[ar->index_current].guid_value, NULL);
+       ret = dsdb_msg_add_guid(msg,
+                               &ar->objs->objects[ar->index_current].object_guid,
+                               "objectGUID");
        if (ret != LDB_SUCCESS) {
                return replmd_replicated_request_error(ar, ret);
        }
@@ -3954,8 +5073,12 @@ static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
        }
 
        if (DEBUGLVL(4)) {
+               struct GUID_txt_buf guid_txt;
+
                char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_ADD, msg);
-               DEBUG(4, ("DRS replication add message:\n%s\n", s));
+               DEBUG(4, ("DRS replication add message of %s:\n%s\n",
+                         GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
+                         s));
                talloc_free(s);
        }
 
@@ -3963,23 +5086,20 @@ static int replmd_replicated_apply_add(struct replmd_replicated_request *ar)
                                                     "isDeleted", false);
 
        /*
-        * the meta data array is already sorted by the caller
+        * the meta data array is already sorted by the caller, except
+        * for the RDN, which needs to be added.
         */
 
-       rdn_name = ldb_dn_get_rdn_name(msg->dn);
-       if (rdn_name == NULL) {
-               ldb_asprintf_errstring(ldb, __location__ ": No rDN for %s?\n", ldb_dn_get_linearized(msg->dn));
-               return replmd_replicated_request_error(ar, LDB_ERR_INVALID_DN_SYNTAX);
-       }
 
-       rdn_sa = dsdb_attribute_by_lDAPDisplayName(ar->schema, rdn_name);
-       if (rdn_sa == NULL) {
-               ldb_asprintf_errstring(ldb, ": No schema attribute found for rDN %s for %s\n",
-                                      rdn_name, ldb_dn_get_linearized(msg->dn));
-               return replmd_replicated_request_error(ar, LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE);
+       rdn_val = ldb_dn_get_rdn_val(msg->dn);
+       ret = replmd_update_rpmd_rdn_attr(ldb, msg, rdn_val, NULL,
+                                    md, ar, now, is_schema_nc);
+       if (ret != LDB_SUCCESS) {
+               ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
+               return replmd_replicated_request_error(ar, ret);
        }
 
-       ret = replmd_replPropertyMetaDataCtr1_verify(ldb, &md->ctr.ctr1, rdn_sa, msg->dn);
+       ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &md->ctr.ctr1, msg->dn);
        if (ret != LDB_SUCCESS) {
                ldb_asprintf_errstring(ldb, "%s: error during DRS repl ADD: %s", __func__, ldb_errstring(ldb));
                return replmd_replicated_request_error(ar, ret);
@@ -4052,12 +5172,14 @@ static int replmd_replicated_apply_search_for_parent_callback(struct ldb_request
                return ldb_module_done(ar->req, NULL, NULL,
                                        LDB_ERR_OPERATIONS_ERROR);
        }
-       if (ares->error != LDB_SUCCESS &&
-           ares->error != LDB_ERR_NO_SUCH_OBJECT) {
-               /*
-                * TODO: deal with the above error that the parent object doesn't exist
-                */
 
+       /*
+        * The error NO_SUCH_OBJECT is not expected, unless the search
+        * base is the partition DN, and that case doesn't happen here
+        * because then we wouldn't get a parent_guid_value in any
+        * case.
+        */
+       if (ares->error != LDB_SUCCESS) {
                return ldb_module_done(ar->req, ares->controls,
                                        ares->response, ares->error);
        }
@@ -4097,7 +5219,7 @@ static int replmd_replicated_apply_search_for_parent_callback(struct ldb_request
                                                       ldb_dn_get_linearized(parent_msg->dn));
                                return ldb_module_done(ar->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
                        }
-                       
+
                        ret = dsdb_wellknown_dn(ldb_module_get_ctx(ar->module), msg,
                                                nc_root,
                                                DS_GUID_LOSTANDFOUND_CONTAINER,
@@ -4115,9 +5237,13 @@ static int replmd_replicated_apply_search_for_parent_callback(struct ldb_request
                        }
                        ar->objs->objects[ar->index_current].last_known_parent
                                = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
+
                } else {
-                       parent_dn = parent_msg->dn;
+                       parent_dn
+                               = talloc_steal(ar->objs->objects[ar->index_current].msg, parent_msg->dn);
+
                }
+               ar->objs->objects[ar->index_current].local_parent_dn = parent_dn;
 
                comp_num = ldb_dn_get_comp_num(msg->dn);
                if (comp_num > 1) {
@@ -4137,6 +5263,32 @@ static int replmd_replicated_apply_search_for_parent_callback(struct ldb_request
                break;
 
        case LDB_REPLY_DONE:
+
+               if (ar->objs->objects[ar->index_current].local_parent_dn == NULL) {
+                       struct GUID_txt_buf str_buf;
+                       if (ar->search_msg != NULL) {
+                               ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
+                                                      "No parent with GUID %s found for object locally known as %s",
+                                                      GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
+                                                      ldb_dn_get_linearized(ar->search_msg->dn));
+                       } else {
+                               ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
+                                                      "No parent with GUID %s found for object remotely known as %s",
+                                                      GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid, &str_buf),
+                                                      ldb_dn_get_linearized(ar->objs->objects[ar->index_current].msg->dn));
+                       }
+
+                       /*
+                        * This error code is really important, as it
+                        * is the flag back to the callers to retry
+                        * this with DRSUAPI_DRS_GET_ANC, and so get
+                        * the parent objects before the child
+                        * objects
+                        */
+                       return ldb_module_done(ar->req, NULL, NULL,
+                                              replmd_replicated_request_werror(ar, WERR_DS_DRA_MISSING_PARENT));
+               }
+
                if (ar->search_msg != NULL) {
                        ret = replmd_replicated_apply_merge(ar);
                } else {
@@ -4166,96 +5318,268 @@ static int replmd_replicated_apply_search_for_parent(struct replmd_replicated_re
        char *filter;
        struct ldb_request *search_req;
        static const char *attrs[] = {"isDeleted", NULL};
+       struct GUID_txt_buf guid_str_buf;
+
+       ldb = ldb_module_get_ctx(ar->module);
+
+       if (ar->objs->objects[ar->index_current].parent_guid == NULL) {
+               if (ar->search_msg != NULL) {
+                       return replmd_replicated_apply_merge(ar);
+               } else {
+                       return replmd_replicated_apply_add(ar);
+               }
+       }
+
+       tmp_str = GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
+                                 &guid_str_buf);
+
+       filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
+       if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
+
+       ret = ldb_build_search_req(&search_req,
+                                  ldb,
+                                  ar,
+                                  ar->objs->partition_dn,
+                                  LDB_SCOPE_SUBTREE,
+                                  filter,
+                                  attrs,
+                                  NULL,
+                                  ar,
+                                  replmd_replicated_apply_search_for_parent_callback,
+                                  ar->req);
+       LDB_REQ_SET_LOCATION(search_req);
+
+       ret = dsdb_request_add_controls(search_req,
+                                       DSDB_SEARCH_SHOW_RECYCLED|
+                                       DSDB_SEARCH_SHOW_DELETED|
+                                       DSDB_SEARCH_SHOW_EXTENDED_DN);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
+
+       return ldb_next_request(ar->module, search_req);
+}
+
+/*
+  handle renames that come in over DRS replication
+ */
+static int replmd_replicated_handle_rename(struct replmd_replicated_request *ar,
+                                          struct ldb_message *msg,
+                                          struct ldb_request *parent,
+                                          bool *renamed)
+{
+       int ret;
+       TALLOC_CTX *tmp_ctx = talloc_new(msg);
+       struct ldb_result *res;
+       struct ldb_dn *conflict_dn;
+       const char *attrs[] = { "replPropertyMetaData", "objectGUID", NULL };
+       const struct ldb_val *omd_value;
+       struct replPropertyMetaDataBlob omd, *rmd;
+       enum ndr_err_code ndr_err;
+       bool rename_incoming_record, rodc;
+       struct replPropertyMetaData1 *rmd_name, *omd_name;
+       struct ldb_dn *new_dn;
+       struct GUID guid;
+
+       DEBUG(4,("replmd_replicated_request rename %s => %s\n",
+                ldb_dn_get_linearized(ar->search_msg->dn),
+                ldb_dn_get_linearized(msg->dn)));
+
+
+       ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
+                                DSDB_FLAG_NEXT_MODULE, ar->req);
+       if (ret == LDB_SUCCESS) {
+               talloc_free(tmp_ctx);
+               *renamed = true;
+               return ret;
+       }
+
+       if (ret != LDB_ERR_ENTRY_ALREADY_EXISTS) {
+               talloc_free(tmp_ctx);
+               ldb_asprintf_errstring(ldb_module_get_ctx(ar->module), "Failed to locally apply remote rename from %s to %s: %s",
+                                      ldb_dn_get_linearized(ar->search_msg->dn),
+                                      ldb_dn_get_linearized(msg->dn),
+                                      ldb_errstring(ldb_module_get_ctx(ar->module)));
+               return ret;
+       }
+
+       ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
+       if (ret != LDB_SUCCESS) {
+               ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
+                                      "Failed to determine if we are an RODC when attempting to form conflict DN: %s",
+                                      ldb_errstring(ldb_module_get_ctx(ar->module)));
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+       /*
+        * we have a conflict, and need to decide if we will keep the
+        * new record or the old record
+        */
+
+       conflict_dn = msg->dn;
+
+       if (rodc) {
+               /*
+                * We are on an RODC, or were a GC for this
+                * partition, so we have to fail this until
+                * someone who owns the partition sorts it
+                * out
+                */
+               ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
+                                      "Conflict adding object '%s' from incoming replication but we are read only for the partition.  \n"
+                                      " - We must fail the operation until a master for this partition resolves the conflict",
+                                      ldb_dn_get_linearized(conflict_dn));
+               goto failed;
+       }
+
+       /*
+        * first we need the replPropertyMetaData attribute from the
+        * old record
+        */
+       ret = dsdb_module_search_dn(ar->module, tmp_ctx, &res, conflict_dn,
+                                   attrs,
+                                   DSDB_FLAG_NEXT_MODULE |
+                                   DSDB_SEARCH_SHOW_DELETED |
+                                   DSDB_SEARCH_SHOW_RECYCLED, ar->req);
+       if (ret != LDB_SUCCESS) {
+               DEBUG(0,(__location__ ": Unable to find object for conflicting record '%s'\n",
+                        ldb_dn_get_linearized(conflict_dn)));
+               goto failed;
+       }
+
+       omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
+       if (omd_value == NULL) {
+               DEBUG(0,(__location__ ": Unable to find replPropertyMetaData for conflicting record '%s'\n",
+                        ldb_dn_get_linearized(conflict_dn)));
+               goto failed;
+       }
+
+       ndr_err = ndr_pull_struct_blob(omd_value, res->msgs[0], &omd,
+                                      (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+               DEBUG(0,(__location__ ": Failed to parse old replPropertyMetaData for %s\n",
+                        ldb_dn_get_linearized(conflict_dn)));
+               goto failed;
+       }
 
-       ldb = ldb_module_get_ctx(ar->module);
+       rmd = ar->objs->objects[ar->index_current].meta_data;
 
-       if (!ar->objs->objects[ar->index_current].parent_guid_value.data) {
-               if (ar->search_msg != NULL) {
-                       return replmd_replicated_apply_merge(ar);
-               } else {
-                       return replmd_replicated_apply_add(ar);
-               }
+       /*
+        * we decide which is newer based on the RPMD on the name
+        * attribute.  See [MS-DRSR] ResolveNameConflict.
+        *
+        * We expect omd_name to be present, as this is from a local
+        * search, but while rmd_name should have been given to us by
+        * the remote server, if it is missing we just prefer the
+        * local name in
+        * replmd_replPropertyMetaData1_new_should_be_taken()
+        */
+       rmd_name = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
+       omd_name = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
+       if (!omd_name) {
+               DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
+                        ldb_dn_get_linearized(conflict_dn)));
+               goto failed;
        }
 
-       tmp_str = ldb_binary_encode(ar, ar->objs->objects[ar->index_current].parent_guid_value);
-       if (!tmp_str) return replmd_replicated_request_werror(ar, WERR_NOMEM);
+       /*
+        * Should we preserve the current record, and so rename the
+        * incoming record to be a conflict?
+        */
+       rename_incoming_record =
+               !replmd_replPropertyMetaData1_new_should_be_taken(
+                       ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
+                       omd_name, rmd_name);
 
-       filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
-       if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
-       talloc_free(tmp_str);
+       if (rename_incoming_record) {
 
-       ret = ldb_build_search_req(&search_req,
-                                  ldb,
-                                  ar,
-                                  ar->objs->partition_dn,
-                                  LDB_SCOPE_SUBTREE,
-                                  filter,
-                                  attrs,
-                                  NULL,
-                                  ar,
-                                  replmd_replicated_apply_search_for_parent_callback,
-                                  ar->req);
-       LDB_REQ_SET_LOCATION(search_req);
+               new_dn = replmd_conflict_dn(msg, msg->dn,
+                                           &ar->objs->objects[ar->index_current].object_guid);
+               if (new_dn == NULL) {
+                       ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
+                                                                 "Failed to form conflict DN for %s\n",
+                                                                 ldb_dn_get_linearized(msg->dn));
 
-       ret = dsdb_request_add_controls(search_req, 
-                                       DSDB_SEARCH_SHOW_RECYCLED|
-                                       DSDB_SEARCH_SHOW_DELETED|
-                                       DSDB_SEARCH_SHOW_EXTENDED_DN);
-       if (ret != LDB_SUCCESS) {
-               return ret;
-       }
+                       return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
+               }
 
-       return ldb_next_request(ar->module, search_req);
-}
+               ret = dsdb_module_rename(ar->module, ar->search_msg->dn, new_dn,
+                                        DSDB_FLAG_NEXT_MODULE, ar->req);
+               if (ret != LDB_SUCCESS) {
+                       ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
+                                              "Failed to rename incoming conflicting dn '%s' (was '%s') to '%s' - %s\n",
+                                              ldb_dn_get_linearized(conflict_dn),
+                                              ldb_dn_get_linearized(ar->search_msg->dn),
+                                              ldb_dn_get_linearized(new_dn),
+                                              ldb_errstring(ldb_module_get_ctx(ar->module)));
+                       return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
+               }
 
-/*
-  handle renames that come in over DRS replication
- */
-static int replmd_replicated_handle_rename(struct replmd_replicated_request *ar,
-                                          struct ldb_message *msg,
-                                          struct ldb_request *parent)
-{
-       struct ldb_request *req;
-       int ret;
-       TALLOC_CTX *tmp_ctx = talloc_new(msg);
-       struct ldb_result *res;
+               msg->dn = new_dn;
+               *renamed = true;
+               return LDB_SUCCESS;
+       }
 
-       DEBUG(4,("replmd_replicated_request rename %s => %s\n",
-                ldb_dn_get_linearized(ar->search_msg->dn),
-                ldb_dn_get_linearized(msg->dn)));
+       /* we are renaming the existing record */
 
+       guid = samdb_result_guid(res->msgs[0], "objectGUID");
+       if (GUID_all_zero(&guid)) {
+               DEBUG(0,(__location__ ": Failed to find objectGUID for existing conflict record %s\n",
+                        ldb_dn_get_linearized(conflict_dn)));
+               goto failed;
+       }
 
-       res = talloc_zero(tmp_ctx, struct ldb_result);
-       if (!res) {
-               talloc_free(tmp_ctx);
-               return ldb_oom(ldb_module_get_ctx(ar->module));
+       new_dn = replmd_conflict_dn(tmp_ctx, conflict_dn, &guid);
+       if (new_dn == NULL) {
+               DEBUG(0,(__location__ ": Failed to form conflict DN for %s\n",
+                        ldb_dn_get_linearized(conflict_dn)));
+               goto failed;
        }
 
-       /* pass rename to the next module
-        * so it doesn't appear as an originating update */
-       ret = ldb_build_rename_req(&req, ldb_module_get_ctx(ar->module), tmp_ctx,
-                                  ar->search_msg->dn, msg->dn,
-                                  NULL,
-                                  ar,
-                                  replmd_op_rename_callback,
-                                  parent);
-       LDB_REQ_SET_LOCATION(req);
+       DEBUG(2,(__location__ ": Resolving conflict record via existing-record rename '%s' -> '%s'\n",
+                ldb_dn_get_linearized(conflict_dn), ldb_dn_get_linearized(new_dn)));
+
+       ret = dsdb_module_rename(ar->module, conflict_dn, new_dn,
+                                DSDB_FLAG_OWN_MODULE, ar->req);
        if (ret != LDB_SUCCESS) {
-               talloc_free(tmp_ctx);
-               return ret;
+               DEBUG(0,(__location__ ": Failed to rename conflict dn '%s' to '%s' - %s\n",
+                        ldb_dn_get_linearized(conflict_dn),
+                        ldb_dn_get_linearized(new_dn),
+                        ldb_errstring(ldb_module_get_ctx(ar->module))));
+               goto failed;
        }
 
-       ret = dsdb_request_add_controls(req, DSDB_MODIFY_RELAX);
+       /*
+        * now we need to ensure that the rename is seen as an
+        * originating update. We do that with a modify.
+        */
+       ret = replmd_name_modify(ar, ar->req, new_dn);
        if (ret != LDB_SUCCESS) {
-               talloc_free(tmp_ctx);
-               return ret;
+               goto failed;
        }
 
-       ret = ldb_next_request(ar->module, req);
+       DEBUG(2,(__location__ ": With conflicting record renamed, re-apply replicated rename '%s' -> '%s'\n",
+                ldb_dn_get_linearized(ar->search_msg->dn),
+                ldb_dn_get_linearized(msg->dn)));
 
-       if (ret == LDB_SUCCESS) {
-               ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+
+       ret = dsdb_module_rename(ar->module, ar->search_msg->dn, msg->dn,
+                                DSDB_FLAG_NEXT_MODULE, ar->req);
+       if (ret != LDB_SUCCESS) {
+               DEBUG(0,(__location__ ": After conflict resolution, failed to rename dn '%s' to '%s' - %s\n",
+                        ldb_dn_get_linearized(ar->search_msg->dn),
+                        ldb_dn_get_linearized(msg->dn),
+                        ldb_errstring(ldb_module_get_ctx(ar->module))));
+                       goto failed;
        }
+failed:
+
+       /*
+        * On failure make the caller get the error
+        * This means replication will stop with an error,
+        * but there is not much else we can do.  In the
+        * LDB_ERR_ENTRY_ALREADY_EXISTS case this is exactly what is
+        * needed.
+        */
 
        talloc_free(tmp_ctx);
        return ret;
@@ -4273,6 +5597,7 @@ static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
        const struct ldb_val *omd_value;
        struct replPropertyMetaDataBlob nmd;
        struct ldb_val nmd_value;
+       struct GUID remote_parent_guid;
        unsigned int i;
        uint32_t j,ni=0;
        unsigned int removed_attrs = 0;
@@ -4284,10 +5609,21 @@ static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
        bool take_remote_isDeleted = false;
        bool sd_updated = false;
        bool renamed = false;
+       bool is_schema_nc = false;
+       NTSTATUS nt_status;
+       const struct ldb_val *old_rdn, *new_rdn;
+       struct replmd_private *replmd_private =
+               talloc_get_type(ldb_module_get_private(ar->module),
+                               struct replmd_private);
+       NTTIME now;
+       time_t t = time(NULL);
+       unix_to_nt_time(&now, t);
 
        ldb = ldb_module_get_ctx(ar->module);
        msg = ar->objs->objects[ar->index_current].msg;
 
+       is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;
+
        rmd = ar->objs->objects[ar->index_current].meta_data;
        ZERO_STRUCT(omd);
        omd.version = 1;
@@ -4298,7 +5634,7 @@ static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
                ndr_err = ndr_pull_struct_blob(omd_value, ar, &omd,
                                               (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
                if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
-                       NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
+                       nt_status = ndr_map_error2ntstatus(ndr_err);
                        return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
                }
 
@@ -4307,12 +5643,52 @@ static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
                }
        }
 
+       if (DEBUGLVL(5)) {
+               struct GUID_txt_buf guid_txt;
+
+               char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
+               DEBUG(5, ("Initial DRS replication modify message of %s is:\n%s\n"
+                         "%s\n"
+                         "%s\n",
+                         GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
+                         s,
+                         ndr_print_struct_string(s,
+                                                 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
+                                                 "existing replPropertyMetaData",
+                                                 &omd),
+                         ndr_print_struct_string(s,
+                                                 (ndr_print_fn_t)ndr_print_replPropertyMetaDataBlob,
+                                                 "incoming replPropertyMetaData",
+                                                 rmd)));
+               talloc_free(s);
+       }
+
        local_isDeleted = ldb_msg_find_attr_as_bool(ar->search_msg,
                                                    "isDeleted", false);
        remote_isDeleted = ldb_msg_find_attr_as_bool(msg,
                                                     "isDeleted", false);
 
-       if (strcmp(ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(ar->search_msg->dn)) == 0) {
+       /*
+        * Fill in the remote_parent_guid with the GUID or an all-zero
+        * GUID.
+        */
+       if (ar->objs->objects[ar->index_current].parent_guid != NULL) {
+               remote_parent_guid = *ar->objs->objects[ar->index_current].parent_guid;
+       } else {
+               remote_parent_guid = GUID_zero();
+       }
+
+       /*
+        * To ensure we follow a complex rename chain around, we have
+        * to confirm that the DN is the same (mostly to confirm the
+        * RDN) and the parentGUID is the same.
+        *
+        * This ensures we keep things under the correct parent, which
+        * replmd_replicated_handle_rename() will do.
+        */
+
+       if (strcmp(ldb_dn_get_linearized(msg->dn), ldb_dn_get_linearized(ar->search_msg->dn)) == 0
+           && GUID_equal(&remote_parent_guid, &ar->local_parent_guid)) {
                ret = LDB_SUCCESS;
        } else {
                /*
@@ -4325,49 +5701,10 @@ static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
                 * the peer has an older name to what we have (see
                 * replmd_replicated_apply_search_callback())
                 */
-               renamed = true;
-               ret = replmd_replicated_handle_rename(ar, msg, ar->req);
+               ret = replmd_replicated_handle_rename(ar, msg, ar->req, &renamed);
        }
 
-       /*
-        * This particular error code means that we already tried the
-        * conflict algrorithm, and the existing record name was newer, so we
-        * need to rename the incoming record
-        */
-       if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) {
-               struct GUID guid;
-               NTSTATUS status;
-               struct ldb_dn *new_dn;
-               status = GUID_from_ndr_blob(&ar->objs->objects[ar->index_current].guid_value, &guid);
-               /* This really, really can't fail */
-               SMB_ASSERT(NT_STATUS_IS_OK(status));
-
-               new_dn = replmd_conflict_dn(msg, msg->dn, &guid);
-               if (new_dn == NULL) {
-                       ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
-                                                                 "Failed to form conflict DN for %s\n",
-                                                                 ldb_dn_get_linearized(msg->dn));
-
-                       return replmd_replicated_request_werror(ar, WERR_NOMEM);
-               }
-
-               ret = dsdb_module_rename(ar->module, ar->search_msg->dn, new_dn,
-                                        DSDB_FLAG_NEXT_MODULE, ar->req);
-               if (ret != LDB_SUCCESS) {
-                       ldb_asprintf_errstring(ldb_module_get_ctx(ar->module),
-                                              "Failed to rename incoming conflicting dn '%s' (was '%s') to '%s' - %s\n",
-                                              ldb_dn_get_linearized(msg->dn),
-                                              ldb_dn_get_linearized(ar->search_msg->dn),
-                                              ldb_dn_get_linearized(new_dn),
-                                              ldb_errstring(ldb_module_get_ctx(ar->module)));
-                       return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
-               }
-
-               /* Set the callback to one that will fix up the name to be a conflict DN */
-               callback = replmd_op_name_modify_callback;
-               msg->dn = new_dn;
-               renamed = true;
-       } else if (ret != LDB_SUCCESS) {
+       if (ret != LDB_SUCCESS) {
                ldb_debug(ldb, LDB_DEBUG_FATAL,
                          "replmd_replicated_request rename %s => %s failed - %s\n",
                          ldb_dn_get_linearized(ar->search_msg->dn),
@@ -4376,13 +5713,21 @@ static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
                return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
        }
 
+       if (renamed == true) {
+               /*
+                * Set the callback to one that will fix up the name
+                * metadata on the new conflict DN
+                */
+               callback = replmd_op_name_modify_callback;
+       }
+
        ZERO_STRUCT(nmd);
        nmd.version = 1;
        nmd.ctr.ctr1.count = omd.ctr.ctr1.count + rmd->ctr.ctr1.count;
        nmd.ctr.ctr1.array = talloc_array(ar,
                                          struct replPropertyMetaData1,
                                          nmd.ctr.ctr1.count);
-       if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOMEM);
+       if (!nmd.ctr.ctr1.array) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
 
        /* first copy the old meta data */
        for (i=0; i < omd.ctr.ctr1.count; i++) {
@@ -4402,26 +5747,10 @@ static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
                                continue;
                        }
 
-                       if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING) {
-                               /*
-                                * if we compare equal then do an
-                                * update. This is used when a client
-                                * asks for a FULL_SYNC, and can be
-                                * used to recover a corrupt
-                                * replica.
-                                *
-                                * This call is a bit tricky, what we
-                                * are doing it turning the 'is_newer'
-                                * call into a 'not is older' by
-                                * swapping i and j, and negating the
-                                * outcome.
-                               */
-                               cmp = !replmd_replPropertyMetaData1_is_newer(&rmd->ctr.ctr1.array[i],
-                                                                            &nmd.ctr.ctr1.array[j]);
-                       } else {
-                               cmp = replmd_replPropertyMetaData1_is_newer(&nmd.ctr.ctr1.array[j],
-                                                                           &rmd->ctr.ctr1.array[i]);
-                       }
+                       cmp = replmd_replPropertyMetaData1_new_should_be_taken(
+                               ar->objs->dsdb_repl_flags,
+                               &nmd.ctr.ctr1.array[j],
+                               &rmd->ctr.ctr1.array[i]);
                        if (cmp) {
                                /* replace the entry */
                                nmd.ctr.ctr1.array[j] = rmd->ctr.ctr1.array[i];
@@ -4489,14 +5818,21 @@ static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
         */
        nmd.ctr.ctr1.count = ni;
 
+       new_rdn = ldb_dn_get_rdn_val(msg->dn);
+       old_rdn = ldb_dn_get_rdn_val(ar->search_msg->dn);
+
+       if (renamed) {
+               ret = replmd_update_rpmd_rdn_attr(ldb, msg, new_rdn, old_rdn,
+                                                 &nmd, ar, now, is_schema_nc);
+               if (ret != LDB_SUCCESS) {
+                       ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
+                       return replmd_replicated_request_error(ar, ret);
+               }
+       }
        /*
-        * the rdn attribute (the alias for the name attribute),
-        * 'cn' for most objects is the last entry in the meta data array
-        * we have stored
-        *
         * sort the new meta data array
         */
-       ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, ar->schema, msg->dn);
+       ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &nmd.ctr.ctr1, msg->dn);
        if (ret != LDB_SUCCESS) {
                ldb_asprintf_errstring(ldb, "%s: error during DRS repl merge: %s", __func__, ldb_errstring(ldb));
                return ret;
@@ -4546,7 +5882,7 @@ static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
        ndr_err = ndr_push_struct_blob(&nmd_value, msg, &nmd,
                                       (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
        if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
-               NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
+               nt_status = ndr_map_error2ntstatus(ndr_err);
                return replmd_replicated_request_werror(ar, ntstatus_to_werror(nt_status));
        }
 
@@ -4583,8 +5919,12 @@ static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
        }
 
        if (DEBUGLVL(4)) {
+               struct GUID_txt_buf guid_txt;
+
                char *s = ldb_ldif_message_string(ldb, ar, LDB_CHANGETYPE_MODIFY, msg);
-               DEBUG(4, ("DRS replication modify message:\n%s\n", s));
+               DEBUG(4, ("Final DRS replication modify message of %s:\n%s\n",
+                         GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid, &guid_txt),
+                         s));
                talloc_free(s);
        }
 
@@ -4643,7 +5983,8 @@ static int replmd_replicated_apply_search_callback(struct ldb_request *req,
                const struct ldb_val *omd_value;
                struct replPropertyMetaDataBlob *rmd;
                struct ldb_message *msg;
-
+               int instanceType;
+               ar->objs->objects[ar->index_current].local_parent_dn = NULL;
                ar->objs->objects[ar->index_current].last_known_parent = NULL;
 
                /*
@@ -4685,33 +6026,100 @@ static int replmd_replicated_apply_search_callback(struct ldb_request *req,
                        }
                }
 
+               ar->local_parent_guid = samdb_result_guid(ar->search_msg, "parentGUID");
+
+               instanceType = ldb_msg_find_attr_as_int(ar->search_msg, "instanceType", 0);
+               if (((instanceType & INSTANCE_TYPE_IS_NC_HEAD) == 0)
+                   && GUID_all_zero(&ar->local_parent_guid)) {
+                       DEBUG(0, ("Refusing to replicate new version of %s "
+                                 "as local object has an all-zero parentGUID attribute, "
+                                 "despite not being an NC root\n",
+                                 ldb_dn_get_linearized(ar->search_msg->dn)));
+                       return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);
+               }
+
                /*
                 * now we need to check for double renames. We could have a
                 * local rename pending which our replication partner hasn't
                 * received yet. We choose which one wins by looking at the
-                * attribute stamps on the two objects, the newer one wins
+                * attribute stamps on the two objects, the newer one wins.
+                *
+                * This also simply applies the correct algorithms for
+                * determining if a change was made to name at all, or
+                * if the object has just been renamed under the same
+                * parent.
                 */
                md_remote = replmd_replPropertyMetaData1_find_attid(rmd, DRSUAPI_ATTID_name);
-               md_local  = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
-               /* if there is no name attribute then we have to assume the
-                  object we've received is in fact newer */
-               if (ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING ||
-                   !md_remote || !md_local ||
-                   replmd_replPropertyMetaData1_is_newer(md_local, md_remote)) {
+               md_local = replmd_replPropertyMetaData1_find_attid(&omd, DRSUAPI_ATTID_name);
+               if (!md_local) {
+                       DEBUG(0,(__location__ ": Failed to find name attribute in local LDB replPropertyMetaData for %s\n",
+                                ldb_dn_get_linearized(ar->search_msg->dn)));
+                       return replmd_replicated_request_werror(ar, WERR_DS_DRA_DB_ERROR);
+               }
+
+               /*
+                * if there is no name attribute given then we have to assume the
+                *  object we've received has the older name
+                */
+               if (replmd_replPropertyMetaData1_new_should_be_taken(
+                           ar->objs->dsdb_repl_flags & DSDB_REPL_FLAG_PRIORITISE_INCOMING,
+                           md_local, md_remote)) {
+                       struct GUID_txt_buf p_guid_local;
+                       struct GUID_txt_buf p_guid_remote;
+                       msg = ar->objs->objects[ar->index_current].msg;
+
+                       /* Merge on the existing object, with rename */
+
+                       DEBUG(4,(__location__ ": Looking for new parent for object %s currently under %s "
+                                "as incoming object changing to %s under %s\n",
+                                ldb_dn_get_linearized(ar->search_msg->dn),
+                                GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
+                                ldb_dn_get_linearized(msg->dn),
+                                GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
+                                                &p_guid_remote)));
                        ret = replmd_replicated_apply_search_for_parent(ar);
                } else {
+                       struct GUID_txt_buf p_guid_local;
+                       struct GUID_txt_buf p_guid_remote;
                        msg = ar->objs->objects[ar->index_current].msg;
 
-                       /* Otherwise, just merge on the existing object, force no rename */
-                       DEBUG(4,(__location__ ": Keeping object %s and rejecting older rename to %s\n",
-                                ldb_dn_get_linearized(ar->search_msg->dn),
-                                ldb_dn_get_linearized(msg->dn)));
+                       /*
+                        * Merge on the existing object, force no
+                        * rename (code below just to explain why in
+                        * the DEBUG() logs)
+                        */
 
+                       if (strcmp(ldb_dn_get_linearized(ar->search_msg->dn),
+                                  ldb_dn_get_linearized(msg->dn)) == 0) {
+                               if (ar->objs->objects[ar->index_current].parent_guid != NULL &&
+                                   GUID_equal(&ar->local_parent_guid,
+                                              ar->objs->objects[ar->index_current].parent_guid)
+                                   == false) {
+                                       DEBUG(4,(__location__ ": Keeping object %s at under %s "
+                                                "despite incoming object changing parent to %s\n",
+                                                ldb_dn_get_linearized(ar->search_msg->dn),
+                                                GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
+                                                GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
+                                                                &p_guid_remote)));
+                               }
+                       } else {
+                               DEBUG(4,(__location__ ": Keeping object %s at under %s "
+                                        " and rejecting older rename to %s under %s\n",
+                                        ldb_dn_get_linearized(ar->search_msg->dn),
+                                        GUID_buf_string(&ar->local_parent_guid, &p_guid_local),
+                                        ldb_dn_get_linearized(msg->dn),
+                                        GUID_buf_string(ar->objs->objects[ar->index_current].parent_guid,
+                                                        &p_guid_remote)));
+                       }
                        /*
                         * This assignment ensures that the strcmp()
-                        * in replmd_replicated_apply_merge() avoids
-                        * the rename call
+                        * and GUID_equal() calls in
+                        * replmd_replicated_apply_merge() avoids the
+                        * rename call
                         */
+                       ar->objs->objects[ar->index_current].parent_guid =
+                               &ar->local_parent_guid;
+
                        msg->dn = ar->search_msg->dn;
                        ret = replmd_replicated_apply_merge(ar);
                }
@@ -4734,6 +6142,11 @@ static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
        char *tmp_str;
        char *filter;
        struct ldb_request *search_req;
+       static const char *attrs[] = { "repsFrom", "replUpToDateVector",
+                                      "parentGUID", "instanceType",
+                                      "replPropertyMetaData", "nTSecurityDescriptor",
+                                      "isDeleted", NULL };
+       struct GUID_txt_buf guid_str_buf;
 
        if (ar->index_current >= ar->objs->num_objects) {
                /* done with it, go to next stage */
@@ -4744,27 +6157,26 @@ static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
        ar->search_msg = NULL;
        ar->isDeleted = false;
 
-       tmp_str = ldb_binary_encode(ar, ar->objs->objects[ar->index_current].guid_value);
-       if (!tmp_str) return replmd_replicated_request_werror(ar, WERR_NOMEM);
+       tmp_str = GUID_buf_string(&ar->objs->objects[ar->index_current].object_guid,
+                                 &guid_str_buf);
 
        filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
-       if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
-       talloc_free(tmp_str);
+       if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
 
        ret = ldb_build_search_req(&search_req,
                                   ldb,
                                   ar,
-                                  NULL,
+                                  ar->objs->partition_dn,
                                   LDB_SCOPE_SUBTREE,
                                   filter,
-                                  NULL,
+                                  attrs,
                                   NULL,
                                   ar,
                                   replmd_replicated_apply_search_callback,
                                   ar->req);
        LDB_REQ_SET_LOCATION(search_req);
 
-       ret = dsdb_request_add_controls(search_req, DSDB_SEARCH_SEARCH_ALL_PARTITIONS|DSDB_SEARCH_SHOW_RECYCLED);
+       ret = dsdb_request_add_controls(search_req, DSDB_SEARCH_SHOW_RECYCLED);
 
        if (ret != LDB_SUCCESS) {
                return ret;
@@ -4890,7 +6302,6 @@ static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *a
        struct replUpToDateVectorBlob nuv;
        struct ldb_val nuv_value;
        struct ldb_message_element *nuv_el = NULL;
-       const struct GUID *our_invocation_id;
        struct ldb_message_element *orf_el = NULL;
        struct repsFromToBlob nrf;
        struct ldb_val *nrf_value = NULL;
@@ -4956,7 +6367,7 @@ static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *a
        nuv.ctr.ctr2.cursors = talloc_array(ar,
                                            struct drsuapi_DsReplicaCursor2,
                                            nuv.ctr.ctr2.count);
-       if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOMEM);
+       if (!nuv.ctr.ctr2.cursors) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
 
        /* first copy the old vector */
        for (i=0; i < ouv.ctr.ctr2.count; i++) {
@@ -4964,20 +6375,12 @@ static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *a
                ni++;
        }
 
-       /* get our invocation_id if we have one already attached to the ldb */
-       our_invocation_id = samdb_ntds_invocation_id(ldb);
-       if (our_invocation_id == NULL) {
-               DEBUG(0, ("repl_meta_data: Could not find our own server's invocationID!\n"));
-               return replmd_replicated_request_werror(ar, WERR_DS_DRA_INTERNAL_ERROR);                
-       }
-
        /* merge in the source_dsa vector is available */
        for (i=0; (ruv && i < ruv->count); i++) {
                found = false;
 
-               if (our_invocation_id &&
-                   GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
-                              our_invocation_id)) {
+               if (GUID_equal(&ruv->cursors[i].source_dsa_invocation_id,
+                              &ar->our_invocation_id)) {
                        continue;
                }
 
@@ -5016,7 +6419,7 @@ static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *a
         * create the change ldb_message
         */
        msg = ldb_msg_new(ar);
-       if (!msg) return replmd_replicated_request_werror(ar, WERR_NOMEM);
+       if (!msg) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
        msg->dn = ar->search_msg->dn;
 
        ndr_err = ndr_push_struct_blob(&nuv_value, msg, &nuv,
@@ -5051,7 +6454,7 @@ static int replmd_replicated_uptodate_modify(struct replmd_replicated_request *a
                        struct repsFromToBlob *trf;
 
                        trf = talloc(ar, struct repsFromToBlob);
-                       if (!trf) return replmd_replicated_request_werror(ar, WERR_NOMEM);
+                       if (!trf) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
 
                        ndr_err = ndr_pull_struct_blob(&orf_el->values[i], trf, trf,
                                                       (ndr_pull_flags_fn_t)ndr_pull_repsFromToBlob);
@@ -5178,7 +6581,10 @@ static int replmd_replicated_uptodate_search_callback(struct ldb_request *req,
 
 static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *ar)
 {
-       struct ldb_context *ldb;
+       struct ldb_context *ldb = ldb_module_get_ctx(ar->module);
+       struct replmd_private *replmd_private =
+               talloc_get_type_abort(ldb_module_get_private(ar->module),
+               struct replmd_private);
        int ret;
        static const char *attrs[] = {
                "replUpToDateVector",
@@ -5188,9 +6594,13 @@ static int replmd_replicated_uptodate_vector(struct replmd_replicated_request *a
        };
        struct ldb_request *search_req;
 
-       ldb = ldb_module_get_ctx(ar->module);
        ar->search_msg = NULL;
 
+       /*
+        * Let the caller know that we did an originating updates
+        */
+       ar->objs->originating_updates = replmd_private->originating_updates;
+
        ret = ldb_build_search_req(&search_req,
                                   ldb,
                                   ar,
@@ -5220,7 +6630,6 @@ static int replmd_extended_replicated_objects(struct ldb_module *module, struct
        uint32_t i;
        struct replmd_private *replmd_private =
                talloc_get_type(ldb_module_get_private(module), struct replmd_private);
-       struct dsdb_control_replicated_update *rep_update;
 
        ldb = ldb_module_get_ctx(module);
 
@@ -5258,18 +6667,10 @@ static int replmd_extended_replicated_objects(struct ldb_module *module, struct
        if (req->controls) {
                req->controls = talloc_memdup(ar, req->controls,
                                              talloc_get_size(req->controls));
-               if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOMEM);
-       }
-
-       /* This allows layers further down to know if a change came in
-          over replication and what the replication flags were */
-       rep_update = talloc_zero(ar, struct dsdb_control_replicated_update);
-       if (rep_update == NULL) {
-               return ldb_module_oom(module);
+               if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
        }
-       rep_update->dsdb_repl_flags = objs->dsdb_repl_flags;
 
-       ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, rep_update);
+       ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, NULL);
        if (ret != LDB_SUCCESS) {
                return ret;
        }
@@ -5323,6 +6724,7 @@ static int replmd_extended_replicated_objects(struct ldb_module *module, struct
   process one linked attribute structure
  */
 static int replmd_process_linked_attribute(struct ldb_module *module,
+                                          struct replmd_private *replmd_private,
                                           struct la_entry *la_entry,
                                           struct ldb_request *parent)
 {
@@ -5343,11 +6745,10 @@ static int replmd_process_linked_attribute(struct ldb_module *module,
        struct ldb_result *target_res;
        const char *attrs[4];
        const char *attrs2[] = { "isDeleted", "isRecycled", NULL };
-       struct parsed_dn *pdn_list, *pdn;
+       struct parsed_dn *pdn_list, *pdn, *next;
        struct GUID guid = GUID_zero();
        NTSTATUS ntstatus;
        bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
-       const struct GUID *our_invocation_id;
 
        enum deletion_state deletion_state = OBJECT_NOT_DELETED;
        enum deletion_state target_deletion_state = OBJECT_NOT_DELETED;
@@ -5390,7 +6791,11 @@ linked_attributes[0]:
        /* find the attribute being modified */
        attr = dsdb_attribute_by_attributeID_id(schema, la->attid);
        if (attr == NULL) {
-               DEBUG(0, (__location__ ": Unable to find attributeID 0x%x\n", la->attid));
+               struct GUID_txt_buf guid_str;
+               ldb_asprintf_errstring(ldb, "Unable to find attributeID 0x%x for link on <GUID=%s>",
+                                      la->attid,
+                                      GUID_buf_string(&la->identifier->guid,
+                                                      &guid_str));
                talloc_free(tmp_ctx);
                return LDB_ERR_OPERATIONS_ERROR;
        }
@@ -5451,21 +6856,9 @@ linked_attributes[0]:
        }
 
        /* parse the existing links */
-       ret = get_parsed_dns(module, tmp_ctx, old_el, &pdn_list, attr->syntax->ldap_oid, parent);
-       if (ret != LDB_SUCCESS) {
-               talloc_free(tmp_ctx);
-               return ret;
-       }
-
-       /* get our invocationId */
-       our_invocation_id = samdb_ntds_invocation_id(ldb);
-       if (!our_invocation_id) {
-               ldb_debug_set(ldb, LDB_DEBUG_ERROR, __location__ ": unable to find invocationId\n");
-               talloc_free(tmp_ctx);
-               return LDB_ERR_OPERATIONS_ERROR;
-       }
+       ret = get_parsed_dns_trusted(module, replmd_private, tmp_ctx, old_el, &pdn_list,
+                                    attr->syntax->ldap_oid, parent);
 
-       ret = replmd_check_upgrade_links(pdn_list, old_el->num_values, old_el, our_invocation_id);
        if (ret != LDB_SUCCESS) {
                talloc_free(tmp_ctx);
                return ret;
@@ -5563,7 +6956,17 @@ linked_attributes[0]:
        }
 
        /* see if this link already exists */
-       pdn = parsed_dn_find(pdn_list, old_el->num_values, &guid, dsdb_dn->dn);
+       ret = parsed_dn_find(ldb, pdn_list, old_el->num_values,
+                            &guid,
+                            dsdb_dn->dn,
+                            &pdn, &next,
+                            attr->syntax->ldap_oid);
+       if (ret != LDB_SUCCESS) {
+               talloc_free(tmp_ctx);
+               return ret;
+       }
+
+
        if (pdn != NULL) {
                /* see if this update is newer than what we have already */
                struct GUID invocation_id = GUID_zero();
@@ -5599,7 +7002,11 @@ linked_attributes[0]:
 
                if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
                        /* remove the existing backlink */
-                       ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, false, attr, false);
+                       ret = replmd_add_backlink(module, replmd_private,
+                                                 schema, 
+                                                 msg->dn,
+                                                 &guid, false, attr,
+                                                 parent);
                        if (ret != LDB_SUCCESS) {
                                talloc_free(tmp_ctx);
                                return ret;
@@ -5619,19 +7026,44 @@ linked_attributes[0]:
 
                if (active) {
                        /* add the new backlink */
-                       ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, true, attr, false);
+                       ret = replmd_add_backlink(module, replmd_private,
+                                                 schema, 
+                                                 msg->dn,
+                                                 &guid, true, attr,
+                                                 parent);
                        if (ret != LDB_SUCCESS) {
                                talloc_free(tmp_ctx);
                                return ret;
                        }
                }
        } else {
+               unsigned offset;
                /* get a seq_num for this change */
                ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
                if (ret != LDB_SUCCESS) {
                        talloc_free(tmp_ctx);
                        return ret;
                }
+               /*
+                * We know where the new one needs to be, from the *next
+                * pointer into pdn_list.
+                */
+               if (next == NULL) {
+                       offset = old_el->num_values;
+               } else {
+                       if (next->dsdb_dn == NULL) {
+                               ret = really_parse_trusted_dn(tmp_ctx, ldb, next,
+                                                             attr->syntax->ldap_oid);
+                               if (ret != LDB_SUCCESS) {
+                                       return ret;
+                               }
+                       }
+                       offset = next - pdn_list;
+                       if (offset > old_el->num_values) {
+                               talloc_free(tmp_ctx);
+                               return LDB_ERR_OPERATIONS_ERROR;
+                       }
+               }
 
                old_el->values = talloc_realloc(msg->elements, old_el->values,
                                                struct ldb_val, old_el->num_values+1);
@@ -5639,22 +7071,31 @@ linked_attributes[0]:
                        ldb_module_oom(module);
                        return LDB_ERR_OPERATIONS_ERROR;
                }
+
+               if (offset != old_el->num_values) {
+                       memmove(&old_el->values[offset + 1], &old_el->values[offset],
+                               (old_el->num_values - offset) * sizeof(old_el->values[0]));
+               }
+
                old_el->num_values++;
 
-               ret = replmd_build_la_val(tmp_ctx, &old_el->values[old_el->num_values-1], dsdb_dn,
+               ret = replmd_build_la_val(tmp_ctx, &old_el->values[offset], dsdb_dn,
                                          &la->meta_data.originating_invocation_id,
                                          la->meta_data.originating_usn, seq_num,
                                          la->meta_data.originating_change_time,
                                          la->meta_data.version,
-                                         (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?false:true);
+                                         !active);
                if (ret != LDB_SUCCESS) {
                        talloc_free(tmp_ctx);
                        return ret;
                }
 
                if (active) {
-                       ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid,
-                                                 true, attr, false);
+                       ret = replmd_add_backlink(module, replmd_private,
+                                                 schema, 
+                                                 msg->dn,
+                                                 &guid, true, attr,
+                                                 parent);
                        if (ret != LDB_SUCCESS) {
                                talloc_free(tmp_ctx);
                                return ret;
@@ -5692,7 +7133,7 @@ linked_attributes[0]:
 
        old_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;
 
-       ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
+       ret = linked_attr_modify(module, msg, parent);
        if (ret != LDB_SUCCESS) {
                ldb_debug(ldb, LDB_DEBUG_WARNING, "Failed to apply linked attribute change '%s'\n%s\n",
                          ldb_errstring(ldb),
@@ -5738,6 +7179,8 @@ static int replmd_start_transaction(struct ldb_module *module)
                talloc_free(e);
        }
 
+       replmd_private->originating_updates = false;
+
        return ldb_next_start_trans(module);
 }
 
@@ -5750,26 +7193,20 @@ static int replmd_prepare_commit(struct ldb_module *module)
        struct replmd_private *replmd_private =
                talloc_get_type(ldb_module_get_private(module), struct replmd_private);
        struct la_entry *la, *prev;
-       struct la_backlink *bl;
        int ret;
 
-       /* walk the list backwards, to do the first entry first, as we
+       /*
+        * Walk the list of linked attributes from DRS replication.
+        *
+        * We walk backwards, to do the first entry first, as we
         * added the entries with DLIST_ADD() which puts them at the
-        * start of the list */
+        * start of the list
+        */
        for (la = DLIST_TAIL(replmd_private->la_list); la; la=prev) {
                prev = DLIST_PREV(la);
                DLIST_REMOVE(replmd_private->la_list, la);
-               ret = replmd_process_linked_attribute(module, la, NULL);
-               if (ret != LDB_SUCCESS) {
-                       replmd_txn_cleanup(replmd_private);
-                       return ret;
-               }
-       }
-
-       /* process our backlink list, creating and deleting backlinks
-          as necessary */
-       for (bl=replmd_private->la_backlinks; bl; bl=bl->next) {
-               ret = replmd_process_backlink(module, bl, NULL);
+               ret = replmd_process_linked_attribute(module, replmd_private,
+                                                     la, NULL);
                if (ret != LDB_SUCCESS) {
                        replmd_txn_cleanup(replmd_private);
                        return ret;