static int reencrypt_context_init(struct crypt_device *cd, struct luks2_hdr *hdr, struct luks2_reencrypt *rh, uint64_t device_size, const struct crypt_params_reencrypt *params)
{
	int r;
	uint64_t dummy, area_length;

	rh->reenc_keyslot = LUKS2_find_keyslot(hdr, "reencrypt");
	if (rh->reenc_keyslot < 0)
		return -EINVAL;
	if (LUKS2_keyslot_area(hdr, rh->reenc_keyslot, &dummy, &area_length) < 0)
		return -EINVAL;

	rh->mode = reencrypt_mode(hdr);

	rh->alignment = reencrypt_get_alignment(cd, hdr);
	if (!rh->alignment)
		return -EINVAL;

	log_dbg(cd, "Hotzone size: %" PRIu64 ", device size: %" PRIu64 ", alignment: %zu.",
		params->max_hotzone_size << SECTOR_SHIFT,
		params->device_size << SECTOR_SHIFT, rh->alignment);

	if ((params->max_hotzone_size << SECTOR_SHIFT) % rh->alignment) {
		log_err(cd, _("Hotzone size must be multiple of calculated zone alignment (%zu bytes)."), rh->alignment);
		return -EINVAL;
	}

	if ((params->device_size << SECTOR_SHIFT) % rh->alignment) {
		log_err(cd, _("Device size must be multiple of calculated zone alignment (%zu bytes)."), rh->alignment);
		return -EINVAL;
	}

	rh->direction = reencrypt_direction(hdr);

	if (!strcmp(params->resilience, "datashift")) {
		log_dbg(cd, "Initializing reencryption context with data_shift resilience.");
		rh->rp.type = REENC_PROTECTION_DATASHIFT;
		rh->data_shift = reencrypt_data_shift(hdr);
	} else if (!strcmp(params->resilience, "journal")) {
		log_dbg(cd, "Initializing reencryption context with journal resilience.");
		rh->rp.type = REENC_PROTECTION_JOURNAL;
	} else if (!strcmp(params->resilience, "checksum")) {
		log_dbg(cd, "Initializing reencryption context with checksum resilience.");
		rh->rp.type = REENC_PROTECTION_CHECKSUM;

		r = snprintf(rh->rp.p.csum.hash,
			sizeof(rh->rp.p.csum.hash), "%s", params->hash);
		if (r < 0 || (size_t)r >= sizeof(rh->rp.p.csum.hash)) {
			log_dbg(cd, "Invalid hash parameter");
			return -EINVAL;
		}

		if (crypt_hash_init(&rh->rp.p.csum.ch, params->hash)) {
			log_dbg(cd, "Failed to initialize checksum resilience hash %s", params->hash);
			return -EINVAL;
		}

		r = crypt_hash_size(params->hash);
		if (r < 1) {
			log_dbg(cd, "Invalid hash size");
			return -EINVAL;
		}
		rh->rp.p.csum.hash_size = r;

		rh->rp.p.csum.checksums_len = area_length;
		if (posix_memalign(&rh->rp.p.csum.checksums, device_alignment(crypt_metadata_device(cd)),
				   rh->rp.p.csum.checksums_len))
			return -ENOMEM;
	} else if (!strcmp(params->resilience, "none")) {
		log_dbg(cd, "Initializing reencryption context with none resilience.");
		rh->rp.type = REENC_PROTECTION_NONE;
	} else {
		log_err(cd, _("Unsupported resilience mode %s"), params->resilience);
		return -EINVAL;
	}

	if (params->device_size) {
		log_dbg(cd, "Switching reencryption to fixed size mode.");
		device_size = params->device_size << SECTOR_SHIFT;
		rh->fixed_length = true;
	} else
		rh->fixed_length = false;

	rh->length = reencrypt_length(cd, hdr, rh, area_length, params->max_hotzone_size << SECTOR_SHIFT);
	if (!rh->length) {
		log_dbg(cd, "Invalid reencryption length.");
		return -EINVAL;
	}

	if (reencrypt_offset(hdr, rh->direction, device_size, &rh->length, &rh->offset)) {
		log_dbg(cd, "Failed to get reencryption offset.");
		return -EINVAL;
	}

	if (rh->offset > device_size)
		return -EINVAL;
	if (rh->length > device_size - rh->offset)
		rh->length = device_size - rh->offset;

	log_dbg(cd, "reencrypt-direction: %s", rh->direction == CRYPT_REENCRYPT_FORWARD ? "forward" : "backward");

	_load_backup_segments(hdr, rh);

	if (rh->direction == CRYPT_REENCRYPT_BACKWARD)
		rh->progress = device_size - rh->offset - rh->length;
	else
		rh->progress = rh->offset;

	log_dbg(cd, "backup-previous digest id: %d", rh->digest_old);
	log_dbg(cd, "backup-final digest id: %d", rh->digest_new);
	log_dbg(cd, "reencrypt length: %" PRIu64, rh->length);
	log_dbg(cd, "reencrypt offset: %" PRIu64, rh->offset);
	log_dbg(cd, "reencrypt shift: %s%" PRIu64, (rh->data_shift && rh->direction == CRYPT_REENCRYPT_BACKWARD ? "-" : ""), rh->data_shift);
	log_dbg(cd, "reencrypt alignment: %zu", rh->alignment);
	log_dbg(cd, "reencrypt progress: %" PRIu64, rh->progress);

	rh->device_size = device_size;

	return rh->length < 512 ? -EINVAL : 0;
}