dsdb: Make less talloc() for parsed_dn.guid
[metze/samba/wip.git] / source4 / dsdb / samdb / ldb_modules / repl_meta_data.c
index 6596d457de1b33e1b86dc52a86d339e570fcdef9..347cf0f8d49986c539c1a42bdb32df063fa48512 100644 (file)
@@ -91,6 +91,7 @@ struct replmd_replicated_request {
        struct dsdb_extended_replicated_objects *objs;
 
        struct ldb_message *search_msg;
+       struct GUID local_parent_guid;
 
        uint64_t seq_num;
        bool is_urgent;
@@ -688,24 +689,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
@@ -717,7 +700,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) {
@@ -740,34 +722,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,
@@ -1082,9 +1042,9 @@ static int replmd_add(struct ldb_module *module, struct ldb_request *req)
        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);
@@ -1366,6 +1326,52 @@ 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)
+{
+       struct ldb_message_element new_el = {
+               .flags = LDB_FLAG_MOD_REPLACE,
+               .name = ldb_dn_get_rdn_name(msg->dn),
+               .num_values = 1,
+               .values = discard_const_p(struct ldb_val, rdn_new)
+       };
+       struct ldb_message_element old_el = {
+               .flags = LDB_FLAG_MOD_REPLACE,
+               .name = ldb_dn_get_rdn_name(msg->dn),
+               .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;
@@ -1401,7 +1407,6 @@ 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;
@@ -1409,11 +1414,19 @@ static int replmd_update_rpmd(struct ldb_module *module,
        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);
@@ -1617,7 +1630,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;
@@ -1647,13 +1660,18 @@ static int replmd_update_rpmd(struct ldb_module *module,
 
 struct parsed_dn {
        struct dsdb_dn *dsdb_dn;
-       struct GUID *guid;
+       struct GUID guid;
        struct ldb_val *v;
 };
 
 static int parsed_dn_compare(struct parsed_dn *pdn1, struct parsed_dn *pdn2)
 {
-       return GUID_compare(pdn1->guid, pdn2->guid);
+       return GUID_compare(&pdn1->guid, &pdn2->guid);
+}
+
+static int GUID_compare_struct(struct GUID *g1, struct GUID g2)
+{
+       return GUID_compare(g1, &g2);
 }
 
 static struct parsed_dn *parsed_dn_find(struct parsed_dn *pdn,
@@ -1673,7 +1691,7 @@ static struct parsed_dn *parsed_dn_find(struct parsed_dn *pdn,
                }
                return NULL;
        }
-       BINARY_ARRAY_SEARCH(pdn, count, guid, guid, GUID_compare, ret);
+       BINARY_ARRAY_SEARCH(pdn, count, guid, guid, GUID_compare_struct, ret);
        return ret;
 }
 
@@ -1714,16 +1732,10 @@ 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");
+               status = dsdb_get_extended_dn_guid(dn, &p->guid, "GUID");
                if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
                        /* 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));
@@ -1734,7 +1746,7 @@ 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;
                        }
@@ -2024,7 +2036,7 @@ static int replmd_modify_la_add(struct ldb_module *module,
 
        /* 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);
+               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);
@@ -2046,8 +2058,9 @@ static int replmd_modify_la_add(struct ldb_module *module,
                        uint32_t rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
 
                        if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
+                               struct GUID_txt_buf guid_str;
                                ldb_asprintf_errstring(ldb, "Attribute %s already exists for target GUID %s",
-                                                      el->name, GUID_string(tmp_ctx, p->guid));
+                                                      el->name, GUID_buf_string(&p->guid, &guid_str));
                                talloc_free(tmp_ctx);
                                /* error codes for 'member' need to be
                                   special cased */
@@ -2065,7 +2078,7 @@ static int replmd_modify_la_add(struct ldb_module *module,
                        }
                }
 
-               ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, true);
+               ret = replmd_add_backlink(module, schema, msg_guid, &dns[i].guid, true, schema_attr, true);
                if (ret != LDB_SUCCESS) {
                        talloc_free(tmp_ctx);
                        return ret;
@@ -2163,10 +2176,11 @@ static int replmd_modify_la_delete(struct ldb_module *module,
                struct parsed_dn *p2;
                uint32_t rmd_flags;
 
-               p2 = parsed_dn_find(old_dns, old_el->num_values, p->guid, NULL);
+               p2 = parsed_dn_find(old_dns, old_el->num_values, &p->guid, NULL);
                if (!p2) {
+                       struct GUID_txt_buf buf;
                        ldb_asprintf_errstring(ldb, "Attribute %s doesn't exist for target GUID %s",
-                                              el->name, GUID_string(tmp_ctx, p->guid));
+                                              el->name, GUID_buf_string(&p->guid, &buf));
                        if (ldb_attr_cmp(el->name, "member") == 0) {
                                return LDB_ERR_UNWILLING_TO_PERFORM;
                        } else {
@@ -2175,8 +2189,9 @@ static int replmd_modify_la_delete(struct ldb_module *module,
                }
                rmd_flags = dsdb_dn_rmd_flags(p2->dsdb_dn->dn);
                if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
+                       struct GUID_txt_buf buf;
                        ldb_asprintf_errstring(ldb, "Attribute %s already deleted for target GUID %s",
-                                              el->name, GUID_string(tmp_ctx, p->guid));
+                                              el->name, GUID_buf_string(&p->guid, &buf));
                        if (ldb_attr_cmp(el->name, "member") == 0) {
                                return LDB_ERR_UNWILLING_TO_PERFORM;
                        } else {
@@ -2192,7 +2207,7 @@ static int replmd_modify_la_delete(struct ldb_module *module,
                struct parsed_dn *p = &old_dns[i];
                uint32_t rmd_flags;
 
-               if (el->num_values && parsed_dn_find(dns, el->num_values, p->guid, NULL) == NULL) {
+               if (el->num_values && parsed_dn_find(dns, el->num_values, &p->guid, NULL) == NULL) {
                        continue;
                }
 
@@ -2206,7 +2221,7 @@ static int replmd_modify_la_delete(struct ldb_module *module,
                        return ret;
                }
 
-               ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, true);
+               ret = replmd_add_backlink(module, schema, msg_guid, &old_dns[i].guid, false, schema_attr, true);
                if (ret != LDB_SUCCESS) {
                        talloc_free(tmp_ctx);
                        return ret;
@@ -2289,13 +2304,13 @@ static int replmd_modify_la_replace(struct ldb_module *module,
 
                if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
 
-               ret = replmd_add_backlink(module, schema, msg_guid, old_dns[i].guid, false, schema_attr, false);
+               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;
                }
 
-               p = parsed_dn_find(dns, el->num_values, old_p->guid, NULL);
+               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;
@@ -2317,7 +2332,7 @@ static int replmd_modify_la_replace(struct ldb_module *module,
 
                if (old_dns &&
                    (old_p = parsed_dn_find(old_dns,
-                                           old_num_values, p->guid, NULL)) != NULL) {
+                                           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,
@@ -2344,7 +2359,7 @@ static int replmd_modify_la_replace(struct ldb_module *module,
                        num_new_values++;
                }
 
-               ret = replmd_add_backlink(module, schema, msg_guid, dns[i].guid, true, schema_attr, false);
+               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;
@@ -3527,6 +3542,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;
 }
 
@@ -3578,6 +3597,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
@@ -3755,7 +3815,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
  */
@@ -3773,36 +3833,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) {
@@ -3821,7 +3882,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,
@@ -3851,40 +3912,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",
@@ -3901,12 +3958,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;
@@ -3948,15 +4002,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);
 }
 
 /*
@@ -3978,16 +4072,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
  */
@@ -4002,19 +4086,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);
        }
@@ -4056,8 +4149,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);
        }
 
@@ -4065,23 +4162,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);
@@ -4154,12 +4248,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);
        }
@@ -4217,9 +4313,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) {
@@ -4239,6 +4339,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 {
@@ -4268,10 +4394,11 @@ 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_value.data) {
+       if (ar->objs->objects[ar->index_current].parent_guid == NULL) {
                if (ar->search_msg != NULL) {
                        return replmd_replicated_apply_merge(ar);
                } else {
@@ -4279,12 +4406,11 @@ static int replmd_replicated_apply_search_for_parent(struct replmd_replicated_re
                }
        }
 
-       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);
+       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_NOMEM);
-       talloc_free(tmp_str);
 
        ret = ldb_build_search_req(&search_req,
                                   ldb,
@@ -4315,50 +4441,222 @@ static int replmd_replicated_apply_search_for_parent(struct replmd_replicated_re
  */
 static int replmd_replicated_handle_rename(struct replmd_replicated_request *ar,
                                           struct ldb_message *msg,
-                                          struct ldb_request *parent)
+                                          struct ldb_request *parent,
+                                          bool *renamed)
 {
-       struct ldb_request *req;
        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)));
 
 
-       res = talloc_zero(tmp_ctx, struct ldb_result);
-       if (!res) {
+       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);
-               return ldb_oom(ldb_module_get_ctx(ar->module));
+               *renamed = true;
+               return ret;
        }
 
-       /* 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);
-       if (ret != LDB_SUCCESS) {
+       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 = dsdb_request_add_controls(req, DSDB_MODIFY_RELAX);
+       ret = samdb_rodc(ldb_module_get_ctx(ar->module), &rodc);
        if (ret != LDB_SUCCESS) {
-               talloc_free(tmp_ctx);
-               return ret;
+               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
+        */
 
-       ret = ldb_next_request(ar->module, req);
+       conflict_dn = msg->dn;
 
-       if (ret == LDB_SUCCESS) {
-               ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+       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;
+       }
+
+       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 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;
+       }
+
+       /*
+        * 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) {
+
+               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));
+
+                       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(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);
+               }
+
+               msg->dn = new_dn;
+               *renamed = true;
+               return LDB_SUCCESS;
+       }
+
+       /* 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;
+       }
+
+       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;
+       }
+
+       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) {
+               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;
+       }
+
+       /*
+        * 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) {
+               goto failed;
+       }
+
+       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)));
+
+
+       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;
 }
@@ -4375,6 +4673,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;
@@ -4386,10 +4685,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;
@@ -4400,7 +4710,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));
                }
 
@@ -4409,12 +4719,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 {
                /*
@@ -4427,49 +4777,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),
@@ -4478,6 +4789,14 @@ 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;
@@ -4504,26 +4823,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];
@@ -4591,14 +4894,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;
@@ -4648,7 +4958,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));
        }
 
@@ -4685,8 +4995,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);
        }
 
@@ -4745,7 +5059,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;
 
                /*
@@ -4787,33 +5102,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);
                }
@@ -4836,6 +5218,10 @@ 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[] = { "*", "parentGUID", "instanceType",
+                                      "replPropertyMetaData", "nTSecurityDescriptor",
+                                      NULL };
+       struct GUID_txt_buf guid_str_buf;
 
        if (ar->index_current >= ar->objs->num_objects) {
                /* done with it, go to next stage */
@@ -4846,12 +5232,11 @@ 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);
 
        ret = ldb_build_search_req(&search_req,
                                   ldb,
@@ -4859,7 +5244,7 @@ static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
                                   ar->objs->partition_dn,
                                   LDB_SCOPE_SUBTREE,
                                   filter,
-                                  NULL,
+                                  attrs,
                                   NULL,
                                   ar,
                                   replmd_replicated_apply_search_callback,
@@ -5692,7 +6077,7 @@ 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, schema, &la->identifier->guid, &guid, false, attr, true);
                        if (ret != LDB_SUCCESS) {
                                talloc_free(tmp_ctx);
                                return ret;
@@ -5712,7 +6097,7 @@ 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, schema, &la->identifier->guid, &guid, true, attr, true);
                        if (ret != LDB_SUCCESS) {
                                talloc_free(tmp_ctx);
                                return ret;
@@ -5747,7 +6132,7 @@ linked_attributes[0]:
 
                if (active) {
                        ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid,
-                                                 true, attr, false);
+                                                 true, attr, true);
                        if (ret != LDB_SUCCESS) {
                                talloc_free(tmp_ctx);
                                return ret;