replmd: fix variable names in replmd_check_upgrade_links
[sfrench/samba-autobuild/.git] / source4 / dsdb / samdb / ldb_modules / repl_meta_data.c
index 183747a38a584ac854adb570728ebb10a35d9c3b..c9deb36c66df408a5ffab81bb3bfb32d2d4cd200 100644 (file)
@@ -68,6 +68,7 @@ struct replmd_private {
                uint64_t mod_usn_urgent;
        } *ncs;
        struct ldb_dn *schema_dn;
+       bool originating_updates;
 };
 
 struct la_entry {
@@ -270,6 +271,55 @@ struct la_backlink {
        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
@@ -360,14 +410,16 @@ static int replmd_process_backlink(struct ldb_module *module, struct la_backlink
   add a backlink to the list of backlinks to add/delete in the prepare
   commit
  */
-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_add_backlink(struct ldb_module *module,
+                              struct replmd_private *replmd_private,
+                              const struct dsdb_schema *schema,
+                              struct GUID *forward_guid,
+                              struct GUID *target_guid, bool active,
+                              const struct dsdb_attribute *schema_attr,
+                              bool immediate)
 {
        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);
 
        target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID ^ 1);
        if (!target_attr) {
@@ -525,6 +577,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) {
@@ -791,7 +846,9 @@ static int replmd_build_la_val(TALLOC_CTX *mem_ctx, struct ldb_val *v, struct ds
   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,
+static int replmd_add_fix_la(struct ldb_module *module,
+                            struct replmd_private *replmd_private,
+                            struct ldb_message_element *el,
                             uint64_t seq_num, const struct GUID *invocationId, NTTIME now,
                             struct GUID *guid, const struct dsdb_attribute *sa, struct ldb_request *parent)
 {
@@ -809,6 +866,11 @@ static int replmd_add_fix_la(struct ldb_module *module, struct ldb_message_eleme
                NTSTATUS status;
                int ret;
 
+               if (dsdb_dn == NULL) {
+                       talloc_free(tmp_ctx);
+                       return LDB_ERR_INVALID_DN_SYNTAX;
+               }
+
                /* 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");
@@ -832,7 +894,9 @@ static int replmd_add_fix_la(struct ldb_module *module, struct ldb_message_eleme
                        return ret;
                }
 
-               ret = replmd_add_backlink(module, schema, guid, &target_guid, true, sa, false);
+               ret = replmd_add_backlink(module, replmd_private,
+                                         schema, guid, &target_guid, true, sa,
+                                         false);
                if (ret != LDB_SUCCESS) {
                        talloc_free(tmp_ctx);
                        return ret;
@@ -1014,7 +1078,8 @@ static int replmd_add(struct ldb_module *module, struct ldb_request *req)
                }
 
                if (sa->linkID != 0 && functional_level > DS_DOMAIN_FUNCTION_2000) {
-                       ret = replmd_add_fix_la(module, e, ac->seq_num,
+                       ret = replmd_add_fix_la(module, replmd_private, e,
+                                               ac->seq_num,
                                                &ac->our_invocation_id, now,
                                                &guid, sa, req);
                        if (ret != LDB_SUCCESS) {
@@ -1254,6 +1319,18 @@ 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) {
@@ -1376,15 +1453,21 @@ static int replmd_update_rpmd_rdn_attr(struct ldb_context *ldb,
                                       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 = ldb_dn_get_rdn_name(msg->dn),
+               .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 = ldb_dn_get_rdn_name(msg->dn),
+               .name = attr_name,
                .num_values = rdn_old ? 1 : 0,
                .values = discard_const_p(struct ldb_val, rdn_old)
        };
@@ -1446,7 +1529,7 @@ static int replmd_update_rpmd(struct ldb_module *module,
        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 {
@@ -1751,6 +1834,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) {
@@ -1800,13 +1884,18 @@ static int get_parsed_dns(struct ldb_module *module, TALLOC_CTX *mem_ctx,
                } 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;
        }
-
-       TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
-
+       if (! values_are_sorted) {
+               TYPESAFE_QSORT(*pdn, el->num_values, parsed_dn_compare);
+       }
        return LDB_SUCCESS;
 }
 
@@ -1903,10 +1992,10 @@ 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 parsed_dn *dns, uint32_t count,
+                                     struct ldb_message_element *el,
+                                     const struct GUID *invocation_id)
 {
        uint32_t i;
        for (i=0; i<count; i++) {
@@ -1914,14 +2003,29 @@ static int replmd_check_upgrade_links(struct parsed_dn *dns, uint32_t count, str
                uint32_t version;
                int ret;
 
-               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;
                }
 
                /* 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;
                }
@@ -2034,6 +2138,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,
@@ -2125,7 +2230,9 @@ 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, replmd_private,
+                                         schema, msg_guid, &dns[i].guid,
+                                         true, schema_attr, true);
                if (ret != LDB_SUCCESS) {
                        talloc_free(tmp_ctx);
                        return ret;
@@ -2161,6 +2268,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,
@@ -2281,7 +2389,12 @@ static int replmd_modify_la_delete(struct ldb_module *module,
                        el->flags = LDB_FLAG_MOD_REPLACE;
 
                        for (i = 0; i < old_el->num_values; i++) {
-                               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_guid,
+                                                         &old_dns[i].guid,
+                                                         false, schema_attr,
+                                                         true);
                                if (ret != LDB_SUCCESS) {
                                        talloc_free(tmp_ctx);
                                        return ret;
@@ -2294,8 +2407,16 @@ static int replmd_modify_la_delete(struct ldb_module *module,
                        unsigned int j = 0;
                        for (i = 0; i < old_el->num_values; i++) {
                                if (parsed_dn_find(dns, num_to_delete, &old_dns[i].guid, NULL) != NULL) {
-                                       /* The element is in the delete list. mark it dead. */
-                                       ret = replmd_add_backlink(module, schema, msg_guid, &old_dns[i].guid, false, schema_attr, true);
+                                       /* The element is in the delete list.
+                                          mark it dead. */
+                                       ret = replmd_add_backlink(module,
+                                                                 replmd_private,
+                                                                 schema,
+                                                                 msg_guid,
+                                                                 &old_dns[i].guid,
+                                                                 false,
+                                                                 schema_attr,
+                                                                 true);
                                        if (ret != LDB_SUCCESS) {
                                                talloc_free(tmp_ctx);
                                                return ret;
@@ -2338,7 +2459,9 @@ static int replmd_modify_la_delete(struct ldb_module *module,
                                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_guid, &p->guid,
+                                                 false, schema_attr, true);
                        if (ret != LDB_SUCCESS) {
                                talloc_free(tmp_ctx);
                                return ret;
@@ -2361,6 +2484,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,
@@ -2421,7 +2545,9 @@ 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, replmd_private,
+                                         schema, msg_guid, &old_dns[i].guid,
+                                         false, schema_attr, false);
                if (ret != LDB_SUCCESS) {
                        talloc_free(tmp_ctx);
                        return ret;
@@ -2476,7 +2602,9 @@ 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, replmd_private,
+                                         schema, msg_guid, &dns[i].guid,
+                                         true, schema_attr, false);
                if (ret != LDB_SUCCESS) {
                        talloc_free(tmp_ctx);
                        return ret;
@@ -2515,6 +2643,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)
@@ -2528,13 +2657,6 @@ static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
        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) {
                /*
                 * Nothing special is required for modifying or vanishing links
@@ -2549,6 +2671,16 @@ static int replmd_modify_handle_linked_attribs(struct ldb_module *module,
                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 |
@@ -2593,13 +2725,22 @@ 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_guid, 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_guid, 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_guid, parent);
                        break;
                default:
                        ldb_asprintf_errstring(ldb,
@@ -2735,7 +2876,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;
@@ -3563,9 +3705,10 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request
                                        ldb_asprintf_errstring(ldb,
                                                               __location__
                                                               ": Failed to remove backlink of "
-                                                              "%s when deleting %s",
+                                                              "%s when deleting %s: %s",
                                                               el->name,
-                                                              old_dn_str);
+                                                              old_dn_str,
+                                                              ldb_errstring(ldb));
                                        talloc_free(tmp_ctx);
                                        return LDB_ERR_OPERATIONS_ERROR;
                                }
@@ -4042,9 +4185,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));
@@ -4466,7 +4609,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,
@@ -4581,7 +4724,7 @@ static int replmd_replicated_apply_search_for_parent(struct replmd_replicated_re
                                  &guid_str_buf);
 
        filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
-       if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
+       if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
 
        ret = ldb_build_search_req(&search_req,
                                   ldb,
@@ -4596,7 +4739,7 @@ static int replmd_replicated_apply_search_for_parent(struct replmd_replicated_re
                                   ar->req);
        LDB_REQ_SET_LOCATION(search_req);
 
-       ret = dsdb_request_add_controls(search_req, 
+       ret = dsdb_request_add_controls(search_req,
                                        DSDB_SEARCH_SHOW_RECYCLED|
                                        DSDB_SEARCH_SHOW_DELETED|
                                        DSDB_SEARCH_SHOW_EXTENDED_DN);
@@ -4746,7 +4889,7 @@ static int replmd_replicated_handle_rename(struct replmd_replicated_request *ar,
                                                                  "Failed to form conflict DN for %s\n",
                                                                  ldb_dn_get_linearized(msg->dn));
 
-                       return replmd_replicated_request_werror(ar, WERR_NOMEM);
+                       return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
                }
 
                ret = dsdb_module_rename(ar->module, ar->search_msg->dn, new_dn,
@@ -4974,7 +5117,7 @@ static int replmd_replicated_apply_merge(struct replmd_replicated_request *ar)
        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++) {
@@ -5408,7 +5551,7 @@ static int replmd_replicated_apply_next(struct replmd_replicated_request *ar)
                                  &guid_str_buf);
 
        filter = talloc_asprintf(ar, "(objectGUID=%s)", tmp_str);
-       if (!filter) return replmd_replicated_request_werror(ar, WERR_NOMEM);
+       if (!filter) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
 
        ret = ldb_build_search_req(&search_req,
                                   ldb,
@@ -5614,7 +5757,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++) {
@@ -5666,7 +5809,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,
@@ -5701,7 +5844,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);
@@ -5828,7 +5971,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",
@@ -5838,9 +5984,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,
@@ -5870,7 +6020,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);
 
@@ -5908,18 +6057,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);
+               if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
        }
 
-       /* 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);
-       }
-       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;
        }
@@ -5973,6 +6114,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)
 {
@@ -6040,7 +6182,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;
        }
@@ -6249,7 +6395,9 @@ 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, true);
+                       ret = replmd_add_backlink(module, replmd_private,
+                                                 schema, &la->identifier->guid,
+                                                 &guid, false, attr, true);
                        if (ret != LDB_SUCCESS) {
                                talloc_free(tmp_ctx);
                                return ret;
@@ -6269,7 +6417,9 @@ linked_attributes[0]:
 
                if (active) {
                        /* add the new backlink */
-                       ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid, true, attr, true);
+                       ret = replmd_add_backlink(module, replmd_private,
+                                                 schema, &la->identifier->guid,
+                                                 &guid, true, attr, true);
                        if (ret != LDB_SUCCESS) {
                                talloc_free(tmp_ctx);
                                return ret;
@@ -6303,8 +6453,9 @@ linked_attributes[0]:
                }
 
                if (active) {
-                       ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid,
-                                                 true, attr, true);
+                       ret = replmd_add_backlink(module, replmd_private,
+                                                 schema, &la->identifier->guid,
+                                                 &guid, true, attr, true);
                        if (ret != LDB_SUCCESS) {
                                talloc_free(tmp_ctx);
                                return ret;
@@ -6342,7 +6493,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),
@@ -6388,6 +6539,8 @@ static int replmd_start_transaction(struct ldb_module *module)
                talloc_free(e);
        }
 
+       replmd_private->originating_updates = false;
+
        return ldb_next_start_trans(module);
 }
 
@@ -6409,7 +6562,8 @@ static int replmd_prepare_commit(struct ldb_module *module)
        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);
+               ret = replmd_process_linked_attribute(module, replmd_private,
+                                                     la, NULL);
                if (ret != LDB_SUCCESS) {
                        replmd_txn_cleanup(replmd_private);
                        return ret;