static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
{
	struct ldb_context *ldb;
	struct replmd_replicated_request *ac;
	struct ldb_request *down_req;
	struct ldb_message *msg;
	time_t t = time(NULL);
	int ret;
	bool is_urgent = false, rodc = false;
	bool is_schema_nc = false;
	unsigned int functional_level;
	const struct ldb_message_element *guid_el = NULL;
	struct ldb_control *sd_propagation_control;
	struct ldb_control *fix_links_control = NULL;
	struct ldb_control *fix_dn_name_control = NULL;
	struct ldb_control *fix_dn_sid_control = NULL;
	struct replmd_private *replmd_private =
		talloc_get_type(ldb_module_get_private(module), struct replmd_private);

	/* do not manipulate our control entries */
	if (ldb_dn_is_special(req->op.mod.message->dn)) {
		return ldb_next_request(module, req);
	}

	sd_propagation_control = ldb_request_get_control(req,
					DSDB_CONTROL_SEC_DESC_PROPAGATION_OID);
	if (sd_propagation_control != NULL) {
		if (req->op.mod.message->num_elements != 1) {
			return ldb_module_operr(module);
		}
		ret = strcmp(req->op.mod.message->elements[0].name,
			     "nTSecurityDescriptor");
		if (ret != 0) {
			return ldb_module_operr(module);
		}

		return ldb_next_request(module, req);
	}

	ldb = ldb_module_get_ctx(module);

	fix_links_control = ldb_request_get_control(req,
					DSDB_CONTROL_DBCHECK_FIX_DUPLICATE_LINKS);
	if (fix_links_control != NULL) {
		struct dsdb_schema *schema = NULL;
		const struct dsdb_attribute *sa = NULL;

		if (req->op.mod.message->num_elements != 1) {
			return ldb_module_operr(module);
		}

		if (LDB_FLAG_MOD_TYPE(req->op.mod.message->elements[0].flags) != LDB_FLAG_MOD_REPLACE) {
			return ldb_module_operr(module);
		}

		schema = dsdb_get_schema(ldb, req);
		if (schema == NULL) {
			return ldb_module_operr(module);
		}

		sa = dsdb_attribute_by_lDAPDisplayName(schema,
				req->op.mod.message->elements[0].name);
		if (sa == NULL) {
			return ldb_module_operr(module);
		}

		if (sa->linkID == 0) {
			return ldb_module_operr(module);
		}

		fix_links_control->critical = false;
		return ldb_next_request(module, req);
	}

	fix_dn_name_control = ldb_request_get_control(req,
					DSDB_CONTROL_DBCHECK_FIX_LINK_DN_NAME);
	if (fix_dn_name_control != NULL) {
		struct dsdb_schema *schema = NULL;
		const struct dsdb_attribute *sa = NULL;

		if (req->op.mod.message->num_elements != 2) {
			return ldb_module_operr(module);
		}

		if (LDB_FLAG_MOD_TYPE(req->op.mod.message->elements[0].flags) != LDB_FLAG_MOD_DELETE) {
			return ldb_module_operr(module);
		}

		if (LDB_FLAG_MOD_TYPE(req->op.mod.message->elements[1].flags) != LDB_FLAG_MOD_ADD) {
			return ldb_module_operr(module);
		}

		if (req->op.mod.message->elements[0].num_values != 1) {
			return ldb_module_operr(module);
		}

		if (req->op.mod.message->elements[1].num_values != 1) {
			return ldb_module_operr(module);
		}

		schema = dsdb_get_schema(ldb, req);
		if (schema == NULL) {
			return ldb_module_operr(module);
		}

		if (ldb_attr_cmp(req->op.mod.message->elements[0].name,
				 req->op.mod.message->elements[1].name) != 0) {
			return ldb_module_operr(module);
		}

		sa = dsdb_attribute_by_lDAPDisplayName(schema,
				req->op.mod.message->elements[0].name);
		if (sa == NULL) {
			return ldb_module_operr(module);
		}

		if (sa->dn_format == DSDB_INVALID_DN) {
			return ldb_module_operr(module);
		}

		if (sa->linkID != 0) {
			return ldb_module_operr(module);
		}

		/*
		 * If we are run from dbcheck and we are not updating
		 * a link (as these would need to be sorted and so
		 * can't go via such a simple update, then do not
		 * trigger replicated updates and a new USN from this
		 * change, it wasn't a real change, just a new
		 * (correct) string DN
		 */

		fix_dn_name_control->critical = false;
		return ldb_next_request(module, req);
	}

	ldb_debug(ldb, LDB_DEBUG_TRACE, "replmd_modify\n");

	guid_el = ldb_msg_find_element(req->op.mod.message, "objectGUID");
	if (guid_el != NULL) {
		ldb_set_errstring(ldb,
				  "replmd_modify: it's not allowed to change the objectGUID!");
		return LDB_ERR_CONSTRAINT_VIOLATION;
	}

	ac = replmd_ctx_init(module, req);
	if (ac == NULL) {
		return ldb_module_oom(module);
	}

	functional_level = dsdb_functional_level(ldb);

	/* we have to copy the message as the caller might have it as a const */
	msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
	if (msg == NULL) {
		ldb_oom(ldb);
		talloc_free(ac);
		return LDB_ERR_OPERATIONS_ERROR;
	}

	fix_dn_sid_control = ldb_request_get_control(req,
					DSDB_CONTROL_DBCHECK_FIX_LINK_DN_SID);
	if (fix_dn_sid_control != NULL) {
		const struct dsdb_attribute *sa = NULL;

		if (msg->num_elements != 1) {
			talloc_free(ac);
			return ldb_module_operr(module);
		}

		if (LDB_FLAG_MOD_TYPE(msg->elements[0].flags) != LDB_FLAG_MOD_ADD) {
			talloc_free(ac);
			return ldb_module_operr(module);
		}

		if (msg->elements[0].num_values != 1) {
			talloc_free(ac);
			return ldb_module_operr(module);
		}

		sa = dsdb_attribute_by_lDAPDisplayName(ac->schema,
				msg->elements[0].name);
		if (sa == NULL) {
			talloc_free(ac);
			return ldb_module_operr(module);
		}

		if (sa->dn_format != DSDB_NORMAL_DN) {
			talloc_free(ac);
			return ldb_module_operr(module);
		}

		fix_dn_sid_control->critical = false;
		ac->fix_link_sid = true;

		goto handle_linked_attribs;
	}

	ldb_msg_remove_attr(msg, "whenChanged");
	ldb_msg_remove_attr(msg, "uSNChanged");

	is_schema_nc = ldb_dn_compare_base(replmd_private->schema_dn, msg->dn) == 0;

	ret = replmd_update_rpmd(module, ac->schema, req, NULL,
				 msg, &ac->seq_num, t, is_schema_nc,
				 &is_urgent, &rodc);
	if (rodc && (ret == LDB_ERR_REFERRAL)) {
		ret = send_rodc_referral(req, ldb, msg->dn);
		talloc_free(ac);
		return ret;

	}

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

 handle_linked_attribs:
	ret = replmd_modify_handle_linked_attribs(module, replmd_private,
						  ac, msg, t, req);
	if (ret != LDB_SUCCESS) {
		talloc_free(ac);
		return ret;
	}

	/* TODO:
	 * - replace the old object with the newly constructed one
	 */

	ac->is_urgent = is_urgent;

	ret = ldb_build_mod_req(&down_req, ldb, ac,
				msg,
				req->controls,
				ac, replmd_op_callback,
				req);
	LDB_REQ_SET_LOCATION(down_req);
	if (ret != LDB_SUCCESS) {
		talloc_free(ac);
		return ret;
	}

	/* current partition control is needed by "replmd_op_callback" */
	if (ldb_request_get_control(req, DSDB_CONTROL_CURRENT_PARTITION_OID) == NULL) {
		ret = ldb_request_add_control(down_req,
					      DSDB_CONTROL_CURRENT_PARTITION_OID,
					      false, NULL);
		if (ret != LDB_SUCCESS) {
			talloc_free(ac);
			return ret;
		}
	}

	/* If we are in functional level 2000, then
	 * replmd_modify_handle_linked_attribs will have done
	 * nothing */
	if (functional_level == DS_DOMAIN_FUNCTION_2000) {
		ret = ldb_request_add_control(down_req, DSDB_CONTROL_APPLY_LINKS, false, NULL);
		if (ret != LDB_SUCCESS) {
			talloc_free(ac);
			return ret;
		}
	}

	talloc_steal(down_req, msg);

	/* we only change whenChanged and uSNChanged if the seq_num
	   has changed */
	if (ac->seq_num != 0) {
		ret = add_time_element(msg, "whenChanged", t);
		if (ret != LDB_SUCCESS) {
			talloc_free(ac);
			ldb_operr(ldb);
			return ret;
		}

		ret = add_uint64_element(ldb, msg, "uSNChanged", ac->seq_num);
		if (ret != LDB_SUCCESS) {
			talloc_free(ac);
			ldb_operr(ldb);
			return ret;
		}
	}

	/* go on with the call chain */
	return ldb_next_request(module, down_req);
}