static int replmd_update_rpmd(struct ldb_module *module,
			      const struct dsdb_schema *schema,
			      struct ldb_request *req,
			      const char * const *rename_attrs,
			      struct ldb_message *msg, uint64_t *seq_num,
			      time_t t, bool is_schema_nc,
			      bool *is_urgent, bool *rodc)
{
	const struct ldb_val *omd_value;
	enum ndr_err_code ndr_err;
	struct replPropertyMetaDataBlob omd;
	unsigned int i;
	NTTIME now;
	const struct GUID *our_invocation_id;
	int ret;
	const char * const *attrs = NULL;
	const char * const attrs2[] = { "uSNChanged", "objectClass", "instanceType", NULL };
	struct ldb_result *res;
	struct ldb_context *ldb;
	struct ldb_message_element *objectclass_el;
	enum urgent_situation situation;
	bool rmd_is_provided;
	bool rmd_is_just_resorted = false;
	const char *not_rename_attrs[4 + msg->num_elements];
	bool is_forced_rodc = false;

	if (rename_attrs) {
		attrs = rename_attrs;
	} else {
		for (i = 0; i < msg->num_elements; i++) {
			not_rename_attrs[i] = msg->elements[i].name;
		}
		not_rename_attrs[i] = "replPropertyMetaData";
		not_rename_attrs[i+1] = "objectClass";
		not_rename_attrs[i+2] = "instanceType";
		not_rename_attrs[i+3] = NULL;
		attrs = not_rename_attrs;
	}

	ldb = ldb_module_get_ctx(module);

	ret = samdb_rodc(ldb, rodc);
	if (ret != LDB_SUCCESS) {
		DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
		*rodc = false;
	}

	if (*rodc &&
	    ldb_request_get_control(req, DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE)) {
		is_forced_rodc = true;
	}

	our_invocation_id = samdb_ntds_invocation_id(ldb);
	if (!our_invocation_id) {
		/* this happens during an initial vampire while
		   updating the schema */
		DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
		return LDB_SUCCESS;
	}

	unix_to_nt_time(&now, t);

	if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_OID)) {
		rmd_is_provided = true;
		if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_RESORT_OID)) {
			rmd_is_just_resorted = true;
		}
	} else {
		rmd_is_provided = false;
	}

	/* if isDeleted is present and is TRUE, then we consider we are deleting,
	 * otherwise we consider we are updating */
	if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
		situation = REPL_URGENT_ON_DELETE;
	} else if (rename_attrs) {
		situation = REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE;
	} else {
		situation = REPL_URGENT_ON_UPDATE;
	}

	if (rmd_is_provided) {
		/* In this case the change_replmetadata control was supplied */
		/* We check that it's the only attribute that is provided
		 * (it's a rare case so it's better to keep the code simplier)
		 * We also check that the highest local_usn is bigger or the same as
		 * uSNChanged. */
		uint64_t db_seq;
		if( msg->num_elements != 1 ||
			strncmp(msg->elements[0].name,
				"replPropertyMetaData", 20) ) {
			DEBUG(0,(__location__ ": changereplmetada control called without "\
				"a specified replPropertyMetaData attribute or with others\n"));
			return LDB_ERR_OPERATIONS_ERROR;
		}
		if (situation != REPL_URGENT_ON_UPDATE) {
			DEBUG(0,(__location__ ": changereplmetada control can't be called when deleting an object\n"));
			return LDB_ERR_OPERATIONS_ERROR;
		}
		omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
		if (!omd_value) {
			DEBUG(0,(__location__ ": replPropertyMetaData was not specified for Object %s\n",
				 ldb_dn_get_linearized(msg->dn)));
			return LDB_ERR_OPERATIONS_ERROR;
		}
		ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
					       (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
		if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
			DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
				 ldb_dn_get_linearized(msg->dn)));
			return LDB_ERR_OPERATIONS_ERROR;
		}

		ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs2,
					    DSDB_FLAG_NEXT_MODULE |
					    DSDB_SEARCH_SHOW_RECYCLED |
					    DSDB_SEARCH_SHOW_EXTENDED_DN |
					    DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
					    DSDB_SEARCH_REVEAL_INTERNALS, req);

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

		if (rmd_is_just_resorted == false) {
			*seq_num = find_max_local_usn(omd);

			db_seq = ldb_msg_find_attr_as_uint64(res->msgs[0], "uSNChanged", 0);

			/*
			 * The test here now allows for a new
			 * replPropertyMetaData with no change, if was
			 * just dbcheck re-sorting the values.
			 */
			if (*seq_num <= db_seq) {
				DEBUG(0,(__location__ ": changereplmetada control provided but max(local_usn)" \
					 " is less than uSNChanged (max = %lld uSNChanged = %lld)\n",
					 (long long)*seq_num, (long long)db_seq));
				return LDB_ERR_OPERATIONS_ERROR;
			}
		}

	} else {
		/* search for the existing replPropertyMetaDataBlob. We need
		 * to use REVEAL and ask for DNs in storage format to support
		 * the check for values being the same in
		 * replmd_update_rpmd_element()
		 */
		ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
					    DSDB_FLAG_NEXT_MODULE |
					    DSDB_SEARCH_SHOW_RECYCLED |
					    DSDB_SEARCH_SHOW_EXTENDED_DN |
					    DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
					    DSDB_SEARCH_REVEAL_INTERNALS, req);
		if (ret != LDB_SUCCESS) {
			return ret;
		}

		omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
		if (!omd_value) {
			DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
				 ldb_dn_get_linearized(msg->dn)));
			return LDB_ERR_OPERATIONS_ERROR;
		}

		ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
					       (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
		if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
			DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
				 ldb_dn_get_linearized(msg->dn)));
			return LDB_ERR_OPERATIONS_ERROR;
		}

		if (omd.version != 1) {
			DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
				 omd.version, ldb_dn_get_linearized(msg->dn)));
			return LDB_ERR_OPERATIONS_ERROR;
		}

		for (i=0; i<msg->num_elements;) {
			struct ldb_message_element *el = &msg->elements[i];
			struct ldb_message_element *old_el;

			old_el = ldb_msg_find_element(res->msgs[0], el->name);
			ret = replmd_update_rpmd_element(ldb, msg, el, old_el,
							 &omd, schema, seq_num,
							 our_invocation_id,
							 now, is_schema_nc,
							 is_forced_rodc,
							 req);
			if (ret != LDB_SUCCESS) {
				return ret;
			}

			if (!*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
				*is_urgent = replmd_check_urgent_attribute(el);
			}

			if (!(el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) {
				i++;
				continue;
			}

			el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;

			if (el->num_values != 0) {
				i++;
				continue;
			}

			ldb_msg_remove_element(msg, el);
		}
	}

	/*
	 * Assert that we have an objectClass attribute - this is major
	 * corruption if we don't have this!
	 */
	objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
	if (objectclass_el != NULL) {
		/*
		 * Now check if this objectClass means we need to do urgent replication
		 */
		if (!*is_urgent && replmd_check_urgent_objectclass(objectclass_el,
								   situation)) {
			*is_urgent = true;
		}
	} else if (!ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
		ldb_asprintf_errstring(ldb, __location__
				       ": objectClass missing on %s\n",
				       ldb_dn_get_linearized(msg->dn));
		return LDB_ERR_OBJECT_CLASS_VIOLATION;
	}

	/*
	 * replmd_update_rpmd_element has done an update if the
	 * seq_num is set
	 */
	if (*seq_num != 0 || rmd_is_just_resorted == true) {
		struct ldb_val *md_value;
		struct ldb_message_element *el;

		/*if we are RODC and this is a DRSR update then its ok*/
		if (!ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)
		    && !ldb_request_get_control(req, DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA)
		    && !is_forced_rodc) {
			unsigned instanceType;

			if (*rodc) {
				ldb_set_errstring(ldb, "RODC modify is forbidden!");
				return LDB_ERR_REFERRAL;
			}

			instanceType = ldb_msg_find_attr_as_uint(res->msgs[0], "instanceType", INSTANCE_TYPE_WRITE);
			if (!(instanceType & INSTANCE_TYPE_WRITE)) {
				return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM,
						 "cannot change replicated attribute on partial replica");
			}
		}

		md_value = talloc(msg, struct ldb_val);
		if (md_value == NULL) {
			ldb_oom(ldb);
			return LDB_ERR_OPERATIONS_ERROR;
		}

		ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &omd.ctr.ctr1, msg->dn);
		if (ret != LDB_SUCCESS) {
			ldb_asprintf_errstring(ldb, "%s: %s", __func__, ldb_errstring(ldb));
			return ret;
		}

		ndr_err = ndr_push_struct_blob(md_value, msg, &omd,
					       (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
		if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
			DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
				 ldb_dn_get_linearized(msg->dn)));
			return LDB_ERR_OPERATIONS_ERROR;
		}

		ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
		if (ret != LDB_SUCCESS) {
			DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
				 ldb_dn_get_linearized(msg->dn)));
			return ret;
		}

		el->num_values = 1;
		el->values = md_value;
	}

	return LDB_SUCCESS;
}
static int replmd_update_rpmd(struct ldb_module *module,
			      const struct dsdb_schema *schema,
			      struct ldb_request *req,
			      const char * const *rename_attrs,
			      struct ldb_message *msg, uint64_t *seq_num,
			      time_t t, bool is_schema_nc,
			      bool *is_urgent, bool *rodc)
{
	const struct ldb_val *omd_value;
	enum ndr_err_code ndr_err;
	struct replPropertyMetaDataBlob omd;
	unsigned int i;
	NTTIME now;
	const struct GUID *our_invocation_id;
	int ret;
	const char * const *attrs = NULL;
	const char * const attrs2[] = { "uSNChanged", "objectClass", "instanceType", NULL };
	struct ldb_result *res;
	struct ldb_context *ldb;
	struct ldb_message_element *objectclass_el;
	enum urgent_situation situation;
	bool rmd_is_provided;
	bool rmd_is_just_resorted = false;
	const char *not_rename_attrs[4 + msg->num_elements];
	bool is_forced_rodc = false;

	if (rename_attrs) {
		attrs = rename_attrs;
	} else {
		for (i = 0; i < msg->num_elements; i++) {
			not_rename_attrs[i] = msg->elements[i].name;
		}
		not_rename_attrs[i] = "replPropertyMetaData";
		not_rename_attrs[i+1] = "objectClass";
		not_rename_attrs[i+2] = "instanceType";
		not_rename_attrs[i+3] = NULL;
		attrs = not_rename_attrs;
	}

	ldb = ldb_module_get_ctx(module);

	ret = samdb_rodc(ldb, rodc);
	if (ret != LDB_SUCCESS) {
		DEBUG(4, (__location__ ": unable to tell if we are an RODC\n"));
		*rodc = false;
	}

	if (*rodc &&
	    ldb_request_get_control(req, DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE)) {
		is_forced_rodc = true;
	}

	our_invocation_id = samdb_ntds_invocation_id(ldb);
	if (!our_invocation_id) {
		 
		DEBUG(5,("No invocationID - skipping replPropertyMetaData update\n"));
		return LDB_SUCCESS;
	}

	unix_to_nt_time(&now, t);

	if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_OID)) {
		rmd_is_provided = true;
		if (ldb_request_get_control(req, DSDB_CONTROL_CHANGEREPLMETADATA_RESORT_OID)) {
			rmd_is_just_resorted = true;
		}
	} else {
		rmd_is_provided = false;
	}

	 
	if (ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE")) {
		situation = REPL_URGENT_ON_DELETE;
	} else if (rename_attrs) {
		situation = REPL_URGENT_ON_CREATE | REPL_URGENT_ON_DELETE;
	} else {
		situation = REPL_URGENT_ON_UPDATE;
	}

	if (rmd_is_provided) {
		 
		 
		uint64_t db_seq;
		if( msg->num_elements != 1 ||
			strncmp(msg->elements[0].name,
				"replPropertyMetaData", 20) ) {
			DEBUG(0,(__location__ ": changereplmetada control called without "\
				"a specified replPropertyMetaData attribute or with others\n"));
			return LDB_ERR_OPERATIONS_ERROR;
		}
		if (situation != REPL_URGENT_ON_UPDATE) {
			DEBUG(0,(__location__ ": changereplmetada control can't be called when deleting an object\n"));
			return LDB_ERR_OPERATIONS_ERROR;
		}
		omd_value = ldb_msg_find_ldb_val(msg, "replPropertyMetaData");
		if (!omd_value) {
			DEBUG(0,(__location__ ": replPropertyMetaData was not specified for Object %s\n",
				 ldb_dn_get_linearized(msg->dn)));
			return LDB_ERR_OPERATIONS_ERROR;
		}
		ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
					       (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
		if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
			DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
				 ldb_dn_get_linearized(msg->dn)));
			return LDB_ERR_OPERATIONS_ERROR;
		}

		ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs2,
					    DSDB_FLAG_NEXT_MODULE |
					    DSDB_SEARCH_SHOW_RECYCLED |
					    DSDB_SEARCH_SHOW_EXTENDED_DN |
					    DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
					    DSDB_SEARCH_REVEAL_INTERNALS, req);

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

		if (rmd_is_just_resorted == false) {
			*seq_num = find_max_local_usn(omd);

			db_seq = ldb_msg_find_attr_as_uint64(res->msgs[0], "uSNChanged", 0);

			 
			if (*seq_num <= db_seq) {
				DEBUG(0,(__location__ ": changereplmetada control provided but max(local_usn)" \
					 " is less than uSNChanged (max = %lld uSNChanged = %lld)\n",
					 (long long)*seq_num, (long long)db_seq));
				return LDB_ERR_OPERATIONS_ERROR;
			}
		}

	} else {
		 
		ret = dsdb_module_search_dn(module, msg, &res, msg->dn, attrs,
					    DSDB_FLAG_NEXT_MODULE |
					    DSDB_SEARCH_SHOW_RECYCLED |
					    DSDB_SEARCH_SHOW_EXTENDED_DN |
					    DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT |
					    DSDB_SEARCH_REVEAL_INTERNALS, req);
		if (ret != LDB_SUCCESS) {
			return ret;
		}

		omd_value = ldb_msg_find_ldb_val(res->msgs[0], "replPropertyMetaData");
		if (!omd_value) {
			DEBUG(0,(__location__ ": Object %s does not have a replPropertyMetaData attribute\n",
				 ldb_dn_get_linearized(msg->dn)));
			return LDB_ERR_OPERATIONS_ERROR;
		}

		ndr_err = ndr_pull_struct_blob(omd_value, msg, &omd,
					       (ndr_pull_flags_fn_t)ndr_pull_replPropertyMetaDataBlob);
		if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
			DEBUG(0,(__location__ ": Failed to parse replPropertyMetaData for %s\n",
				 ldb_dn_get_linearized(msg->dn)));
			return LDB_ERR_OPERATIONS_ERROR;
		}

		if (omd.version != 1) {
			DEBUG(0,(__location__ ": bad version %u in replPropertyMetaData for %s\n",
				 omd.version, ldb_dn_get_linearized(msg->dn)));
			return LDB_ERR_OPERATIONS_ERROR;
		}

		for (i=0; i<msg->num_elements;) {
			struct ldb_message_element *el = &msg->elements[i];
			struct ldb_message_element *old_el;

			old_el = ldb_msg_find_element(res->msgs[0], el->name);
			ret = replmd_update_rpmd_element(ldb, msg, el, old_el,
							 &omd, schema, seq_num,
							 our_invocation_id,
							 now, is_schema_nc,
							 is_forced_rodc,
							 req);
			if (ret != LDB_SUCCESS) {
				return ret;
			}

			if (!*is_urgent && (situation == REPL_URGENT_ON_UPDATE)) {
				*is_urgent = replmd_check_urgent_attribute(el);
			}

			if (!(el->flags & DSDB_FLAG_INTERNAL_FORCE_META_DATA)) {
				i++;
				continue;
			}

			el->flags &= ~DSDB_FLAG_INTERNAL_FORCE_META_DATA;

			if (el->num_values != 0) {
				i++;
				continue;
			}

			ldb_msg_remove_element(msg, el);
		}
	}

	 
	objectclass_el = ldb_msg_find_element(res->msgs[0], "objectClass");
	if (objectclass_el != NULL) {
		 
		if (!*is_urgent && replmd_check_urgent_objectclass(objectclass_el,
								   situation)) {
			*is_urgent = true;
		}
	} else if (!ldb_request_get_control(req, DSDB_CONTROL_DBCHECK)) {
		ldb_asprintf_errstring(ldb, __location__
				       ": objectClass missing on %s\n",
				       ldb_dn_get_linearized(msg->dn));
		return LDB_ERR_OBJECT_CLASS_VIOLATION;
	}

	 
	if (*seq_num != 0 || rmd_is_just_resorted == true) {
		struct ldb_val *md_value;
		struct ldb_message_element *el;

		 
		if (!ldb_request_get_control(req, DSDB_CONTROL_REPLICATED_UPDATE_OID)
		    && !ldb_request_get_control(req, DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA)
		    && !is_forced_rodc) {
			unsigned instanceType;

			if (*rodc) {
				ldb_set_errstring(ldb, "RODC modify is forbidden!");
				return LDB_ERR_REFERRAL;
			}

			instanceType = ldb_msg_find_attr_as_uint(res->msgs[0], "instanceType", INSTANCE_TYPE_WRITE);
			if (!(instanceType & INSTANCE_TYPE_WRITE)) {
				return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM,
						 "cannot change replicated attribute on partial replica");
			}
		}

		md_value = talloc(msg, struct ldb_val);
		if (md_value == NULL) {
			ldb_oom(ldb);
			return LDB_ERR_OPERATIONS_ERROR;
		}

		ret = replmd_replPropertyMetaDataCtr1_sort_and_verify(ldb, &omd.ctr.ctr1, msg->dn);
		if (ret != LDB_SUCCESS) {
			ldb_asprintf_errstring(ldb, "%s: %s", __func__, ldb_errstring(ldb));
			return ret;
		}

		ndr_err = ndr_push_struct_blob(md_value, msg, &omd,
					       (ndr_push_flags_fn_t)ndr_push_replPropertyMetaDataBlob);
		if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
			DEBUG(0,(__location__ ": Failed to marshall replPropertyMetaData for %s\n",
				 ldb_dn_get_linearized(msg->dn)));
			return LDB_ERR_OPERATIONS_ERROR;
		}

		ret = ldb_msg_add_empty(msg, "replPropertyMetaData", LDB_FLAG_MOD_REPLACE, &el);
		if (ret != LDB_SUCCESS) {
			DEBUG(0,(__location__ ": Failed to add updated replPropertyMetaData %s\n",
				 ldb_dn_get_linearized(msg->dn)));
			return ret;
		}

		el->num_values = 1;
		el->values = md_value;
	}

	return LDB_SUCCESS;
}