LUKS encryption reserves clusters for its own payload data. The size of this area must be included in the qemu-img measure calculation so that we arrive at the correct minimum required image size.
(Ab)use the qcrypto_block_create() API to determine the payload overhead. We discard the payload data that qcrypto thinks will be written to the image. Signed-off-by: Stefan Hajnoczi <stefa...@redhat.com> --- block/qcow2.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/block/qcow2.c b/block/qcow2.c index 4897abae5e..7ab93a5d2f 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -4231,6 +4231,24 @@ static coroutine_fn int qcow2_co_flush_to_os(BlockDriverState *bs) return ret; } +static ssize_t qcow2_measure_crypto_hdr_init_func(QCryptoBlock *block, + size_t headerlen, void *opaque, Error **errp) +{ + size_t *headerlenp = opaque; + + /* Stash away the payload size */ + *headerlenp = headerlen; + return 0; +} + +static ssize_t qcow2_measure_crypto_hdr_write_func(QCryptoBlock *block, + size_t offset, const uint8_t *buf, size_t buflen, + void *opaque, Error **errp) +{ + /* Discard the bytes, we're not actually writing to an image */ + return buflen; +} + static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs, Error **errp) { @@ -4240,11 +4258,13 @@ static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs, uint64_t virtual_size; /* disk size as seen by guest */ uint64_t refcount_bits; uint64_t l2_tables; + uint64_t luks_payload_size = 0; size_t cluster_size; int version; char *optstr; PreallocMode prealloc; bool has_backing_file; + bool has_luks; /* Parse image creation options */ cluster_size = qcow2_opt_get_cluster_size_del(opts, &local_err); @@ -4274,6 +4294,35 @@ static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs, has_backing_file = !!optstr; g_free(optstr); + optstr = qemu_opt_get_del(opts, BLOCK_OPT_ENCRYPT_FORMAT); + has_luks = optstr && strcmp(optstr, "luks") == 0; + g_free(optstr); + + if (has_luks) { + QCryptoBlockCreateOptions cryptoopts = { + .format = Q_CRYPTO_BLOCK_FORMAT_LUKS, + }; + QCryptoBlock *crypto; + size_t headerlen; + + optstr = qemu_opt_get_del(opts, "encrypt.key-secret"); + cryptoopts.u.luks.has_key_secret = !!optstr; + cryptoopts.u.luks.key_secret = optstr; + + crypto = qcrypto_block_create(&cryptoopts, "encrypt.", + qcow2_measure_crypto_hdr_init_func, + qcow2_measure_crypto_hdr_write_func, + &headerlen, &local_err); + + g_free(optstr); + if (!crypto) { + goto err; + } + qcrypto_block_free(crypto); + + luks_payload_size = ROUND_UP(headerlen, cluster_size); + } + virtual_size = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0); virtual_size = ROUND_UP(virtual_size, cluster_size); @@ -4344,7 +4393,7 @@ static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs, info = g_new(BlockMeasureInfo, 1); info->fully_allocated = qcow2_calc_prealloc_size(virtual_size, cluster_size, - ctz32(refcount_bits)); + ctz32(refcount_bits)) + luks_payload_size; /* Remove data clusters that are not required. This overestimates the * required size because metadata needed for the fully allocated file is -- 2.20.1