static int replmd_modify_la_add(struct ldb_module *module,
				struct replmd_private *replmd_private,
				struct replmd_replicated_request *ac,
				struct ldb_message *msg,
				struct ldb_message_element *el,
				struct ldb_message_element *old_el,
				const struct dsdb_attribute *schema_attr,
				time_t t,
				struct ldb_dn *msg_dn,
				struct ldb_request *parent)
{
	unsigned int i, j;
	struct parsed_dn *dns, *old_dns;
	TALLOC_CTX *tmp_ctx = talloc_new(msg);
	int ret;
	struct ldb_val *new_values = NULL;
	unsigned old_num_values = old_el ? old_el->num_values : 0;
	unsigned num_values = 0;
	unsigned max_num_values;
	struct ldb_context *ldb = ldb_module_get_ctx(module);
	NTTIME now;
	unix_to_nt_time(&now, t);

	/* get the DNs to be added, fully parsed.
	 *
	 * We need full parsing because they came off the wire and we don't
	 * trust them, besides which we need their details to know where to put
	 * them.
	 */
	ret = get_parsed_dns(module, tmp_ctx, el, &dns,
			     schema_attr->syntax->ldap_oid, parent);
	if (ret != LDB_SUCCESS) {
		talloc_free(tmp_ctx);
		return ret;
	}

	/* get the existing DNs, lazily parsed */
	ret = get_parsed_dns_trusted_fallback(module, replmd_private,
					      tmp_ctx, old_el, &old_dns,
					      schema_attr->syntax->ldap_oid,
					      parent);

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

	max_num_values = old_num_values + el->num_values;
	if (max_num_values < old_num_values) {
		DEBUG(0, ("we seem to have overflow in replmd_modify_la_add. "
			  "old values: %u, new values: %u, sum: %u\n",
			  old_num_values, el->num_values, max_num_values));
		talloc_free(tmp_ctx);
		return LDB_ERR_OPERATIONS_ERROR;
	}

	new_values = talloc_zero_array(tmp_ctx, struct ldb_val, max_num_values);

	if (new_values == NULL) {
		ldb_module_oom(module);
		talloc_free(tmp_ctx);
		return LDB_ERR_OPERATIONS_ERROR;
	}

	/*
	 * For each new value, find where it would go in the list. If there is
	 * a matching GUID there, we update the existing value; otherwise we
	 * put it in place.
	 */
	j = 0;
	for (i = 0; i < el->num_values; i++) {
		struct parsed_dn *exact;
		struct parsed_dn *next;
		unsigned offset;
		int err = parsed_dn_find(ldb, old_dns, old_num_values,
					 &dns[i].guid,
					 dns[i].dsdb_dn->dn,
					 dns[i].dsdb_dn->extra_part, 0,
					 &exact, &next,
					 schema_attr->syntax->ldap_oid,
					 true);
		if (err != LDB_SUCCESS) {
			talloc_free(tmp_ctx);
			return err;
		}

		if (ac->fix_link_sid) {
			char *fixed_dnstring = NULL;
			struct dom_sid tmp_sid = { 0, };
			DATA_BLOB sid_blob = data_blob_null;
			enum ndr_err_code ndr_err;
			NTSTATUS status;
			int num;

			if (exact == NULL) {
				talloc_free(tmp_ctx);
				return ldb_operr(ldb);
			}

			if (dns[i].dsdb_dn->dn_format != DSDB_NORMAL_DN) {
				talloc_free(tmp_ctx);
				return ldb_operr(ldb);
			}

			/*
			 * Only "<GUID=...><SID=...>" is allowed.
			 *
			 * We get the GUID to just to find the old
			 * value and the SID in order to add it
			 * to the found value.
			 */

			num = ldb_dn_get_comp_num(dns[i].dsdb_dn->dn);
			if (num != 0) {
				talloc_free(tmp_ctx);
				return ldb_operr(ldb);
			}

			num = ldb_dn_get_extended_comp_num(dns[i].dsdb_dn->dn);
			if (num != 2) {
				talloc_free(tmp_ctx);
				return ldb_operr(ldb);
			}

			status = dsdb_get_extended_dn_sid(exact->dsdb_dn->dn,
							  &tmp_sid, "SID");
			if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
				/* this is what we expect */
			} else if (NT_STATUS_IS_OK(status)) {
				struct GUID_txt_buf guid_str;
				ldb_debug_set(ldb, LDB_DEBUG_FATAL,
						       "i[%u] SID NOT MISSING... Attribute %s already "
						       "exists for target GUID %s, SID %s, DN: %s",
						       i, el->name,
						       GUID_buf_string(&exact->guid,
								       &guid_str),
						       dom_sid_string(tmp_ctx, &tmp_sid),
						       dsdb_dn_get_extended_linearized(tmp_ctx,
							       exact->dsdb_dn, 1));
				talloc_free(tmp_ctx);
				return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
			} else {
				talloc_free(tmp_ctx);
				return ldb_operr(ldb);
			}

			status = dsdb_get_extended_dn_sid(dns[i].dsdb_dn->dn,
							  &tmp_sid, "SID");
			if (!NT_STATUS_IS_OK(status)) {
				struct GUID_txt_buf guid_str;
				ldb_asprintf_errstring(ldb,
						       "NO SID PROVIDED... Attribute %s already "
						       "exists for target GUID %s",
						       el->name,
						       GUID_buf_string(&exact->guid,
								       &guid_str));
				talloc_free(tmp_ctx);
				return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
			}

			ndr_err = ndr_push_struct_blob(&sid_blob, tmp_ctx, &tmp_sid,
						       (ndr_push_flags_fn_t)ndr_push_dom_sid);
			if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
				talloc_free(tmp_ctx);
				return ldb_operr(ldb);
			}

			ret = ldb_dn_set_extended_component(exact->dsdb_dn->dn, "SID", &sid_blob);
			data_blob_free(&sid_blob);
			if (ret != LDB_SUCCESS) {
				talloc_free(tmp_ctx);
				return ret;
			}

			fixed_dnstring = dsdb_dn_get_extended_linearized(
					new_values, exact->dsdb_dn, 1);
			if (fixed_dnstring == NULL) {
				talloc_free(tmp_ctx);
				return ldb_operr(ldb);
			}

			/*
			 * We just replace the existing value...
			 */
			*exact->v = data_blob_string_const(fixed_dnstring);

			continue;
		}

		if (exact != NULL) {
			/*
			 * We are trying to add one that exists, which is only
			 * allowed if it was previously deleted.
			 *
			 * When we do undelete a link we change it in place.
			 * It will be copied across into the right spot in due
			 * course.
			 */
			uint32_t rmd_flags;
			rmd_flags = dsdb_dn_rmd_flags(exact->dsdb_dn->dn);

			if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
				struct GUID_txt_buf guid_str;
				ldb_asprintf_errstring(ldb,
						       "Attribute %s already "
						       "exists for target GUID %s",
						       el->name,
						       GUID_buf_string(&exact->guid,
								       &guid_str));
				talloc_free(tmp_ctx);
				/* error codes for 'member' need to be
				   special cased */
				if (ldb_attr_cmp(el->name, "member") == 0) {
					return LDB_ERR_ENTRY_ALREADY_EXISTS;
				} else {
					return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
				}
			}

			ret = replmd_update_la_val(new_values, exact->v,
						   dns[i].dsdb_dn,
						   exact->dsdb_dn,
						   &ac->our_invocation_id,
						   ac->seq_num, ac->seq_num,
						   now, false);
			if (ret != LDB_SUCCESS) {
				talloc_free(tmp_ctx);
				return ret;
			}

			ret = replmd_add_backlink(module, replmd_private,
						  ac->schema,
						  msg_dn,
						  &dns[i].guid, 
						  true,
						  schema_attr,
						  parent);
			if (ret != LDB_SUCCESS) {
				talloc_free(tmp_ctx);
				return ret;
				}
			continue;
		}
		/*
		 * Here we don't have an exact match.
		 *
		 * If next is NULL, this one goes beyond the end of the
		 * existing list, so we need to add all of those ones first.
		 *
		 * If next is not NULL, we need to add all the ones before
		 * next.
		 */
		if (next == NULL) {
			offset = old_num_values;
		} else {
			/* next should have been parsed, but let's make sure */
			if (next->dsdb_dn == NULL) {
				ret = really_parse_trusted_dn(tmp_ctx, ldb, next,
							      schema_attr->syntax->ldap_oid);
				if (ret != LDB_SUCCESS) {
					return ret;
				}
			}
			offset = MIN(next - old_dns, old_num_values);
		}

		/* put all the old ones before next on the list */
		for (; j < offset; j++) {
			new_values[num_values] = *old_dns[j].v;
			num_values++;
		}

		ret = replmd_add_backlink(module, replmd_private,
					  ac->schema, msg_dn,
					  &dns[i].guid,
					  true, schema_attr,
					  parent);
		/* Make the new linked attribute ldb_val. */
		ret = replmd_build_la_val(new_values, &new_values[num_values],
					  dns[i].dsdb_dn, &ac->our_invocation_id,
					  ac->seq_num, now);
		if (ret != LDB_SUCCESS) {
			talloc_free(tmp_ctx);
			return ret;
		}
		num_values++;
		if (ret != LDB_SUCCESS) {
			talloc_free(tmp_ctx);
			return ret;
		}
	}
	/* copy the rest of the old ones (if any) */
	for (; j < old_num_values; j++) {
		new_values[num_values] = *old_dns[j].v;
		num_values++;
	}

	talloc_steal(msg->elements, new_values);
	if (old_el != NULL) {
		talloc_steal(msg->elements, old_el->values);
	}
	el->values = new_values;
	el->num_values = num_values;

	talloc_free(tmp_ctx);

	/* we now tell the backend to replace all existing values
	   with the one we have constructed */
	el->flags = LDB_FLAG_MOD_REPLACE;

	return LDB_SUCCESS;
}
static int replmd_modify_la_add(struct ldb_module *module,
				struct replmd_private *replmd_private,
				struct replmd_replicated_request *ac,
				struct ldb_message *msg,
				struct ldb_message_element *el,
				struct ldb_message_element *old_el,
				const struct dsdb_attribute *schema_attr,
				time_t t,
				struct ldb_dn *msg_dn,
				struct ldb_request *parent)
{
	unsigned int i, j;
	struct parsed_dn *dns, *old_dns;
	TALLOC_CTX *tmp_ctx = talloc_new(msg);
	int ret;
	struct ldb_val *new_values = NULL;
	unsigned old_num_values = old_el ? old_el->num_values : 0;
	unsigned num_values = 0;
	unsigned max_num_values;
	struct ldb_context *ldb = ldb_module_get_ctx(module);
	NTTIME now;
	unix_to_nt_time(&now, t);

	 
	ret = get_parsed_dns(module, tmp_ctx, el, &dns,
			     schema_attr->syntax->ldap_oid, parent);
	if (ret != LDB_SUCCESS) {
		talloc_free(tmp_ctx);
		return ret;
	}

	 
	ret = get_parsed_dns_trusted_fallback(module, replmd_private,
					      tmp_ctx, old_el, &old_dns,
					      schema_attr->syntax->ldap_oid,
					      parent);

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

	max_num_values = old_num_values + el->num_values;
	if (max_num_values < old_num_values) {
		DEBUG(0, ("we seem to have overflow in replmd_modify_la_add. "
			  "old values: %u, new values: %u, sum: %u\n",
			  old_num_values, el->num_values, max_num_values));
		talloc_free(tmp_ctx);
		return LDB_ERR_OPERATIONS_ERROR;
	}

	new_values = talloc_zero_array(tmp_ctx, struct ldb_val, max_num_values);

	if (new_values == NULL) {
		ldb_module_oom(module);
		talloc_free(tmp_ctx);
		return LDB_ERR_OPERATIONS_ERROR;
	}

	 
	j = 0;
	for (i = 0; i < el->num_values; i++) {
		struct parsed_dn *exact;
		struct parsed_dn *next;
		unsigned offset;
		int err = parsed_dn_find(ldb, old_dns, old_num_values,
					 &dns[i].guid,
					 dns[i].dsdb_dn->dn,
					 dns[i].dsdb_dn->extra_part, 0,
					 &exact, &next,
					 schema_attr->syntax->ldap_oid,
					 true);
		if (err != LDB_SUCCESS) {
			talloc_free(tmp_ctx);
			return err;
		}

		if (ac->fix_link_sid) {
			char *fixed_dnstring = NULL;
			struct dom_sid tmp_sid = { 0, };
			DATA_BLOB sid_blob = data_blob_null;
			enum ndr_err_code ndr_err;
			NTSTATUS status;
			int num;

			if (exact == NULL) {
				talloc_free(tmp_ctx);
				return ldb_operr(ldb);
			}

			if (dns[i].dsdb_dn->dn_format != DSDB_NORMAL_DN) {
				talloc_free(tmp_ctx);
				return ldb_operr(ldb);
			}

			 

			num = ldb_dn_get_comp_num(dns[i].dsdb_dn->dn);
			if (num != 0) {
				talloc_free(tmp_ctx);
				return ldb_operr(ldb);
			}

			num = ldb_dn_get_extended_comp_num(dns[i].dsdb_dn->dn);
			if (num != 2) {
				talloc_free(tmp_ctx);
				return ldb_operr(ldb);
			}

			status = dsdb_get_extended_dn_sid(exact->dsdb_dn->dn,
							  &tmp_sid, "SID");
			if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
				 
			} else if (NT_STATUS_IS_OK(status)) {
				struct GUID_txt_buf guid_str;
				ldb_debug_set(ldb, LDB_DEBUG_FATAL,
						       "i[%u] SID NOT MISSING... Attribute %s already "
						       "exists for target GUID %s, SID %s, DN: %s",
						       i, el->name,
						       GUID_buf_string(&exact->guid,
								       &guid_str),
						       dom_sid_string(tmp_ctx, &tmp_sid),
						       dsdb_dn_get_extended_linearized(tmp_ctx,
							       exact->dsdb_dn, 1));
				talloc_free(tmp_ctx);
				return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
			} else {
				talloc_free(tmp_ctx);
				return ldb_operr(ldb);
			}

			status = dsdb_get_extended_dn_sid(dns[i].dsdb_dn->dn,
							  &tmp_sid, "SID");
			if (!NT_STATUS_IS_OK(status)) {
				struct GUID_txt_buf guid_str;
				ldb_asprintf_errstring(ldb,
						       "NO SID PROVIDED... Attribute %s already "
						       "exists for target GUID %s",
						       el->name,
						       GUID_buf_string(&exact->guid,
								       &guid_str));
				talloc_free(tmp_ctx);
				return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
			}

			ndr_err = ndr_push_struct_blob(&sid_blob, tmp_ctx, &tmp_sid,
						       (ndr_push_flags_fn_t)ndr_push_dom_sid);
			if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
				talloc_free(tmp_ctx);
				return ldb_operr(ldb);
			}

			ret = ldb_dn_set_extended_component(exact->dsdb_dn->dn, "SID", &sid_blob);
			data_blob_free(&sid_blob);
			if (ret != LDB_SUCCESS) {
				talloc_free(tmp_ctx);
				return ret;
			}

			fixed_dnstring = dsdb_dn_get_extended_linearized(
					new_values, exact->dsdb_dn, 1);
			if (fixed_dnstring == NULL) {
				talloc_free(tmp_ctx);
				return ldb_operr(ldb);
			}

			 
			*exact->v = data_blob_string_const(fixed_dnstring);

			continue;
		}

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

			if (!(rmd_flags & DSDB_RMD_FLAG_DELETED)) {
				struct GUID_txt_buf guid_str;
				ldb_asprintf_errstring(ldb,
						       "Attribute %s already "
						       "exists for target GUID %s",
						       el->name,
						       GUID_buf_string(&exact->guid,
								       &guid_str));
				talloc_free(tmp_ctx);
				 
				if (ldb_attr_cmp(el->name, "member") == 0) {
					return LDB_ERR_ENTRY_ALREADY_EXISTS;
				} else {
					return LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS;
				}
			}

			ret = replmd_update_la_val(new_values, exact->v,
						   dns[i].dsdb_dn,
						   exact->dsdb_dn,
						   &ac->our_invocation_id,
						   ac->seq_num, ac->seq_num,
						   now, false);
			if (ret != LDB_SUCCESS) {
				talloc_free(tmp_ctx);
				return ret;
			}

			ret = replmd_add_backlink(module, replmd_private,
						  ac->schema,
						  msg_dn,
						  &dns[i].guid, 
						  true,
						  schema_attr,
						  parent);
			if (ret != LDB_SUCCESS) {
				talloc_free(tmp_ctx);
				return ret;
				}
			continue;
		}
		 
		if (next == NULL) {
			offset = old_num_values;
		} else {
			 
			if (next->dsdb_dn == NULL) {
				ret = really_parse_trusted_dn(tmp_ctx, ldb, next,
							      schema_attr->syntax->ldap_oid);
				if (ret != LDB_SUCCESS) {
					return ret;
				}
			}
			offset = MIN(next - old_dns, old_num_values);
		}

		 
		for (; j < offset; j++) {
			new_values[num_values] = *old_dns[j].v;
			num_values++;
		}

		ret = replmd_add_backlink(module, replmd_private,
					  ac->schema, msg_dn,
					  &dns[i].guid,
					  true, schema_attr,
					  parent);
		 
		ret = replmd_build_la_val(new_values, &new_values[num_values],
					  dns[i].dsdb_dn, &ac->our_invocation_id,
					  ac->seq_num, now);
		if (ret != LDB_SUCCESS) {
			talloc_free(tmp_ctx);
			return ret;
		}
		num_values++;
		if (ret != LDB_SUCCESS) {
			talloc_free(tmp_ctx);
			return ret;
		}
	}
	 
	for (; j < old_num_values; j++) {
		new_values[num_values] = *old_dns[j].v;
		num_values++;
	}

	talloc_steal(msg->elements, new_values);
	if (old_el != NULL) {
		talloc_steal(msg->elements, old_el->values);
	}
	el->values = new_values;
	el->num_values = num_values;

	talloc_free(tmp_ctx);

	 
	el->flags = LDB_FLAG_MOD_REPLACE;

	return LDB_SUCCESS;
}