static int replmd_delete_remove_link(struct ldb_module *module,
				     const struct dsdb_schema *schema,
				     struct replmd_private *replmd_private,
				     struct ldb_dn *dn,
				     struct GUID *guid,
				     struct ldb_message_element *el,
				     const struct dsdb_attribute *sa,
				     struct ldb_request *parent,
				     bool *caller_should_vanish)
{
	unsigned int i;
	TALLOC_CTX *tmp_ctx = talloc_new(module);
	struct ldb_context *ldb = ldb_module_get_ctx(module);

	for (i=0; i<el->num_values; i++) {
		struct dsdb_dn *dsdb_dn;
		int ret;
		struct ldb_message *msg;
		const struct dsdb_attribute *target_attr;
		struct ldb_message_element *el2;
		const char *dn_str;
		struct ldb_val dn_val;
		uint32_t dsdb_flags = 0;
		const char *attrs[] = { NULL, NULL };
		struct ldb_result *link_res;
		struct ldb_message *link_msg;
		struct ldb_message_element *link_el;
		struct parsed_dn *link_dns;
		struct parsed_dn *p = NULL, *unused = NULL;

		if (dsdb_dn_is_deleted_val(&el->values[i])) {
			continue;
		}

		dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
		if (!dsdb_dn) {
			talloc_free(tmp_ctx);
			return LDB_ERR_OPERATIONS_ERROR;
		}

		/* remove the link */
		msg = ldb_msg_new(tmp_ctx);
		if (!msg) {
			ldb_module_oom(module);
			talloc_free(tmp_ctx);
			return LDB_ERR_OPERATIONS_ERROR;
		}

		msg->dn = dsdb_dn->dn;

		target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
		if (target_attr == NULL) {
			continue;
		}
		attrs[0] = target_attr->lDAPDisplayName;

		ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName,
					LDB_FLAG_MOD_DELETE, &el2);
		if (ret != LDB_SUCCESS) {
			ldb_module_oom(module);
			talloc_free(tmp_ctx);
			return LDB_ERR_OPERATIONS_ERROR;
		}

		ret = dsdb_module_search_dn(module, tmp_ctx, &link_res,
					    msg->dn, attrs,
					    DSDB_FLAG_NEXT_MODULE |
					    DSDB_SEARCH_SHOW_EXTENDED_DN |
					    DSDB_SEARCH_SHOW_RECYCLED,
					    parent);

		if (ret == LDB_ERR_NO_SUCH_OBJECT) {
			DBG_WARNING("Failed to find forward link object %s "
				    "to remove backlink %s on %s",
				    ldb_dn_get_linearized(msg->dn),
				    sa->lDAPDisplayName,
				    ldb_dn_get_linearized(dn));
			*caller_should_vanish = true;
			continue;
		}

		if (ret != LDB_SUCCESS) {
			talloc_free(tmp_ctx);
			return ret;
		}

		link_msg = link_res->msgs[0];
		link_el = ldb_msg_find_element(link_msg,
					       target_attr->lDAPDisplayName);
		if (link_el == NULL) {
			DBG_WARNING("Failed to find forward link on %s "
				    "as %s to remove backlink %s on %s",
				    ldb_dn_get_linearized(msg->dn),
				    target_attr->lDAPDisplayName,
				    sa->lDAPDisplayName,
				    ldb_dn_get_linearized(dn));
			*caller_should_vanish = true;
			continue;
		}

		/*
		 * This call 'upgrades' the links in link_dns, but we
		 * do not commit the result back into the database, so
		 * this is safe to call in FL2000 or on databases that
		 * have been run at that level in the past.
		 */
		ret = get_parsed_dns_trusted_fallback(module, replmd_private,
						tmp_ctx,
						link_el, &link_dns,
						target_attr->syntax->ldap_oid,
						parent);
		if (ret != LDB_SUCCESS) {
			talloc_free(tmp_ctx);
			return ret;
		}

		ret = parsed_dn_find(ldb, link_dns, link_el->num_values,
				     guid, dn,
				     data_blob_null, 0,
				     &p, &unused,
				     target_attr->syntax->ldap_oid, false);
		if (ret != LDB_SUCCESS) {
			talloc_free(tmp_ctx);
			return ret;
		}

		if (p == NULL) {
			DBG_WARNING("Failed to find forward link on %s "
				    "as %s to remove backlink %s on %s",
				    ldb_dn_get_linearized(msg->dn),
				    target_attr->lDAPDisplayName,
				    sa->lDAPDisplayName,
				    ldb_dn_get_linearized(dn));
			*caller_should_vanish = true;
			continue;
		}

		/*
		 * If we find a backlink to ourself, we will delete
		 * the forward link before we get to process that
		 * properly, so just let the caller process this via
		 * the forward link.
		 *
		 * We do this once we are sure we have the forward
		 * link (to ourself) in case something is very wrong
		 * and they are out of sync.
		 */
		if (ldb_dn_compare(dsdb_dn->dn, dn) == 0) {
			continue;
		}

		/* This needs to get the Binary DN, by first searching */
		dn_str = dsdb_dn_get_linearized(tmp_ctx,
						p->dsdb_dn);

		dn_val = data_blob_string_const(dn_str);
		el2->values = &dn_val;
		el2->num_values = 1;

		/*
		 * 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;
		}
	}
	talloc_free(tmp_ctx);
	return LDB_SUCCESS;
}
static int replmd_delete_remove_link(struct ldb_module *module,
				     const struct dsdb_schema *schema,
				     struct replmd_private *replmd_private,
				     struct ldb_dn *dn,
				     struct GUID *guid,
				     struct ldb_message_element *el,
				     const struct dsdb_attribute *sa,
				     struct ldb_request *parent,
				     bool *caller_should_vanish)
{
	unsigned int i;
	TALLOC_CTX *tmp_ctx = talloc_new(module);
	struct ldb_context *ldb = ldb_module_get_ctx(module);

	for (i=0; i<el->num_values; i++) {
		struct dsdb_dn *dsdb_dn;
		int ret;
		struct ldb_message *msg;
		const struct dsdb_attribute *target_attr;
		struct ldb_message_element *el2;
		const char *dn_str;
		struct ldb_val dn_val;
		uint32_t dsdb_flags = 0;
		const char *attrs[] = { NULL, NULL };
		struct ldb_result *link_res;
		struct ldb_message *link_msg;
		struct ldb_message_element *link_el;
		struct parsed_dn *link_dns;
		struct parsed_dn *p = NULL, *unused = NULL;

		if (dsdb_dn_is_deleted_val(&el->values[i])) {
			continue;
		}

		dsdb_dn = dsdb_dn_parse(tmp_ctx, ldb, &el->values[i], sa->syntax->ldap_oid);
		if (!dsdb_dn) {
			talloc_free(tmp_ctx);
			return LDB_ERR_OPERATIONS_ERROR;
		}

		 
		msg = ldb_msg_new(tmp_ctx);
		if (!msg) {
			ldb_module_oom(module);
			talloc_free(tmp_ctx);
			return LDB_ERR_OPERATIONS_ERROR;
		}

		msg->dn = dsdb_dn->dn;

		target_attr = dsdb_attribute_by_linkID(schema, sa->linkID ^ 1);
		if (target_attr == NULL) {
			continue;
		}
		attrs[0] = target_attr->lDAPDisplayName;

		ret = ldb_msg_add_empty(msg, target_attr->lDAPDisplayName,
					LDB_FLAG_MOD_DELETE, &el2);
		if (ret != LDB_SUCCESS) {
			ldb_module_oom(module);
			talloc_free(tmp_ctx);
			return LDB_ERR_OPERATIONS_ERROR;
		}

		ret = dsdb_module_search_dn(module, tmp_ctx, &link_res,
					    msg->dn, attrs,
					    DSDB_FLAG_NEXT_MODULE |
					    DSDB_SEARCH_SHOW_EXTENDED_DN |
					    DSDB_SEARCH_SHOW_RECYCLED,
					    parent);

		if (ret == LDB_ERR_NO_SUCH_OBJECT) {
			DBG_WARNING("Failed to find forward link object %s "
				    "to remove backlink %s on %s",
				    ldb_dn_get_linearized(msg->dn),
				    sa->lDAPDisplayName,
				    ldb_dn_get_linearized(dn));
			*caller_should_vanish = true;
			continue;
		}

		if (ret != LDB_SUCCESS) {
			talloc_free(tmp_ctx);
			return ret;
		}

		link_msg = link_res->msgs[0];
		link_el = ldb_msg_find_element(link_msg,
					       target_attr->lDAPDisplayName);
		if (link_el == NULL) {
			DBG_WARNING("Failed to find forward link on %s "
				    "as %s to remove backlink %s on %s",
				    ldb_dn_get_linearized(msg->dn),
				    target_attr->lDAPDisplayName,
				    sa->lDAPDisplayName,
				    ldb_dn_get_linearized(dn));
			*caller_should_vanish = true;
			continue;
		}

		 
		ret = get_parsed_dns_trusted_fallback(module, replmd_private,
						tmp_ctx,
						link_el, &link_dns,
						target_attr->syntax->ldap_oid,
						parent);
		if (ret != LDB_SUCCESS) {
			talloc_free(tmp_ctx);
			return ret;
		}

		ret = parsed_dn_find(ldb, link_dns, link_el->num_values,
				     guid, dn,
				     data_blob_null, 0,
				     &p, &unused,
				     target_attr->syntax->ldap_oid, false);
		if (ret != LDB_SUCCESS) {
			talloc_free(tmp_ctx);
			return ret;
		}

		if (p == NULL) {
			DBG_WARNING("Failed to find forward link on %s "
				    "as %s to remove backlink %s on %s",
				    ldb_dn_get_linearized(msg->dn),
				    target_attr->lDAPDisplayName,
				    sa->lDAPDisplayName,
				    ldb_dn_get_linearized(dn));
			*caller_should_vanish = true;
			continue;
		}

		 
		if (ldb_dn_compare(dsdb_dn->dn, dn) == 0) {
			continue;
		}

		 
		dn_str = dsdb_dn_get_linearized(tmp_ctx,
						p->dsdb_dn);

		dn_val = data_blob_string_const(dn_str);
		el2->values = &dn_val;
		el2->num_values = 1;

		 
		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;
		}
	}
	talloc_free(tmp_ctx);
	return LDB_SUCCESS;
}