replmd: fix variable names in replmd_check_upgrade_links
[sfrench/samba-autobuild/.git] / source4 / dsdb / samdb / ldb_modules / repl_meta_data.c
index c6dc6c663fe1fb4d9fa482c0554cfdf794d5f396..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,
@@ -2173,10 +2281,13 @@ static int replmd_modify_la_delete(struct ldb_module *module,
 {
        unsigned int i;
        struct parsed_dn *dns, *old_dns;
-       TALLOC_CTX *tmp_ctx = talloc_new(msg);
+       TALLOC_CTX *tmp_ctx = NULL;
        int ret;
        const struct GUID *invocation_id;
        struct ldb_context *ldb = ldb_module_get_ctx(module);
+       struct ldb_control *vanish_links_ctrl = NULL;
+       bool vanish_links = false;
+       unsigned int num_to_delete = el->num_values;
        NTTIME now;
 
        unix_to_nt_time(&now, t);
@@ -2191,6 +2302,11 @@ static int replmd_modify_la_delete(struct ldb_module *module,
                return LDB_ERR_NO_SUCH_ATTRIBUTE;
        }
 
+       tmp_ctx = talloc_new(msg);
+       if (tmp_ctx == NULL) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
        ret = get_parsed_dns(module, tmp_ctx, el, &dns, schema_attr->syntax->ldap_oid, parent);
        if (ret != LDB_SUCCESS) {
                talloc_free(tmp_ctx);
@@ -2205,6 +2321,7 @@ static int replmd_modify_la_delete(struct ldb_module *module,
 
        invocation_id = samdb_ntds_invocation_id(ldb);
        if (!invocation_id) {
+               talloc_free(tmp_ctx);
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
@@ -2214,11 +2331,20 @@ static int replmd_modify_la_delete(struct ldb_module *module,
                return ret;
        }
 
+       if (parent) {
+               vanish_links_ctrl = ldb_request_get_control(parent, DSDB_CONTROL_REPLMD_VANISH_LINKS);
+               if (vanish_links_ctrl) {
+                       vanish_links = true;
+                       vanish_links_ctrl->critical = false;
+               }
+       }
+
+       el->num_values = 0;
        el->values = NULL;
 
        /* see if we are being asked to delete any links that
           don't exist or are already deleted */
-       for (i=0; i<el->num_values; i++) {
+       for (i=0; i < num_to_delete; i++) {
                struct parsed_dn *p = &dns[i];
                struct parsed_dn *p2;
                uint32_t rmd_flags;
@@ -2229,52 +2355,119 @@ static int replmd_modify_la_delete(struct ldb_module *module,
                        ldb_asprintf_errstring(ldb, "Attribute %s doesn't exist for target GUID %s",
                                               el->name, GUID_buf_string(&p->guid, &buf));
                        if (ldb_attr_cmp(el->name, "member") == 0) {
+                               talloc_free(tmp_ctx);
                                return LDB_ERR_UNWILLING_TO_PERFORM;
                        } else {
+                               talloc_free(tmp_ctx);
                                return LDB_ERR_NO_SUCH_ATTRIBUTE;
                        }
                }
                rmd_flags = dsdb_dn_rmd_flags(p2->dsdb_dn->dn);
                if (rmd_flags & DSDB_RMD_FLAG_DELETED) {
                        struct GUID_txt_buf buf;
+                       const char *guid_str = GUID_buf_string(&p->guid, &buf);
+                       if (vanish_links) {
+                               DEBUG(0, ("Deleting deleted linked attribute %s to %s, "
+                                         "because vanish_links control is set\n",
+                                         el->name, guid_str));
+                               continue;
+                       }
                        ldb_asprintf_errstring(ldb, "Attribute %s already deleted for target GUID %s",
-                                              el->name, GUID_buf_string(&p->guid, &buf));
+                                              el->name, guid_str);
                        if (ldb_attr_cmp(el->name, "member") == 0) {
+                               talloc_free(tmp_ctx);
                                return LDB_ERR_UNWILLING_TO_PERFORM;
                        } else {
+                               talloc_free(tmp_ctx);
                                return LDB_ERR_NO_SUCH_ATTRIBUTE;
                        }
                }
        }
 
-       /* for each new value, see if it exists already with the same GUID
-          if it is not already deleted and matches the delete list then delete it
-       */
-       for (i=0; i<old_el->num_values; i++) {
-               struct parsed_dn *p = &old_dns[i];
-               uint32_t rmd_flags;
+       if (vanish_links) {
+               if (num_to_delete == old_el->num_values || num_to_delete == 0) {
+                       el->flags = LDB_FLAG_MOD_REPLACE;
 
-               if (el->num_values && parsed_dn_find(dns, el->num_values, &p->guid, NULL) == NULL) {
-                       continue;
+                       for (i = 0; i < old_el->num_values; i++) {
+                               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;
+                               }
+                       }
+                       talloc_free(tmp_ctx);
+                       return LDB_SUCCESS;
+               } else {
+                       unsigned int num_values = 0;
+                       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,
+                                                                 replmd_private,
+                                                                 schema,
+                                                                 msg_guid,
+                                                                 &old_dns[i].guid,
+                                                                 false,
+                                                                 schema_attr,
+                                                                 true);
+                                       if (ret != LDB_SUCCESS) {
+                                               talloc_free(tmp_ctx);
+                                               return ret;
+                                       }
+                                       old_dns[i].v->length = 0;
+                               } else {
+                                       num_values++;
+                               }
+                       }
+                       for (i = 0; i < old_el->num_values; i++) {
+                               if (old_el->values[i].length != 0) {
+                                       old_el->values[j] = old_el->values[i];
+                                       j++;
+                                       if (j == num_values) {
+                                               break;
+                                       }
+                               }
+                       }
+                       old_el->num_values = num_values;
                }
+       } else {
 
-               rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
-               if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
+               /* for each new value, see if it exists already with the same GUID
+                  if it is not already deleted and matches the delete list then delete it
+               */
+               for (i=0; i<old_el->num_values; i++) {
+                       struct parsed_dn *p = &old_dns[i];
+                       uint32_t rmd_flags;
 
-               ret = replmd_update_la_val(old_el->values, p->v, p->dsdb_dn, p->dsdb_dn,
-                                          invocation_id, seq_num, seq_num, now, 0, true);
-               if (ret != LDB_SUCCESS) {
-                       talloc_free(tmp_ctx);
-                       return ret;
-               }
+                       if (num_to_delete && parsed_dn_find(dns, num_to_delete, &p->guid, NULL) == NULL) {
+                               continue;
+                       }
 
-               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;
+                       rmd_flags = dsdb_dn_rmd_flags(p->dsdb_dn->dn);
+                       if (rmd_flags & DSDB_RMD_FLAG_DELETED) continue;
+
+                       ret = replmd_update_la_val(old_el->values, p->v, p->dsdb_dn, p->dsdb_dn,
+                                                  invocation_id, seq_num, seq_num, now, 0, true);
+                       if (ret != LDB_SUCCESS) {
+                               talloc_free(tmp_ctx);
+                               return ret;
+                       }
+                       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;
+                       }
                }
        }
-
        el->values = talloc_steal(msg->elements, old_el->values);
        el->num_values = old_el->num_values;
 
@@ -2291,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,
@@ -2351,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;
@@ -2406,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;
@@ -2445,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)
@@ -2458,18 +2657,30 @@ 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) {
-               /* don't do anything special for linked attributes */
+               /*
+                * Nothing special is required for modifying or vanishing links
+                * in fl2000 since they are just strings in a multi-valued
+                * attribute.
+                */
+               struct ldb_control *ctrl = ldb_request_get_control(parent,
+                                                                  DSDB_CONTROL_REPLMD_VANISH_LINKS);
+               if (ctrl) {
+                       ctrl->critical = false;
+               }
                return LDB_SUCCESS;
        }
 
+       /*
+        * TODO:
+        *
+        * We should restrict this to the intersection of the list of
+        * linked attributes in the schema and the list of attributes
+        * being modified.
+        *
+        * This will help performance a little, as otherwise we have
+        * to allocate the entire object value-by-value.
+        */
        ret = dsdb_module_search_dn(module, msg, &res, msg->dn, NULL,
                                    DSDB_FLAG_NEXT_MODULE |
                                    DSDB_SEARCH_SHOW_RECYCLED |
@@ -2514,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,
@@ -2656,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;
@@ -3005,6 +3226,7 @@ static int replmd_delete_remove_link(struct ldb_module *module,
                const struct dsdb_attribute *target_attr;
                struct ldb_message_element *el2;
                struct ldb_val dn_val;
+               uint32_t dsdb_flags = 0;
 
                if (dsdb_dn_is_deleted_val(&el->values[i])) {
                        continue;
@@ -3048,7 +3270,13 @@ static int replmd_delete_remove_link(struct ldb_module *module,
                el2->values = &dn_val;
                el2->num_values = 1;
 
-               ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE, parent);
+               /*
+                * Ensure that we tell the modification to vanish any linked
+                * attributes (not simply mark them as isDeleted = TRUE)
+                */
+               dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
+
+               ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, parent);
                if (ret != LDB_SUCCESS) {
                        talloc_free(tmp_ctx);
                        return ret;
@@ -3136,6 +3364,7 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request
                NULL
        };
        unsigned int i, el_count = 0;
+       uint32_t dsdb_flags = 0;
        enum deletion_state deletion_state, next_deletion_state;
 
        if (ldb_dn_is_special(req->op.del.dn)) {
@@ -3476,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;
                                }
@@ -3495,6 +3725,12 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request
                                if (sa->searchFlags & SEARCH_FLAG_PRESERVEONDELETE) {
                                        continue;
                                }
+                       } else {
+                               /*
+                                * Ensure that we tell the modification to vanish any linked
+                                * attributes (not simply mark them as isDeleted = TRUE)
+                                */
+                               dsdb_flags |= DSDB_REPLMD_VANISH_LINKS;
                        }
                        ret = ldb_msg_add_empty(msg, el->name, LDB_FLAG_MOD_DELETE, &el);
                        if (ret != LDB_SUCCESS) {
@@ -3592,7 +3828,7 @@ static int replmd_delete_internals(struct ldb_module *module, struct ldb_request
                msg->dn = new_dn;
        }
 
-       ret = dsdb_module_modify(module, msg, DSDB_FLAG_OWN_MODULE, req);
+       ret = dsdb_module_modify(module, msg, dsdb_flags|DSDB_FLAG_OWN_MODULE, req);
        if (ret != LDB_SUCCESS) {
                ldb_asprintf_errstring(ldb, "replmd_delete: Failed to modify object %s in delete - %s",
                                       ldb_dn_get_linearized(old_dn), ldb_errstring(ldb));
@@ -3949,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));
@@ -4373,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,
@@ -4488,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,
@@ -4503,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);
@@ -4653,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,
@@ -4881,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++) {
@@ -5315,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,
@@ -5521,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++) {
@@ -5573,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,
@@ -5608,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);
@@ -5735,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",
@@ -5745,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,
@@ -5777,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);
 
@@ -5815,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);
-       }
-
-       /* This allows layers further down to know if a change came in
-          over replication and what the replication flags were */
-       rep_update = talloc_zero(ar, struct dsdb_control_replicated_update);
-       if (rep_update == NULL) {
-               return ldb_module_oom(module);
+               if (!req->controls) return replmd_replicated_request_werror(ar, WERR_NOT_ENOUGH_MEMORY);
        }
-       rep_update->dsdb_repl_flags = objs->dsdb_repl_flags;
 
-       ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, rep_update);
+       ret = ldb_request_add_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID, false, NULL);
        if (ret != LDB_SUCCESS) {
                return ret;
        }
@@ -5880,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)
 {
@@ -5947,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;
        }
@@ -6156,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;
@@ -6176,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;
@@ -6203,15 +6446,16 @@ linked_attributes[0]:
                                          la->meta_data.originating_usn, seq_num,
                                          la->meta_data.originating_change_time,
                                          la->meta_data.version,
-                                         (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?false:true);
+                                         !active);
                if (ret != LDB_SUCCESS) {
                        talloc_free(tmp_ctx);
                        return ret;
                }
 
                if (active) {
-                       ret = replmd_add_backlink(module, schema, &la->identifier->guid, &guid,
-                                                 true, attr, 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;
@@ -6249,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),
@@ -6295,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);
 }
 
@@ -6316,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;