static int replmd_process_linked_attribute(struct ldb_module *module,
					   TALLOC_CTX *mem_ctx,
					   struct replmd_private *replmd_private,
					   struct ldb_dn *src_dn,
					   const struct dsdb_attribute *attr,
					   struct la_entry *la_entry,
					   struct ldb_request *parent,
					   TALLOC_CTX *element_ctx,
					   struct ldb_message_element *old_el,
					   struct parsed_dn *pdn_list,
					   replmd_link_changed *change)
{
	struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
	struct ldb_context *ldb = ldb_module_get_ctx(module);
	const struct dsdb_schema *schema = dsdb_get_schema(ldb, mem_ctx);
	int ret;
	struct dsdb_dn *dsdb_dn = NULL;
	uint64_t seq_num = 0;
	struct parsed_dn *pdn, *next;
	struct GUID guid = GUID_zero();
	bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
	bool ignore_link;
	struct dsdb_dn *old_dsdb_dn = NULL;
	struct ldb_val *val_to_update = NULL;
	bool add_as_inactive = false;
	WERROR status;

	*change = LINK_CHANGE_NONE;

	/* the value blob for the attribute holds the target object DN */
	status = dsdb_dn_la_from_blob(ldb, attr, schema, mem_ctx,
				      la->value.blob, &dsdb_dn);
	if (!W_ERROR_IS_OK(status)) {
		ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
				       attr->lDAPDisplayName,
				       ldb_dn_get_linearized(src_dn),
				       win_errstr(status));
		return LDB_ERR_OPERATIONS_ERROR;
	}

	ret = replmd_check_target_exists(module, dsdb_dn, la_entry, src_dn,
					 true, &guid, &ignore_link);

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

	/*
	 * there are some cases where the target object doesn't exist, but it's
	 * OK to ignore the linked attribute
	 */
	if (ignore_link) {
		return ret;
	}

	/* see if this link already exists */
	ret = parsed_dn_find(ldb, pdn_list, old_el->num_values,
			     &guid,
			     dsdb_dn->dn,
			     dsdb_dn->extra_part, 0,
			     &pdn, &next,
			     attr->syntax->ldap_oid,
			     true);
	if (ret != LDB_SUCCESS) {
		return ret;
	}

	if (!replmd_link_update_is_newer(pdn, la)) {
		DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
			 old_el->name, ldb_dn_get_linearized(src_dn),
			 GUID_string(mem_ctx, &la->meta_data.originating_invocation_id)));
		return LDB_SUCCESS;
	}

	/* get a seq_num for this change */
	ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
	if (ret != LDB_SUCCESS) {
		return ret;
	}

	/*
	 * check for single-valued link conflicts, i.e. an active linked
	 * attribute already exists, but it has a different target value
	 */
	if (active) {
		ret = replmd_check_singleval_la_conflict(module, replmd_private,
							 mem_ctx, src_dn, la,
							 dsdb_dn, pdn, pdn_list,
							 old_el, schema, attr,
							 seq_num,
							 &add_as_inactive);
		if (ret != LDB_SUCCESS) {
			return ret;
		}
	}

	if (pdn != NULL) {
		uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);

		if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
			/* remove the existing backlink */
			ret = replmd_add_backlink(module, replmd_private,
						  schema, 
						  src_dn,
						  &pdn->guid, false, attr,
						  parent);
			if (ret != LDB_SUCCESS) {
				return ret;
			}
		}

		val_to_update = pdn->v;
		old_dsdb_dn = pdn->dsdb_dn;
		*change = LINK_CHANGE_MODIFIED;

	} else {
		unsigned offset;

		/*
		 * We know where the new one needs to be, from the *next
		 * pointer into pdn_list.
		 */
		if (next == NULL) {
			offset = old_el->num_values;
		} else {
			if (next->dsdb_dn == NULL) {
				ret = really_parse_trusted_dn(mem_ctx, ldb, next,
							      attr->syntax->ldap_oid);
				if (ret != LDB_SUCCESS) {
					return ret;
				}
			}
			offset = next - pdn_list;
			if (offset > old_el->num_values) {
				return LDB_ERR_OPERATIONS_ERROR;
			}
		}

		old_el->values = talloc_realloc(element_ctx, old_el->values,
						struct ldb_val, old_el->num_values+1);
		if (!old_el->values) {
			ldb_module_oom(module);
			return LDB_ERR_OPERATIONS_ERROR;
		}

		if (offset != old_el->num_values) {
			memmove(&old_el->values[offset + 1], &old_el->values[offset],
				(old_el->num_values - offset) * sizeof(old_el->values[0]));
		}

		old_el->num_values++;

		val_to_update = &old_el->values[offset];
		old_dsdb_dn = NULL;
		*change = LINK_CHANGE_ADDED;
	}

	/* set the link attribute's value to the info that was received */
	ret = replmd_set_la_val(mem_ctx, val_to_update, dsdb_dn, old_dsdb_dn,
				&la->meta_data.originating_invocation_id,
				la->meta_data.originating_usn, seq_num,
				la->meta_data.originating_change_time,
				la->meta_data.version,
				!active);
	if (ret != LDB_SUCCESS) {
		return ret;
	}

	if (add_as_inactive) {

		/* Set the new link as inactive/deleted to avoid conflicts */
		ret = replmd_delete_link_value(module, replmd_private, old_el,
					       src_dn, schema, attr, seq_num,
					       false, &guid, dsdb_dn,
					       val_to_update);

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

	} else if (active) {

		/* if the new link is active, then add the new backlink */
		ret = replmd_add_backlink(module, replmd_private,
					  schema,
					  src_dn,
					  &guid, true, attr,
					  parent);
		if (ret != LDB_SUCCESS) {
			return ret;
		}
	}

	ret = dsdb_check_single_valued_link(attr, old_el);
	if (ret != LDB_SUCCESS) {
		return ret;
	}

	old_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;

	return ret;
}
static int replmd_process_linked_attribute(struct ldb_module *module,
					   TALLOC_CTX *mem_ctx,
					   struct replmd_private *replmd_private,
					   struct ldb_dn *src_dn,
					   const struct dsdb_attribute *attr,
					   struct la_entry *la_entry,
					   struct ldb_request *parent,
					   TALLOC_CTX *element_ctx,
					   struct ldb_message_element *old_el,
					   struct parsed_dn *pdn_list,
					   replmd_link_changed *change)
{
	struct drsuapi_DsReplicaLinkedAttribute *la = la_entry->la;
	struct ldb_context *ldb = ldb_module_get_ctx(module);
	const struct dsdb_schema *schema = dsdb_get_schema(ldb, mem_ctx);
	int ret;
	struct dsdb_dn *dsdb_dn = NULL;
	uint64_t seq_num = 0;
	struct parsed_dn *pdn, *next;
	struct GUID guid = GUID_zero();
	bool active = (la->flags & DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE)?true:false;
	bool ignore_link;
	struct dsdb_dn *old_dsdb_dn = NULL;
	struct ldb_val *val_to_update = NULL;
	bool add_as_inactive = false;
	WERROR status;

	*change = LINK_CHANGE_NONE;

	 
	status = dsdb_dn_la_from_blob(ldb, attr, schema, mem_ctx,
				      la->value.blob, &dsdb_dn);
	if (!W_ERROR_IS_OK(status)) {
		ldb_asprintf_errstring(ldb, "Failed to parsed linked attribute blob for %s on %s - %s\n",
				       attr->lDAPDisplayName,
				       ldb_dn_get_linearized(src_dn),
				       win_errstr(status));
		return LDB_ERR_OPERATIONS_ERROR;
	}

	ret = replmd_check_target_exists(module, dsdb_dn, la_entry, src_dn,
					 true, &guid, &ignore_link);

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

	 
	if (ignore_link) {
		return ret;
	}

	 
	ret = parsed_dn_find(ldb, pdn_list, old_el->num_values,
			     &guid,
			     dsdb_dn->dn,
			     dsdb_dn->extra_part, 0,
			     &pdn, &next,
			     attr->syntax->ldap_oid,
			     true);
	if (ret != LDB_SUCCESS) {
		return ret;
	}

	if (!replmd_link_update_is_newer(pdn, la)) {
		DEBUG(3,("Discarding older DRS linked attribute update to %s on %s from %s\n",
			 old_el->name, ldb_dn_get_linearized(src_dn),
			 GUID_string(mem_ctx, &la->meta_data.originating_invocation_id)));
		return LDB_SUCCESS;
	}

	 
	ret = ldb_sequence_number(ldb, LDB_SEQ_NEXT, &seq_num);
	if (ret != LDB_SUCCESS) {
		return ret;
	}

	 
	if (active) {
		ret = replmd_check_singleval_la_conflict(module, replmd_private,
							 mem_ctx, src_dn, la,
							 dsdb_dn, pdn, pdn_list,
							 old_el, schema, attr,
							 seq_num,
							 &add_as_inactive);
		if (ret != LDB_SUCCESS) {
			return ret;
		}
	}

	if (pdn != NULL) {
		uint32_t rmd_flags = dsdb_dn_rmd_flags(pdn->dsdb_dn->dn);

		if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
			 
			ret = replmd_add_backlink(module, replmd_private,
						  schema, 
						  src_dn,
						  &pdn->guid, false, attr,
						  parent);
			if (ret != LDB_SUCCESS) {
				return ret;
			}
		}

		val_to_update = pdn->v;
		old_dsdb_dn = pdn->dsdb_dn;
		*change = LINK_CHANGE_MODIFIED;

	} else {
		unsigned offset;

		 
		if (next == NULL) {
			offset = old_el->num_values;
		} else {
			if (next->dsdb_dn == NULL) {
				ret = really_parse_trusted_dn(mem_ctx, ldb, next,
							      attr->syntax->ldap_oid);
				if (ret != LDB_SUCCESS) {
					return ret;
				}
			}
			offset = next - pdn_list;
			if (offset > old_el->num_values) {
				return LDB_ERR_OPERATIONS_ERROR;
			}
		}

		old_el->values = talloc_realloc(element_ctx, old_el->values,
						struct ldb_val, old_el->num_values+1);
		if (!old_el->values) {
			ldb_module_oom(module);
			return LDB_ERR_OPERATIONS_ERROR;
		}

		if (offset != old_el->num_values) {
			memmove(&old_el->values[offset + 1], &old_el->values[offset],
				(old_el->num_values - offset) * sizeof(old_el->values[0]));
		}

		old_el->num_values++;

		val_to_update = &old_el->values[offset];
		old_dsdb_dn = NULL;
		*change = LINK_CHANGE_ADDED;
	}

	 
	ret = replmd_set_la_val(mem_ctx, val_to_update, dsdb_dn, old_dsdb_dn,
				&la->meta_data.originating_invocation_id,
				la->meta_data.originating_usn, seq_num,
				la->meta_data.originating_change_time,
				la->meta_data.version,
				!active);
	if (ret != LDB_SUCCESS) {
		return ret;
	}

	if (add_as_inactive) {

		 
		ret = replmd_delete_link_value(module, replmd_private, old_el,
					       src_dn, schema, attr, seq_num,
					       false, &guid, dsdb_dn,
					       val_to_update);

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

	} else if (active) {

		 
		ret = replmd_add_backlink(module, replmd_private,
					  schema,
					  src_dn,
					  &guid, true, attr,
					  parent);
		if (ret != LDB_SUCCESS) {
			return ret;
		}
	}

	ret = dsdb_check_single_valued_link(attr, old_el);
	if (ret != LDB_SUCCESS) {
		return ret;
	}

	old_el->flags |= LDB_FLAG_INTERNAL_DISABLE_SINGLE_VALUE_CHECK;

	return ret;
}