static int reencrypt_make_backup_segments(struct crypt_device *cd,
		struct luks2_hdr *hdr,
		int keyslot_new,
		const char *cipher,
		uint64_t data_offset,
		const struct crypt_params_reencrypt *params)
{
	int r, segment, moved_segment = -1, digest_old = -1, digest_new = -1;
	json_object *jobj_segment_new = NULL, *jobj_segment_old = NULL, *jobj_segment_bcp = NULL;
	uint32_t sector_size = params->luks2 ? params->luks2->sector_size : SECTOR_SIZE;
	uint64_t segment_offset, tmp, data_shift = params->data_shift << SECTOR_SHIFT;

	if (params->mode != CRYPT_REENCRYPT_DECRYPT) {
		digest_new = LUKS2_digest_by_keyslot(hdr, keyslot_new);
		if (digest_new < 0)
			return -EINVAL;
	}

	if (params->mode != CRYPT_REENCRYPT_ENCRYPT) {
		digest_old = LUKS2_digest_by_segment(hdr, CRYPT_DEFAULT_SEGMENT);
		if (digest_old < 0)
			return -EINVAL;
	}

	segment = LUKS2_segment_first_unused_id(hdr);
	if (segment < 0)
		return -EINVAL;

	if (params->mode == CRYPT_REENCRYPT_ENCRYPT &&
	    (params->flags & CRYPT_REENCRYPT_MOVE_FIRST_SEGMENT)) {
		json_object_copy(LUKS2_get_segment_jobj(hdr, 0), &jobj_segment_bcp);
		r = LUKS2_segment_set_flag(jobj_segment_bcp, "backup-moved-segment");
		if (r)
			goto err;
		moved_segment = segment++;
		json_object_object_add_by_uint(LUKS2_get_segments_jobj(hdr), moved_segment, jobj_segment_bcp);
	}

	/* FIXME: Add detection for case (digest old == digest new && old segment == new segment) */
	if (digest_old >= 0)
		json_object_copy(LUKS2_get_segment_jobj(hdr, CRYPT_DEFAULT_SEGMENT), &jobj_segment_old);
	else if (params->mode == CRYPT_REENCRYPT_ENCRYPT) {
		r = LUKS2_get_data_size(hdr, &tmp, NULL);
		if (r)
			goto err;

		if (params->flags & CRYPT_REENCRYPT_MOVE_FIRST_SEGMENT)
			jobj_segment_old = json_segment_create_linear(0, tmp ? &tmp : NULL, 0);
		else
			jobj_segment_old = json_segment_create_linear(data_offset, tmp ? &tmp : NULL, 0);
	}

	if (!jobj_segment_old) {
		r = -EINVAL;
		goto err;
	}

	r = LUKS2_segment_set_flag(jobj_segment_old, "backup-previous");
	if (r)
		goto err;
	json_object_object_add_by_uint(LUKS2_get_segments_jobj(hdr), segment, jobj_segment_old);
	jobj_segment_old = NULL;
	if (digest_old >= 0)
		LUKS2_digest_segment_assign(cd, hdr, segment, digest_old, 1, 0);
	segment++;

	if (digest_new >= 0) {
		segment_offset = data_offset;
		if (params->mode != CRYPT_REENCRYPT_ENCRYPT &&
		    modify_offset(&segment_offset, data_shift, params->direction)) {
			r = -EINVAL;
			goto err;
		}
		jobj_segment_new = json_segment_create_crypt(segment_offset,
							crypt_get_iv_offset(cd),
							NULL, cipher, sector_size, 0);
	} else if (params->mode == CRYPT_REENCRYPT_DECRYPT) {
		segment_offset = data_offset;
		if (modify_offset(&segment_offset, data_shift, params->direction)) {
			r = -EINVAL;
			goto err;
		}
		jobj_segment_new = json_segment_create_linear(segment_offset, NULL, 0);
	}

	if (!jobj_segment_new) {
		r = -EINVAL;
		goto err;
	}

	r = LUKS2_segment_set_flag(jobj_segment_new, "backup-final");
	if (r)
		goto err;
	json_object_object_add_by_uint(LUKS2_get_segments_jobj(hdr), segment, jobj_segment_new);
	jobj_segment_new = NULL;
	if (digest_new >= 0)
		LUKS2_digest_segment_assign(cd, hdr, segment, digest_new, 1, 0);

	/* FIXME: also check occupied space by keyslot in shrunk area */
	if (params->direction == CRYPT_REENCRYPT_FORWARD && data_shift &&
	    crypt_metadata_device(cd) == crypt_data_device(cd) &&
	    LUKS2_set_keyslots_size(cd, hdr, json_segment_get_offset(reencrypt_segment_new(hdr), 0))) {
		log_err(cd, _("Failed to set new keyslots area size."));
		r = -EINVAL;
		goto err;
	}

	return 0;
err:
	json_object_put(jobj_segment_new);
	json_object_put(jobj_segment_old);
	return r;
}