static int replmd_modify_la_delete(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;
	struct parsed_dn *dns, *old_dns;
	TALLOC_CTX *tmp_ctx = NULL;
	int ret;
	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;
	uint32_t rmd_flags;
	NTTIME now;

	unix_to_nt_time(&now, t);

	if (old_el == NULL || old_el->num_values == 0) {
		/* there is nothing to delete... */
		if (num_to_delete == 0) {
			/* and we're deleting nothing, so that's OK */
			return LDB_SUCCESS;
		}
		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);
		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;
	}

	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;
	}

	/* we empty out el->values here to avoid damage if we return early. */
	el->num_values = 0;
	el->values = NULL;

	/*
	 * If vanish links is set, we are actually removing members of
	 *  old_el->values; otherwise we are just marking them deleted.
	 *
	 * There is a special case when no values are given: we remove them
	 * all. When we have the vanish_links control we just have to remove
	 * the backlinks and change our element to replace the existing values
	 * with the empty list.
	 */

	if (num_to_delete == 0) {
		for (i = 0; i < old_el->num_values; i++) {
			struct parsed_dn *p = &old_dns[i];
			if (p->dsdb_dn == NULL) {
				ret = really_parse_trusted_dn(tmp_ctx, ldb, p,
							      schema_attr->syntax->ldap_oid);
				if (ret != LDB_SUCCESS) {
					return ret;
				}
			}
			ret = replmd_add_backlink(module, replmd_private,
						  ac->schema, msg_dn, &p->guid,
						  false, schema_attr,
						  parent);
			if (ret != LDB_SUCCESS) {
				talloc_free(tmp_ctx);
				return ret;
			}
			if (vanish_links) {
				continue;
			}

			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,
						   &ac->our_invocation_id,
						   ac->seq_num, ac->seq_num,
						   now, true);
			if (ret != LDB_SUCCESS) {
				talloc_free(tmp_ctx);
				return ret;
			}
		}

		if (vanish_links) {
			el->flags = LDB_FLAG_MOD_REPLACE;
			talloc_free(tmp_ctx);
			return LDB_SUCCESS;
		}
	}


	for (i = 0; i < num_to_delete; i++) {
		struct parsed_dn *p = &dns[i];
		struct parsed_dn *exact = NULL;
		struct parsed_dn *next = NULL;
		ret = parsed_dn_find(ldb, old_dns, old_el->num_values,
				     &p->guid,
				     NULL,
				     p->dsdb_dn->extra_part, 0,
				     &exact, &next,
				     schema_attr->syntax->ldap_oid,
				     true);
		if (ret != LDB_SUCCESS) {
			talloc_free(tmp_ctx);
			return ret;
		}
		if (exact == NULL) {
			struct GUID_txt_buf buf;
			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;
			}
		}

		if (vanish_links) {
			if (CHECK_DEBUGLVL(5)) {
				rmd_flags = dsdb_dn_rmd_flags(exact->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);
					DEBUG(5, ("Deleting deleted linked "
						  "attribute %s to %s, because "
						  "vanish_links control is set\n",
						  el->name, guid_str));
				}
			}

			/* remove the backlink */
			ret = replmd_add_backlink(module,
						  replmd_private,
						  ac->schema,
						  msg_dn,
						  &p->guid,
						  false, schema_attr,
						  parent);
			if (ret != LDB_SUCCESS) {
				talloc_free(tmp_ctx);
				return ret;
			}

			/* We flag the deletion and tidy it up later. */
			exact->v = NULL;
			continue;
		}

		rmd_flags = dsdb_dn_rmd_flags(exact->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);
			ldb_asprintf_errstring(ldb, "Attribute %s already "
					       "deleted for target GUID %s",
					       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;
			}
		}

		ret = replmd_update_la_val(old_el->values, exact->v,
					   exact->dsdb_dn, exact->dsdb_dn,
					   &ac->our_invocation_id,
					   ac->seq_num, ac->seq_num,
					   now, true);
		if (ret != LDB_SUCCESS) {
			talloc_free(tmp_ctx);
			return ret;
		}
		ret = replmd_add_backlink(module, replmd_private,
					  ac->schema, msg_dn,
					  &p->guid,
					  false, schema_attr,
					  parent);
		if (ret != LDB_SUCCESS) {
			talloc_free(tmp_ctx);
			return ret;
		}
	}

	if (vanish_links) {
		unsigned j = 0;
		struct ldb_val *tmp_vals = NULL;

		tmp_vals = talloc_array(tmp_ctx, struct ldb_val,
					old_el->num_values);
		if (tmp_vals == NULL) {
			talloc_free(tmp_ctx);
			return ldb_module_oom(module);
		}
		for (i = 0; i < old_el->num_values; i++) {
			if (old_dns[i].v == NULL) {
				continue;
			}
			tmp_vals[j] = *old_dns[i].v;
			j++;
		}
		for (i = 0; i < j; i++) {
			old_el->values[i] = tmp_vals[i];
		}
		old_el->num_values = j;
	}

	el->values = talloc_steal(msg->elements, old_el->values);
	el->num_values = old_el->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_delete(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;
	struct parsed_dn *dns, *old_dns;
	TALLOC_CTX *tmp_ctx = NULL;
	int ret;
	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;
	uint32_t rmd_flags;
	NTTIME now;

	unix_to_nt_time(&now, t);

	if (old_el == NULL || old_el->num_values == 0) {
		 
		if (num_to_delete == 0) {
			 
			return LDB_SUCCESS;
		}
		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);
		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;
	}

	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;

	 

	if (num_to_delete == 0) {
		for (i = 0; i < old_el->num_values; i++) {
			struct parsed_dn *p = &old_dns[i];
			if (p->dsdb_dn == NULL) {
				ret = really_parse_trusted_dn(tmp_ctx, ldb, p,
							      schema_attr->syntax->ldap_oid);
				if (ret != LDB_SUCCESS) {
					return ret;
				}
			}
			ret = replmd_add_backlink(module, replmd_private,
						  ac->schema, msg_dn, &p->guid,
						  false, schema_attr,
						  parent);
			if (ret != LDB_SUCCESS) {
				talloc_free(tmp_ctx);
				return ret;
			}
			if (vanish_links) {
				continue;
			}

			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,
						   &ac->our_invocation_id,
						   ac->seq_num, ac->seq_num,
						   now, true);
			if (ret != LDB_SUCCESS) {
				talloc_free(tmp_ctx);
				return ret;
			}
		}

		if (vanish_links) {
			el->flags = LDB_FLAG_MOD_REPLACE;
			talloc_free(tmp_ctx);
			return LDB_SUCCESS;
		}
	}


	for (i = 0; i < num_to_delete; i++) {
		struct parsed_dn *p = &dns[i];
		struct parsed_dn *exact = NULL;
		struct parsed_dn *next = NULL;
		ret = parsed_dn_find(ldb, old_dns, old_el->num_values,
				     &p->guid,
				     NULL,
				     p->dsdb_dn->extra_part, 0,
				     &exact, &next,
				     schema_attr->syntax->ldap_oid,
				     true);
		if (ret != LDB_SUCCESS) {
			talloc_free(tmp_ctx);
			return ret;
		}
		if (exact == NULL) {
			struct GUID_txt_buf buf;
			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;
			}
		}

		if (vanish_links) {
			if (CHECK_DEBUGLVL(5)) {
				rmd_flags = dsdb_dn_rmd_flags(exact->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);
					DEBUG(5, ("Deleting deleted linked "
						  "attribute %s to %s, because "
						  "vanish_links control is set\n",
						  el->name, guid_str));
				}
			}

			 
			ret = replmd_add_backlink(module,
						  replmd_private,
						  ac->schema,
						  msg_dn,
						  &p->guid,
						  false, schema_attr,
						  parent);
			if (ret != LDB_SUCCESS) {
				talloc_free(tmp_ctx);
				return ret;
			}

			 
			exact->v = NULL;
			continue;
		}

		rmd_flags = dsdb_dn_rmd_flags(exact->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);
			ldb_asprintf_errstring(ldb, "Attribute %s already "
					       "deleted for target GUID %s",
					       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;
			}
		}

		ret = replmd_update_la_val(old_el->values, exact->v,
					   exact->dsdb_dn, exact->dsdb_dn,
					   &ac->our_invocation_id,
					   ac->seq_num, ac->seq_num,
					   now, true);
		if (ret != LDB_SUCCESS) {
			talloc_free(tmp_ctx);
			return ret;
		}
		ret = replmd_add_backlink(module, replmd_private,
					  ac->schema, msg_dn,
					  &p->guid,
					  false, schema_attr,
					  parent);
		if (ret != LDB_SUCCESS) {
			talloc_free(tmp_ctx);
			return ret;
		}
	}

	if (vanish_links) {
		unsigned j = 0;
		struct ldb_val *tmp_vals = NULL;

		tmp_vals = talloc_array(tmp_ctx, struct ldb_val,
					old_el->num_values);
		if (tmp_vals == NULL) {
			talloc_free(tmp_ctx);
			return ldb_module_oom(module);
		}
		for (i = 0; i < old_el->num_values; i++) {
			if (old_dns[i].v == NULL) {
				continue;
			}
			tmp_vals[j] = *old_dns[i].v;
			j++;
		}
		for (i = 0; i < j; i++) {
			old_el->values[i] = tmp_vals[i];
		}
		old_el->num_values = j;
	}

	el->values = talloc_steal(msg->elements, old_el->values);
	el->num_values = old_el->num_values;

	talloc_free(tmp_ctx);

	 
	el->flags = LDB_FLAG_MOD_REPLACE;

	return LDB_SUCCESS;
}