* [PATCH v8 00/14] LUKS: encryption slot management using amend interface
@ 2020-06-08 9:40 Maxim Levitsky
2020-06-08 9:40 ` [PATCH v8 01/14] qcrypto/core: add generic infrastructure for crypto options amendment Maxim Levitsky
` (15 more replies)
0 siblings, 16 replies; 25+ messages in thread
From: Maxim Levitsky @ 2020-06-08 9:40 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Daniel P. Berrangé, qemu-block, John Snow,
Markus Armbruster, Max Reitz, Maxim Levitsky
clone of "luks-keymgmnt-v2"
Maxim Levitsky (14):
qcrypto/core: add generic infrastructure for crypto options amendment
qcrypto/luks: implement encryption key management
block/amend: add 'force' option
block/amend: separate amend and create options for qemu-img
block/amend: refactor qcow2 amend options
block/crypto: rename two functions
block/crypto: implement the encryption key management
block/qcow2: extend qemu-img amend interface with crypto options
iotests: filter few more luks specific create options
iotests: qemu-img tests for luks key management
block/core: add generic infrastructure for x-blockdev-amend qmp
command
block/crypto: implement blockdev-amend
block/qcow2: implement blockdev-amend
iotests: add tests for blockdev-amend
block.c | 4 +-
block/Makefile.objs | 2 +-
block/amend.c | 113 +++++++++
block/crypto.c | 206 +++++++++++++--
block/crypto.h | 37 +++
block/qcow2.c | 332 +++++++++++++-----------
crypto/block-luks.c | 416 ++++++++++++++++++++++++++++++-
crypto/block.c | 29 +++
crypto/blockpriv.h | 8 +
docs/tools/qemu-img.rst | 5 +-
include/block/block.h | 1 +
include/block/block_int.h | 24 +-
include/crypto/block.h | 22 ++
qapi/block-core.json | 68 +++++
qapi/crypto.json | 73 +++++-
qapi/job.json | 4 +-
qemu-img-cmds.hx | 4 +-
qemu-img.c | 44 +++-
tests/qemu-iotests/049.out | 102 ++++----
tests/qemu-iotests/061.out | 12 +-
tests/qemu-iotests/079.out | 18 +-
tests/qemu-iotests/082.out | 185 ++++----------
tests/qemu-iotests/085.out | 38 +--
tests/qemu-iotests/087.out | 6 +-
tests/qemu-iotests/115.out | 2 +-
tests/qemu-iotests/121.out | 4 +-
tests/qemu-iotests/125.out | 192 +++++++-------
tests/qemu-iotests/134.out | 2 +-
tests/qemu-iotests/144.out | 4 +-
tests/qemu-iotests/158.out | 4 +-
tests/qemu-iotests/182.out | 2 +-
tests/qemu-iotests/185.out | 8 +-
tests/qemu-iotests/188.out | 2 +-
tests/qemu-iotests/189.out | 4 +-
tests/qemu-iotests/198.out | 4 +-
tests/qemu-iotests/243.out | 16 +-
tests/qemu-iotests/250.out | 2 +-
tests/qemu-iotests/255.out | 8 +-
tests/qemu-iotests/259.out | 2 +-
tests/qemu-iotests/263.out | 4 +-
tests/qemu-iotests/274.out | 46 ++--
tests/qemu-iotests/280.out | 2 +-
tests/qemu-iotests/284.out | 6 +-
tests/qemu-iotests/293 | 207 +++++++++++++++
tests/qemu-iotests/293.out | 99 ++++++++
tests/qemu-iotests/294 | 90 +++++++
tests/qemu-iotests/294.out | 30 +++
tests/qemu-iotests/295 | 279 +++++++++++++++++++++
tests/qemu-iotests/295.out | 40 +++
tests/qemu-iotests/296 | 234 +++++++++++++++++
tests/qemu-iotests/296.out | 33 +++
tests/qemu-iotests/common.filter | 6 +-
tests/qemu-iotests/group | 4 +
53 files changed, 2524 insertions(+), 565 deletions(-)
create mode 100644 block/amend.c
create mode 100755 tests/qemu-iotests/293
create mode 100644 tests/qemu-iotests/293.out
create mode 100755 tests/qemu-iotests/294
create mode 100644 tests/qemu-iotests/294.out
create mode 100755 tests/qemu-iotests/295
create mode 100644 tests/qemu-iotests/295.out
create mode 100755 tests/qemu-iotests/296
create mode 100644 tests/qemu-iotests/296.out
--
2.25.4
^ permalink raw reply [flat|nested] 25+ messages in thread
* [PATCH v8 01/14] qcrypto/core: add generic infrastructure for crypto options amendment
2020-06-08 9:40 [PATCH v8 00/14] LUKS: encryption slot management using amend interface Maxim Levitsky
@ 2020-06-08 9:40 ` Maxim Levitsky
2020-06-08 9:40 ` [PATCH v8 02/14] qcrypto/luks: implement encryption key management Maxim Levitsky
` (14 subsequent siblings)
15 siblings, 0 replies; 25+ messages in thread
From: Maxim Levitsky @ 2020-06-08 9:40 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Daniel P. Berrangé, qemu-block, John Snow,
Markus Armbruster, Max Reitz, Maxim Levitsky
This will be used first to implement luks keyslot management.
block_crypto_amend_opts_init will be used to convert
qemu-img cmdline to QCryptoBlockAmendOptions
Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
---
block/crypto.c | 17 +++++++++++++++++
block/crypto.h | 3 +++
crypto/block.c | 29 +++++++++++++++++++++++++++++
crypto/blockpriv.h | 8 ++++++++
include/crypto/block.h | 22 ++++++++++++++++++++++
qapi/crypto.json | 16 ++++++++++++++++
6 files changed, 95 insertions(+)
diff --git a/block/crypto.c b/block/crypto.c
index 973b57b3eb..dcf8b42bb2 100644
--- a/block/crypto.c
+++ b/block/crypto.c
@@ -202,6 +202,23 @@ block_crypto_create_opts_init(QDict *opts, Error **errp)
return ret;
}
+QCryptoBlockAmendOptions *
+block_crypto_amend_opts_init(QDict *opts, Error **errp)
+{
+ Visitor *v;
+ QCryptoBlockAmendOptions *ret;
+
+ v = qobject_input_visitor_new_flat_confused(opts, errp);
+ if (!v) {
+ return NULL;
+ }
+
+ visit_type_QCryptoBlockAmendOptions(v, NULL, &ret, errp);
+
+ visit_free(v);
+ return ret;
+}
+
static int block_crypto_open_generic(QCryptoBlockFormat format,
QemuOptsList *opts_spec,
diff --git a/block/crypto.h b/block/crypto.h
index b935695e79..06e044c9be 100644
--- a/block/crypto.h
+++ b/block/crypto.h
@@ -91,6 +91,9 @@
QCryptoBlockCreateOptions *
block_crypto_create_opts_init(QDict *opts, Error **errp);
+QCryptoBlockAmendOptions *
+block_crypto_amend_opts_init(QDict *opts, Error **errp);
+
QCryptoBlockOpenOptions *
block_crypto_open_opts_init(QDict *opts, Error **errp);
diff --git a/crypto/block.c b/crypto/block.c
index 6f42b32f1e..eb057948b5 100644
--- a/crypto/block.c
+++ b/crypto/block.c
@@ -150,6 +150,35 @@ qcrypto_block_calculate_payload_offset(QCryptoBlockCreateOptions *create_opts,
return crypto != NULL;
}
+int qcrypto_block_amend_options(QCryptoBlock *block,
+ QCryptoBlockReadFunc readfunc,
+ QCryptoBlockWriteFunc writefunc,
+ void *opaque,
+ QCryptoBlockAmendOptions *options,
+ bool force,
+ Error **errp)
+{
+ if (options->format != block->format) {
+ error_setg(errp,
+ "Cannot amend encryption format");
+ return -1;
+ }
+
+ if (!block->driver->amend) {
+ error_setg(errp,
+ "Crypto format %s doesn't support format options amendment",
+ QCryptoBlockFormat_str(block->format));
+ return -1;
+ }
+
+ return block->driver->amend(block,
+ readfunc,
+ writefunc,
+ opaque,
+ options,
+ force,
+ errp);
+}
QCryptoBlockInfo *qcrypto_block_get_info(QCryptoBlock *block,
Error **errp)
diff --git a/crypto/blockpriv.h b/crypto/blockpriv.h
index 71c59cb542..3c7ccea504 100644
--- a/crypto/blockpriv.h
+++ b/crypto/blockpriv.h
@@ -62,6 +62,14 @@ struct QCryptoBlockDriver {
void *opaque,
Error **errp);
+ int (*amend)(QCryptoBlock *block,
+ QCryptoBlockReadFunc readfunc,
+ QCryptoBlockWriteFunc writefunc,
+ void *opaque,
+ QCryptoBlockAmendOptions *options,
+ bool force,
+ Error **errp);
+
int (*get_info)(QCryptoBlock *block,
QCryptoBlockInfo *info,
Error **errp);
diff --git a/include/crypto/block.h b/include/crypto/block.h
index c77ccaf9c0..d274819791 100644
--- a/include/crypto/block.h
+++ b/include/crypto/block.h
@@ -144,6 +144,28 @@ QCryptoBlock *qcrypto_block_create(QCryptoBlockCreateOptions *options,
void *opaque,
Error **errp);
+/**
+ * qcrypto_block_amend_options:
+ * @block: the block encryption object
+ *
+ * @readfunc: callback for reading data from the volume header
+ * @writefunc: callback for writing data to the volume header
+ * @opaque: data to pass to @readfunc and @writefunc
+ * @options: the new/amended encryption options
+ * @force: hint for the driver to allow unsafe operation
+ * @errp: error pointer
+ *
+ * Changes the crypto options of the encryption format
+ *
+ */
+int qcrypto_block_amend_options(QCryptoBlock *block,
+ QCryptoBlockReadFunc readfunc,
+ QCryptoBlockWriteFunc writefunc,
+ void *opaque,
+ QCryptoBlockAmendOptions *options,
+ bool force,
+ Error **errp);
+
/**
* qcrypto_block_calculate_payload_offset:
diff --git a/qapi/crypto.json b/qapi/crypto.json
index b2a4cff683..aeb6c7ef7b 100644
--- a/qapi/crypto.json
+++ b/qapi/crypto.json
@@ -309,3 +309,19 @@
'base': 'QCryptoBlockInfoBase',
'discriminator': 'format',
'data': { 'luks': 'QCryptoBlockInfoLUKS' } }
+
+
+
+##
+# @QCryptoBlockAmendOptions:
+#
+# The options that are available for all encryption formats
+# when amending encryption settings
+#
+# Since: 5.1
+##
+{ 'union': 'QCryptoBlockAmendOptions',
+ 'base': 'QCryptoBlockOptionsBase',
+ 'discriminator': 'format',
+ 'data': {
+ } }
--
2.25.4
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH v8 02/14] qcrypto/luks: implement encryption key management
2020-06-08 9:40 [PATCH v8 00/14] LUKS: encryption slot management using amend interface Maxim Levitsky
2020-06-08 9:40 ` [PATCH v8 01/14] qcrypto/core: add generic infrastructure for crypto options amendment Maxim Levitsky
@ 2020-06-08 9:40 ` Maxim Levitsky
2020-06-08 9:40 ` [PATCH v8 03/14] block/amend: add 'force' option Maxim Levitsky
` (13 subsequent siblings)
15 siblings, 0 replies; 25+ messages in thread
From: Maxim Levitsky @ 2020-06-08 9:40 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Daniel P. Berrangé, qemu-block, John Snow,
Markus Armbruster, Max Reitz, Maxim Levitsky
Next few patches will expose that functionality to the user.
Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
---
crypto/block-luks.c | 416 +++++++++++++++++++++++++++++++++++++++++++-
qapi/crypto.json | 59 ++++++-
2 files changed, 469 insertions(+), 6 deletions(-)
diff --git a/crypto/block-luks.c b/crypto/block-luks.c
index 4861db810c..564caa1094 100644
--- a/crypto/block-luks.c
+++ b/crypto/block-luks.c
@@ -32,6 +32,7 @@
#include "qemu/uuid.h"
#include "qemu/coroutine.h"
+#include "qemu/bitmap.h"
/*
* Reference for the LUKS format implemented here is
@@ -70,6 +71,9 @@ typedef struct QCryptoBlockLUKSKeySlot QCryptoBlockLUKSKeySlot;
#define QCRYPTO_BLOCK_LUKS_SECTOR_SIZE 512LL
+#define QCRYPTO_BLOCK_LUKS_DEFAULT_ITER_TIME_MS 2000
+#define QCRYPTO_BLOCK_LUKS_ERASE_ITERATIONS 40
+
static const char qcrypto_block_luks_magic[QCRYPTO_BLOCK_LUKS_MAGIC_LEN] = {
'L', 'U', 'K', 'S', 0xBA, 0xBE
};
@@ -219,6 +223,9 @@ struct QCryptoBlockLUKS {
/* Hash algorithm used in pbkdf2 function */
QCryptoHashAlgorithm hash_alg;
+
+ /* Name of the secret that was used to open the image */
+ char *secret;
};
@@ -720,7 +727,7 @@ qcrypto_block_luks_store_key(QCryptoBlock *block,
Error **errp)
{
QCryptoBlockLUKS *luks = block->opaque;
- QCryptoBlockLUKSKeySlot *slot = &luks->header.key_slots[slot_idx];
+ QCryptoBlockLUKSKeySlot *slot;
g_autofree uint8_t *splitkey = NULL;
size_t splitkeylen;
g_autofree uint8_t *slotkey = NULL;
@@ -730,6 +737,8 @@ qcrypto_block_luks_store_key(QCryptoBlock *block,
uint64_t iters;
int ret = -1;
+ assert(slot_idx < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS);
+ slot = &luks->header.key_slots[slot_idx];
if (qcrypto_random_bytes(slot->salt,
QCRYPTO_BLOCK_LUKS_SALT_LEN,
errp) < 0) {
@@ -890,7 +899,7 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
Error **errp)
{
QCryptoBlockLUKS *luks = block->opaque;
- const QCryptoBlockLUKSKeySlot *slot = &luks->header.key_slots[slot_idx];
+ const QCryptoBlockLUKSKeySlot *slot;
g_autofree uint8_t *splitkey = NULL;
size_t splitkeylen;
g_autofree uint8_t *possiblekey = NULL;
@@ -900,6 +909,8 @@ qcrypto_block_luks_load_key(QCryptoBlock *block,
g_autoptr(QCryptoIVGen) ivgen = NULL;
size_t niv;
+ assert(slot_idx < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS);
+ slot = &luks->header.key_slots[slot_idx];
if (slot->active != QCRYPTO_BLOCK_LUKS_KEY_SLOT_ENABLED) {
return 0;
}
@@ -1069,6 +1080,126 @@ qcrypto_block_luks_find_key(QCryptoBlock *block,
return -1;
}
+/*
+ * Returns true if a slot i is marked as active
+ * (contains encrypted copy of the master key)
+ */
+static bool
+qcrypto_block_luks_slot_active(const QCryptoBlockLUKS *luks,
+ unsigned int slot_idx)
+{
+ uint32_t val;
+
+ assert(slot_idx < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS);
+ val = luks->header.key_slots[slot_idx].active;
+ return val == QCRYPTO_BLOCK_LUKS_KEY_SLOT_ENABLED;
+}
+
+/*
+ * Returns the number of slots that are marked as active
+ * (slots that contain encrypted copy of the master key)
+ */
+static unsigned int
+qcrypto_block_luks_count_active_slots(const QCryptoBlockLUKS *luks)
+{
+ size_t i = 0;
+ unsigned int ret = 0;
+
+ for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
+ if (qcrypto_block_luks_slot_active(luks, i)) {
+ ret++;
+ }
+ }
+ return ret;
+}
+
+/*
+ * Finds first key slot which is not active
+ * Returns the key slot index, or -1 if it doesn't exist
+ */
+static int
+qcrypto_block_luks_find_free_keyslot(const QCryptoBlockLUKS *luks)
+{
+ size_t i;
+
+ for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
+ if (!qcrypto_block_luks_slot_active(luks, i)) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+/*
+ * Erases an keyslot given its index
+ * Returns:
+ * 0 if the keyslot was erased successfully
+ * -1 if a error occurred while erasing the keyslot
+ *
+ */
+static int
+qcrypto_block_luks_erase_key(QCryptoBlock *block,
+ unsigned int slot_idx,
+ QCryptoBlockWriteFunc writefunc,
+ void *opaque,
+ Error **errp)
+{
+ QCryptoBlockLUKS *luks = block->opaque;
+ QCryptoBlockLUKSKeySlot *slot;
+ g_autofree uint8_t *garbagesplitkey = NULL;
+ size_t splitkeylen;
+ size_t i;
+ Error *local_err = NULL;
+ int ret;
+
+ assert(slot_idx < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS);
+ slot = &luks->header.key_slots[slot_idx];
+
+ splitkeylen = luks->header.master_key_len * slot->stripes;
+ assert(splitkeylen > 0);
+
+ garbagesplitkey = g_new0(uint8_t, splitkeylen);
+
+ /* Reset the key slot header */
+ memset(slot->salt, 0, QCRYPTO_BLOCK_LUKS_SALT_LEN);
+ slot->iterations = 0;
+ slot->active = QCRYPTO_BLOCK_LUKS_KEY_SLOT_DISABLED;
+
+ ret = qcrypto_block_luks_store_header(block, writefunc,
+ opaque, &local_err);
+
+ if (ret < 0) {
+ error_propagate(errp, local_err);
+ }
+ /*
+ * Now try to erase the key material, even if the header
+ * update failed
+ */
+ for (i = 0; i < QCRYPTO_BLOCK_LUKS_ERASE_ITERATIONS; i++) {
+ if (qcrypto_random_bytes(garbagesplitkey,
+ splitkeylen, &local_err) < 0) {
+ /*
+ * If we failed to get the random data, still write
+ * at least zeros to the key slot at least once
+ */
+ error_propagate(errp, local_err);
+
+ if (i > 0) {
+ return -1;
+ }
+ }
+ if (writefunc(block,
+ slot->key_offset_sector * QCRYPTO_BLOCK_LUKS_SECTOR_SIZE,
+ garbagesplitkey,
+ splitkeylen,
+ opaque,
+ &local_err) != splitkeylen) {
+ error_propagate(errp, local_err);
+ return -1;
+ }
+ }
+ return ret;
+}
static int
qcrypto_block_luks_open(QCryptoBlock *block,
@@ -1099,6 +1230,7 @@ qcrypto_block_luks_open(QCryptoBlock *block,
luks = g_new0(QCryptoBlockLUKS, 1);
block->opaque = luks;
+ luks->secret = g_strdup(options->u.luks.key_secret);
if (qcrypto_block_luks_load_header(block, readfunc, opaque, errp) < 0) {
goto fail;
@@ -1164,6 +1296,7 @@ qcrypto_block_luks_open(QCryptoBlock *block,
fail:
qcrypto_block_free_cipher(block);
qcrypto_ivgen_free(block->ivgen);
+ g_free(luks->secret);
g_free(luks);
return -1;
}
@@ -1204,7 +1337,7 @@ qcrypto_block_luks_create(QCryptoBlock *block,
memcpy(&luks_opts, &options->u.luks, sizeof(luks_opts));
if (!luks_opts.has_iter_time) {
- luks_opts.iter_time = 2000;
+ luks_opts.iter_time = QCRYPTO_BLOCK_LUKS_DEFAULT_ITER_TIME_MS;
}
if (!luks_opts.has_cipher_alg) {
luks_opts.cipher_alg = QCRYPTO_CIPHER_ALG_AES_256;
@@ -1244,6 +1377,8 @@ qcrypto_block_luks_create(QCryptoBlock *block,
optprefix ? optprefix : "");
goto error;
}
+ luks->secret = g_strdup(options->u.luks.key_secret);
+
password = qcrypto_secret_lookup_as_utf8(luks_opts.key_secret, errp);
if (!password) {
goto error;
@@ -1471,10 +1606,278 @@ qcrypto_block_luks_create(QCryptoBlock *block,
qcrypto_block_free_cipher(block);
qcrypto_ivgen_free(block->ivgen);
+ g_free(luks->secret);
g_free(luks);
return -1;
}
+static int
+qcrypto_block_luks_amend_add_keyslot(QCryptoBlock *block,
+ QCryptoBlockReadFunc readfunc,
+ QCryptoBlockWriteFunc writefunc,
+ void *opaque,
+ QCryptoBlockAmendOptionsLUKS *opts_luks,
+ bool force,
+ Error **errp)
+{
+ QCryptoBlockLUKS *luks = block->opaque;
+ uint64_t iter_time = opts_luks->has_iter_time ?
+ opts_luks->iter_time :
+ QCRYPTO_BLOCK_LUKS_DEFAULT_ITER_TIME_MS;
+ int keyslot;
+ g_autofree char *old_password = NULL;
+ g_autofree char *new_password = NULL;
+ g_autofree uint8_t *master_key = NULL;
+
+ char *secret = opts_luks->has_secret ? opts_luks->secret : luks->secret;
+
+ if (!opts_luks->has_new_secret) {
+ error_setg(errp, "'new-secret' is required to activate a keyslot");
+ return -1;
+ }
+ if (opts_luks->has_old_secret) {
+ error_setg(errp,
+ "'old-secret' must not be given when activating keyslots");
+ return -1;
+ }
+
+ if (opts_luks->has_keyslot) {
+ keyslot = opts_luks->keyslot;
+ if (keyslot < 0 || keyslot >= QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS) {
+ error_setg(errp,
+ "Invalid keyslot %u specified, must be between 0 and %u",
+ keyslot, QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS - 1);
+ return -1;
+ }
+ } else {
+ keyslot = qcrypto_block_luks_find_free_keyslot(luks);
+ if (keyslot == -1) {
+ error_setg(errp,
+ "Can't add a keyslot - all keyslots are in use");
+ return -1;
+ }
+ }
+
+ if (!force && qcrypto_block_luks_slot_active(luks, keyslot)) {
+ error_setg(errp,
+ "Refusing to overwrite active keyslot %i - "
+ "please erase it first",
+ keyslot);
+ return -1;
+ }
+
+ /* Locate the password that will be used to retrieve the master key */
+ old_password = qcrypto_secret_lookup_as_utf8(secret, errp);
+ if (!old_password) {
+ return -1;
+ }
+
+ /* Retrieve the master key */
+ master_key = g_new0(uint8_t, luks->header.master_key_len);
+
+ if (qcrypto_block_luks_find_key(block, old_password, master_key,
+ readfunc, opaque, errp) < 0) {
+ error_append_hint(errp, "Failed to retrieve the master key");
+ return -1;
+ }
+
+ /* Locate the new password*/
+ new_password = qcrypto_secret_lookup_as_utf8(opts_luks->new_secret, errp);
+ if (!new_password) {
+ return -1;
+ }
+
+ /* Now set the new keyslots */
+ if (qcrypto_block_luks_store_key(block, keyslot, new_password, master_key,
+ iter_time, writefunc, opaque, errp)) {
+ error_append_hint(errp, "Failed to write to keyslot %i", keyslot);
+ return -1;
+ }
+ return 0;
+}
+
+static int
+qcrypto_block_luks_amend_erase_keyslots(QCryptoBlock *block,
+ QCryptoBlockReadFunc readfunc,
+ QCryptoBlockWriteFunc writefunc,
+ void *opaque,
+ QCryptoBlockAmendOptionsLUKS *opts_luks,
+ bool force,
+ Error **errp)
+{
+ QCryptoBlockLUKS *luks = block->opaque;
+ g_autofree uint8_t *tmpkey = NULL;
+ g_autofree char *old_password = NULL;
+
+ if (opts_luks->has_new_secret) {
+ error_setg(errp,
+ "'new-secret' must not be given when erasing keyslots");
+ return -1;
+ }
+ if (opts_luks->has_iter_time) {
+ error_setg(errp,
+ "'iter-time' must not be given when erasing keyslots");
+ return -1;
+ }
+ if (opts_luks->has_secret) {
+ error_setg(errp,
+ "'secret' must not be given when erasing keyslots");
+ return -1;
+ }
+
+ /* Load the old password if given */
+ if (opts_luks->has_old_secret) {
+ old_password = qcrypto_secret_lookup_as_utf8(opts_luks->old_secret,
+ errp);
+ if (!old_password) {
+ return -1;
+ }
+
+ /*
+ * Allocate a temporary key buffer that we will need when
+ * checking if slot matches the given old password
+ */
+ tmpkey = g_new0(uint8_t, luks->header.master_key_len);
+ }
+
+ /* Erase an explicitly given keyslot */
+ if (opts_luks->has_keyslot) {
+ int keyslot = opts_luks->keyslot;
+
+ if (keyslot < 0 || keyslot >= QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS) {
+ error_setg(errp,
+ "Invalid keyslot %i specified, must be between 0 and %i",
+ keyslot, QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS - 1);
+ return -1;
+ }
+
+ if (opts_luks->has_old_secret) {
+ int rv = qcrypto_block_luks_load_key(block,
+ keyslot,
+ old_password,
+ tmpkey,
+ readfunc,
+ opaque,
+ errp);
+ if (rv == -1) {
+ return -1;
+ } else if (rv == 0) {
+ error_setg(errp,
+ "Given keyslot %i doesn't contain the given "
+ "old password for erase operation",
+ keyslot);
+ return -1;
+ }
+ }
+
+ if (!force && !qcrypto_block_luks_slot_active(luks, keyslot)) {
+ error_setg(errp,
+ "Given keyslot %i is already erased (inactive) ",
+ keyslot);
+ return -1;
+ }
+
+ if (!force && qcrypto_block_luks_count_active_slots(luks) == 1) {
+ error_setg(errp,
+ "Attempt to erase the only active keyslot %i "
+ "which will erase all the data in the image "
+ "irreversibly - refusing operation",
+ keyslot);
+ return -1;
+ }
+
+ if (qcrypto_block_luks_erase_key(block, keyslot,
+ writefunc, opaque, errp)) {
+ error_append_hint(errp, "Failed to erase keyslot %i", keyslot);
+ return -1;
+ }
+
+ /* Erase all keyslots that match the given old password */
+ } else if (opts_luks->has_old_secret) {
+
+ unsigned long slots_to_erase_bitmap = 0;
+ size_t i;
+ int slot_count;
+
+ assert(QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS <=
+ sizeof(slots_to_erase_bitmap) * 8);
+
+ for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
+ int rv = qcrypto_block_luks_load_key(block,
+ i,
+ old_password,
+ tmpkey,
+ readfunc,
+ opaque,
+ errp);
+ if (rv == -1) {
+ return -1;
+ } else if (rv == 1) {
+ bitmap_set(&slots_to_erase_bitmap, i, 1);
+ }
+ }
+
+ slot_count = bitmap_count_one(&slots_to_erase_bitmap,
+ QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS);
+ if (slot_count == 0) {
+ error_setg(errp,
+ "No keyslots match given (old) password for erase operation");
+ return -1;
+ }
+
+ if (!force &&
+ slot_count == qcrypto_block_luks_count_active_slots(luks)) {
+ error_setg(errp,
+ "All the active keyslots match the (old) password that "
+ "was given and erasing them will erase all the data in "
+ "the image irreversibly - refusing operation");
+ return -1;
+ }
+
+ /* Now apply the update */
+ for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) {
+ if (!test_bit(i, &slots_to_erase_bitmap)) {
+ continue;
+ }
+ if (qcrypto_block_luks_erase_key(block, i, writefunc,
+ opaque, errp)) {
+ error_append_hint(errp, "Failed to erase keyslot %zu", i);
+ return -1;
+ }
+ }
+ } else {
+ error_setg(errp,
+ "To erase keyslot(s), either explicit keyslot index "
+ "or the password currently contained in them must be given");
+ return -1;
+ }
+ return 0;
+}
+
+static int
+qcrypto_block_luks_amend_options(QCryptoBlock *block,
+ QCryptoBlockReadFunc readfunc,
+ QCryptoBlockWriteFunc writefunc,
+ void *opaque,
+ QCryptoBlockAmendOptions *options,
+ bool force,
+ Error **errp)
+{
+ QCryptoBlockAmendOptionsLUKS *opts_luks = &options->u.luks;
+
+ switch (opts_luks->state) {
+ case Q_CRYPTO_BLOCKLUKS_KEYSLOT_STATE_ACTIVE:
+ return qcrypto_block_luks_amend_add_keyslot(block, readfunc,
+ writefunc, opaque,
+ opts_luks, force, errp);
+ case Q_CRYPTO_BLOCKLUKS_KEYSLOT_STATE_INACTIVE:
+ return qcrypto_block_luks_amend_erase_keyslots(block, readfunc,
+ writefunc, opaque,
+ opts_luks, force, errp);
+ default:
+ g_assert_not_reached();
+ }
+}
static int qcrypto_block_luks_get_info(QCryptoBlock *block,
QCryptoBlockInfo *info,
@@ -1523,7 +1926,11 @@ static int qcrypto_block_luks_get_info(QCryptoBlock *block,
static void qcrypto_block_luks_cleanup(QCryptoBlock *block)
{
- g_free(block->opaque);
+ QCryptoBlockLUKS *luks = block->opaque;
+ if (luks) {
+ g_free(luks->secret);
+ g_free(luks);
+ }
}
@@ -1560,6 +1967,7 @@ qcrypto_block_luks_encrypt(QCryptoBlock *block,
const QCryptoBlockDriver qcrypto_block_driver_luks = {
.open = qcrypto_block_luks_open,
.create = qcrypto_block_luks_create,
+ .amend = qcrypto_block_luks_amend_options,
.get_info = qcrypto_block_luks_get_info,
.cleanup = qcrypto_block_luks_cleanup,
.decrypt = qcrypto_block_luks_decrypt,
diff --git a/qapi/crypto.json b/qapi/crypto.json
index aeb6c7ef7b..5a68e0db25 100644
--- a/qapi/crypto.json
+++ b/qapi/crypto.json
@@ -297,7 +297,6 @@
'uuid': 'str',
'slots': [ 'QCryptoBlockInfoLUKSSlot' ] }}
-
##
# @QCryptoBlockInfo:
#
@@ -310,7 +309,63 @@
'discriminator': 'format',
'data': { 'luks': 'QCryptoBlockInfoLUKS' } }
+##
+# @QCryptoBlockLUKSKeyslotState:
+#
+# Defines state of keyslots that are affected by the update
+#
+# @active: The slots contain the given password and marked as active
+# @inactive: The slots are erased (contain garbage) and marked as inactive
+#
+# Since: 5.1
+##
+{ 'enum': 'QCryptoBlockLUKSKeyslotState',
+ 'data': [ 'active', 'inactive' ] }
+
+##
+# @QCryptoBlockAmendOptionsLUKS:
+#
+# This struct defines the update parameters that activate/de-activate set
+# of keyslots
+#
+# @state: the desired state of the keyslots
+#
+# @new-secret: The ID of a QCryptoSecret object providing the password to be
+# written into added active keyslots
+#
+# @old-secret: Optional (for deactivation only)
+# If given will deactive all keyslots that
+# match password located in QCryptoSecret with this ID
+#
+# @iter-time: Optional (for activation only)
+# Number of milliseconds to spend in
+# PBKDF passphrase processing for the newly activated keyslot.
+# Currently defaults to 2000.
+#
+# @keyslot: Optional. ID of the keyslot to activate/deactivate.
+# For keyslot activation, keyslot should not be active already
+# (this is unsafe to update an active keyslot),
+# but possible if 'force' parameter is given.
+# If keyslot is not given, first free keyslot will be written.
+#
+# For keyslot deactivation, this parameter specifies the exact
+# keyslot to deactivate
+#
+# @secret: Optional. The ID of a QCryptoSecret object providing the
+# password to use to retrive current master key.
+# Defaults to the same secret that was used to open the image
+#
+#
+# Since 5.1
+##
+{ 'struct': 'QCryptoBlockAmendOptionsLUKS',
+ 'data': { 'state': 'QCryptoBlockLUKSKeyslotState',
+ '*new-secret': 'str',
+ '*old-secret': 'str',
+ '*keyslot': 'int',
+ '*iter-time': 'int',
+ '*secret': 'str' } }
##
# @QCryptoBlockAmendOptions:
@@ -324,4 +379,4 @@
'base': 'QCryptoBlockOptionsBase',
'discriminator': 'format',
'data': {
- } }
+ 'luks': 'QCryptoBlockAmendOptionsLUKS' } }
--
2.25.4
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH v8 03/14] block/amend: add 'force' option
2020-06-08 9:40 [PATCH v8 00/14] LUKS: encryption slot management using amend interface Maxim Levitsky
2020-06-08 9:40 ` [PATCH v8 01/14] qcrypto/core: add generic infrastructure for crypto options amendment Maxim Levitsky
2020-06-08 9:40 ` [PATCH v8 02/14] qcrypto/luks: implement encryption key management Maxim Levitsky
@ 2020-06-08 9:40 ` Maxim Levitsky
2020-06-08 9:40 ` [PATCH v8 04/14] block/amend: separate amend and create options for qemu-img Maxim Levitsky
` (12 subsequent siblings)
15 siblings, 0 replies; 25+ messages in thread
From: Maxim Levitsky @ 2020-06-08 9:40 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Daniel P. Berrangé, qemu-block, John Snow,
Markus Armbruster, Max Reitz, Maxim Levitsky
'force' option will be used for some unsafe amend operations.
This includes things like erasing last keyslot in luks based formats
(which destroys the data, unless the master key is backed up
by external means), but that _might_ be desired result.
Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
---
block.c | 4 +++-
block/qcow2.c | 1 +
docs/tools/qemu-img.rst | 5 ++++-
include/block/block.h | 1 +
include/block/block_int.h | 1 +
qemu-img-cmds.hx | 4 ++--
qemu-img.c | 8 +++++++-
7 files changed, 19 insertions(+), 5 deletions(-)
diff --git a/block.c b/block.c
index 8416376c9b..10a1e87d1e 100644
--- a/block.c
+++ b/block.c
@@ -6482,6 +6482,7 @@ void bdrv_remove_aio_context_notifier(BlockDriverState *bs,
int bdrv_amend_options(BlockDriverState *bs, QemuOpts *opts,
BlockDriverAmendStatusCB *status_cb, void *cb_opaque,
+ bool force,
Error **errp)
{
if (!bs->drv) {
@@ -6493,7 +6494,8 @@ int bdrv_amend_options(BlockDriverState *bs, QemuOpts *opts,
bs->drv->format_name);
return -ENOTSUP;
}
- return bs->drv->bdrv_amend_options(bs, opts, status_cb, cb_opaque, errp);
+ return bs->drv->bdrv_amend_options(bs, opts, status_cb,
+ cb_opaque, force, errp);
}
/*
diff --git a/block/qcow2.c b/block/qcow2.c
index 0cd2e6757e..2ab0c382f7 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -5340,6 +5340,7 @@ static void qcow2_amend_helper_cb(BlockDriverState *bs,
static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
BlockDriverAmendStatusCB *status_cb,
void *cb_opaque,
+ bool force,
Error **errp)
{
BDRVQcow2State *s = bs->opaque;
diff --git a/docs/tools/qemu-img.rst b/docs/tools/qemu-img.rst
index 69cd9a3037..f14332ea50 100644
--- a/docs/tools/qemu-img.rst
+++ b/docs/tools/qemu-img.rst
@@ -253,11 +253,14 @@ Command description:
.. program:: qemu-img-commands
-.. option:: amend [--object OBJECTDEF] [--image-opts] [-p] [-q] [-f FMT] [-t CACHE] -o OPTIONS FILENAME
+.. option:: amend [--object OBJECTDEF] [--image-opts] [-p] [-q] [-f FMT] [-t CACHE] [--force] -o OPTIONS FILENAME
Amends the image format specific *OPTIONS* for the image file
*FILENAME*. Not all file formats support this operation.
+ --force allows some unsafe operations. Currently for -f luks, it allows to
+ erase the last encryption key, and to overwrite an active encryption key.
+
.. option:: bench [-c COUNT] [-d DEPTH] [-f FMT] [--flush-interval=FLUSH_INTERVAL] [-i AIO] [-n] [--no-drain] [-o OFFSET] [--pattern=PATTERN] [-q] [-s BUFFER_SIZE] [-S STEP_SIZE] [-t CACHE] [-w] [-U] FILENAME
Run a simple sequential I/O benchmark on the specified image. If ``-w`` is
diff --git a/include/block/block.h b/include/block/block.h
index 25e299605e..86f9728f00 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -450,6 +450,7 @@ typedef void BlockDriverAmendStatusCB(BlockDriverState *bs, int64_t offset,
int64_t total_work_size, void *opaque);
int bdrv_amend_options(BlockDriverState *bs_new, QemuOpts *opts,
BlockDriverAmendStatusCB *status_cb, void *cb_opaque,
+ bool force,
Error **errp);
/* check if a named node can be replaced when doing drive-mirror */
diff --git a/include/block/block_int.h b/include/block/block_int.h
index 791de6a59c..066b9eaa40 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -440,6 +440,7 @@ struct BlockDriver {
int (*bdrv_amend_options)(BlockDriverState *bs, QemuOpts *opts,
BlockDriverAmendStatusCB *status_cb,
void *cb_opaque,
+ bool force,
Error **errp);
void (*bdrv_debug_event)(BlockDriverState *bs, BlkdebugEvent event);
diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx
index 10b910b67c..b89c019b76 100644
--- a/qemu-img-cmds.hx
+++ b/qemu-img-cmds.hx
@@ -10,9 +10,9 @@ HXCOMM When amending the rST sections, please remember to copy the usage
HXCOMM over to the per-command sections in docs/tools/qemu-img.rst.
DEF("amend", img_amend,
- "amend [--object objectdef] [--image-opts] [-p] [-q] [-f fmt] [-t cache] -o options filename")
+ "amend [--object objectdef] [--image-opts] [-p] [-q] [-f fmt] [-t cache] [--force] -o options filename")
SRST
-.. option:: amend [--object OBJECTDEF] [--image-opts] [-p] [-q] [-f FMT] [-t CACHE] -o OPTIONS FILENAME
+.. option:: amend [--object OBJECTDEF] [--image-opts] [-p] [-q] [-f FMT] [-t CACHE] [--force] -o OPTIONS FILENAME
ERST
DEF("bench", img_bench,
diff --git a/qemu-img.c b/qemu-img.c
index d7e846e607..10d81f09db 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -79,6 +79,7 @@ enum {
OPTION_DISABLE = 273,
OPTION_MERGE = 274,
OPTION_BITMAPS = 275,
+ OPTION_FORCE = 276,
};
typedef enum OutputFormat {
@@ -4099,6 +4100,7 @@ static int img_amend(int argc, char **argv)
BlockBackend *blk = NULL;
BlockDriverState *bs = NULL;
bool image_opts = false;
+ bool force = false;
cache = BDRV_DEFAULT_CACHE;
for (;;) {
@@ -4106,6 +4108,7 @@ static int img_amend(int argc, char **argv)
{"help", no_argument, 0, 'h'},
{"object", required_argument, 0, OPTION_OBJECT},
{"image-opts", no_argument, 0, OPTION_IMAGE_OPTS},
+ {"force", no_argument, 0, OPTION_FORCE},
{0, 0, 0, 0}
};
c = getopt_long(argc, argv, ":ho:f:t:pq",
@@ -4153,6 +4156,9 @@ static int img_amend(int argc, char **argv)
case OPTION_IMAGE_OPTS:
image_opts = true;
break;
+ case OPTION_FORCE:
+ force = true;
+ break;
}
}
@@ -4230,7 +4236,7 @@ static int img_amend(int argc, char **argv)
/* In case the driver does not call amend_status_cb() */
qemu_progress_print(0.f, 0);
- ret = bdrv_amend_options(bs, opts, &amend_status_cb, NULL, &err);
+ ret = bdrv_amend_options(bs, opts, &amend_status_cb, NULL, force, &err);
qemu_progress_print(100.f, 0);
if (ret < 0) {
error_report_err(err);
--
2.25.4
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH v8 04/14] block/amend: separate amend and create options for qemu-img
2020-06-08 9:40 [PATCH v8 00/14] LUKS: encryption slot management using amend interface Maxim Levitsky
` (2 preceding siblings ...)
2020-06-08 9:40 ` [PATCH v8 03/14] block/amend: add 'force' option Maxim Levitsky
@ 2020-06-08 9:40 ` Maxim Levitsky
2020-06-08 9:40 ` [PATCH v8 05/14] block/amend: refactor qcow2 amend options Maxim Levitsky
` (11 subsequent siblings)
15 siblings, 0 replies; 25+ messages in thread
From: Maxim Levitsky @ 2020-06-08 9:40 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Daniel P. Berrangé, qemu-block, John Snow,
Markus Armbruster, Max Reitz, Maxim Levitsky
Some options are only useful for creation
(or hard to be amended, like cluster size for qcow2), while some other
options are only useful for amend, like upcoming keyslot management
options for luks
Since currently only qcow2 supports amend, move all its options
to a common macro and then include it in each action option list.
In future it might be useful to remove some options which are
not supported anyway from amend list, which currently
cause an error message if amended.
Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
---
block/qcow2.c | 173 +++++++++++++++++++++-----------------
include/block/block_int.h | 4 +
qemu-img.c | 18 ++--
3 files changed, 107 insertions(+), 88 deletions(-)
diff --git a/block/qcow2.c b/block/qcow2.c
index 2ab0c382f7..fcfd90f2e2 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -5649,89 +5649,103 @@ void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset,
s->signaled_corruption = true;
}
+#define QCOW_COMMON_OPTIONS \
+ { \
+ .name = BLOCK_OPT_SIZE, \
+ .type = QEMU_OPT_SIZE, \
+ .help = "Virtual disk size" \
+ }, \
+ { \
+ .name = BLOCK_OPT_COMPAT_LEVEL, \
+ .type = QEMU_OPT_STRING, \
+ .help = "Compatibility level (v2 [0.10] or v3 [1.1])" \
+ }, \
+ { \
+ .name = BLOCK_OPT_BACKING_FILE, \
+ .type = QEMU_OPT_STRING, \
+ .help = "File name of a base image" \
+ }, \
+ { \
+ .name = BLOCK_OPT_BACKING_FMT, \
+ .type = QEMU_OPT_STRING, \
+ .help = "Image format of the base image" \
+ }, \
+ { \
+ .name = BLOCK_OPT_DATA_FILE, \
+ .type = QEMU_OPT_STRING, \
+ .help = "File name of an external data file" \
+ }, \
+ { \
+ .name = BLOCK_OPT_DATA_FILE_RAW, \
+ .type = QEMU_OPT_BOOL, \
+ .help = "The external data file must stay valid " \
+ "as a raw image" \
+ }, \
+ { \
+ .name = BLOCK_OPT_ENCRYPT, \
+ .type = QEMU_OPT_BOOL, \
+ .help = "Encrypt the image with format 'aes'. (Deprecated " \
+ "in favor of " BLOCK_OPT_ENCRYPT_FORMAT "=aes)", \
+ }, \
+ { \
+ .name = BLOCK_OPT_ENCRYPT_FORMAT, \
+ .type = QEMU_OPT_STRING, \
+ .help = "Encrypt the image, format choices: 'aes', 'luks'", \
+ }, \
+ BLOCK_CRYPTO_OPT_DEF_KEY_SECRET("encrypt.", \
+ "ID of secret providing qcow AES key or LUKS passphrase"), \
+ BLOCK_CRYPTO_OPT_DEF_LUKS_CIPHER_ALG("encrypt."), \
+ BLOCK_CRYPTO_OPT_DEF_LUKS_CIPHER_MODE("encrypt."), \
+ BLOCK_CRYPTO_OPT_DEF_LUKS_IVGEN_ALG("encrypt."), \
+ BLOCK_CRYPTO_OPT_DEF_LUKS_IVGEN_HASH_ALG("encrypt."), \
+ BLOCK_CRYPTO_OPT_DEF_LUKS_HASH_ALG("encrypt."), \
+ BLOCK_CRYPTO_OPT_DEF_LUKS_ITER_TIME("encrypt."), \
+ { \
+ .name = BLOCK_OPT_CLUSTER_SIZE, \
+ .type = QEMU_OPT_SIZE, \
+ .help = "qcow2 cluster size", \
+ .def_value_str = stringify(DEFAULT_CLUSTER_SIZE) \
+ }, \
+ { \
+ .name = BLOCK_OPT_PREALLOC, \
+ .type = QEMU_OPT_STRING, \
+ .help = "Preallocation mode (allowed values: off, " \
+ "metadata, falloc, full)" \
+ }, \
+ { \
+ .name = BLOCK_OPT_LAZY_REFCOUNTS, \
+ .type = QEMU_OPT_BOOL, \
+ .help = "Postpone refcount updates", \
+ .def_value_str = "off" \
+ }, \
+ { \
+ .name = BLOCK_OPT_REFCOUNT_BITS, \
+ .type = QEMU_OPT_NUMBER, \
+ .help = "Width of a reference count entry in bits", \
+ .def_value_str = "16" \
+ }, \
+ { \
+ .name = BLOCK_OPT_COMPRESSION_TYPE, \
+ .type = QEMU_OPT_STRING, \
+ .help = "Compression method used for image cluster " \
+ "compression", \
+ .def_value_str = "zlib" \
+ }
+
static QemuOptsList qcow2_create_opts = {
.name = "qcow2-create-opts",
.head = QTAILQ_HEAD_INITIALIZER(qcow2_create_opts.head),
.desc = {
- {
- .name = BLOCK_OPT_SIZE,
- .type = QEMU_OPT_SIZE,
- .help = "Virtual disk size"
- },
- {
- .name = BLOCK_OPT_COMPAT_LEVEL,
- .type = QEMU_OPT_STRING,
- .help = "Compatibility level (v2 [0.10] or v3 [1.1])"
- },
- {
- .name = BLOCK_OPT_BACKING_FILE,
- .type = QEMU_OPT_STRING,
- .help = "File name of a base image"
- },
- {
- .name = BLOCK_OPT_BACKING_FMT,
- .type = QEMU_OPT_STRING,
- .help = "Image format of the base image"
- },
- {
- .name = BLOCK_OPT_DATA_FILE,
- .type = QEMU_OPT_STRING,
- .help = "File name of an external data file"
- },
- {
- .name = BLOCK_OPT_DATA_FILE_RAW,
- .type = QEMU_OPT_BOOL,
- .help = "The external data file must stay valid as a raw image"
- },
- {
- .name = BLOCK_OPT_ENCRYPT,
- .type = QEMU_OPT_BOOL,
- .help = "Encrypt the image with format 'aes'. (Deprecated "
- "in favor of " BLOCK_OPT_ENCRYPT_FORMAT "=aes)",
- },
- {
- .name = BLOCK_OPT_ENCRYPT_FORMAT,
- .type = QEMU_OPT_STRING,
- .help = "Encrypt the image, format choices: 'aes', 'luks'",
- },
- BLOCK_CRYPTO_OPT_DEF_KEY_SECRET("encrypt.",
- "ID of secret providing qcow AES key or LUKS passphrase"),
- BLOCK_CRYPTO_OPT_DEF_LUKS_CIPHER_ALG("encrypt."),
- BLOCK_CRYPTO_OPT_DEF_LUKS_CIPHER_MODE("encrypt."),
- BLOCK_CRYPTO_OPT_DEF_LUKS_IVGEN_ALG("encrypt."),
- BLOCK_CRYPTO_OPT_DEF_LUKS_IVGEN_HASH_ALG("encrypt."),
- BLOCK_CRYPTO_OPT_DEF_LUKS_HASH_ALG("encrypt."),
- BLOCK_CRYPTO_OPT_DEF_LUKS_ITER_TIME("encrypt."),
- {
- .name = BLOCK_OPT_CLUSTER_SIZE,
- .type = QEMU_OPT_SIZE,
- .help = "qcow2 cluster size",
- .def_value_str = stringify(DEFAULT_CLUSTER_SIZE)
- },
- {
- .name = BLOCK_OPT_PREALLOC,
- .type = QEMU_OPT_STRING,
- .help = "Preallocation mode (allowed values: off, metadata, "
- "falloc, full)"
- },
- {
- .name = BLOCK_OPT_LAZY_REFCOUNTS,
- .type = QEMU_OPT_BOOL,
- .help = "Postpone refcount updates",
- .def_value_str = "off"
- },
- {
- .name = BLOCK_OPT_REFCOUNT_BITS,
- .type = QEMU_OPT_NUMBER,
- .help = "Width of a reference count entry in bits",
- .def_value_str = "16"
- },
- {
- .name = BLOCK_OPT_COMPRESSION_TYPE,
- .type = QEMU_OPT_STRING,
- .help = "Compression method used for image cluster compression",
- .def_value_str = "zlib"
- },
+ QCOW_COMMON_OPTIONS,
+ { /* end of list */ }
+ }
+};
+
+static QemuOptsList qcow2_amend_opts = {
+ .name = "qcow2-amend-opts",
+ .head = QTAILQ_HEAD_INITIALIZER(qcow2_amend_opts.head),
+ .desc = {
+ QCOW_COMMON_OPTIONS,
{ /* end of list */ }
}
};
@@ -5792,6 +5806,7 @@ BlockDriver bdrv_qcow2 = {
.bdrv_inactivate = qcow2_inactivate,
.create_opts = &qcow2_create_opts,
+ .amend_opts = &qcow2_amend_opts,
.strong_runtime_opts = qcow2_strong_runtime_opts,
.mutable_opts = mutable_opts,
.bdrv_co_check = qcow2_co_check,
diff --git a/include/block/block_int.h b/include/block/block_int.h
index 066b9eaa40..ed335519cc 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -420,6 +420,10 @@ struct BlockDriver {
/* List of options for creating images, terminated by name == NULL */
QemuOptsList *create_opts;
+
+ /* List of options for image amend */
+ QemuOptsList *amend_opts;
+
/*
* If this driver supports reopening images this contains a
* NULL-terminated list of the runtime options that can be
diff --git a/qemu-img.c b/qemu-img.c
index 10d81f09db..0c4541b017 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -4077,11 +4077,11 @@ static int print_amend_option_help(const char *format)
return 1;
}
- /* Every driver supporting amendment must have create_opts */
- assert(drv->create_opts);
+ /* Every driver supporting amendment must have amend_opts */
+ assert(drv->amend_opts);
printf("Creation options for '%s':\n", format);
- qemu_opts_print_help(drv->create_opts, false);
+ qemu_opts_print_help(drv->amend_opts, false);
printf("\nNote that not all of these options may be amendable.\n");
return 0;
}
@@ -4091,7 +4091,7 @@ static int img_amend(int argc, char **argv)
Error *err = NULL;
int c, ret = 0;
char *options = NULL;
- QemuOptsList *create_opts = NULL;
+ QemuOptsList *amend_opts = NULL;
QemuOpts *opts = NULL;
const char *fmt = NULL, *filename, *cache;
int flags;
@@ -4222,11 +4222,11 @@ static int img_amend(int argc, char **argv)
goto out;
}
- /* Every driver supporting amendment must have create_opts */
- assert(bs->drv->create_opts);
+ /* Every driver supporting amendment must have amend_opts */
+ assert(bs->drv->amend_opts);
- create_opts = qemu_opts_append(create_opts, bs->drv->create_opts);
- opts = qemu_opts_create(create_opts, NULL, 0, &error_abort);
+ amend_opts = qemu_opts_append(amend_opts, bs->drv->amend_opts);
+ opts = qemu_opts_create(amend_opts, NULL, 0, &error_abort);
qemu_opts_do_parse(opts, options, NULL, &err);
if (err) {
error_report_err(err);
@@ -4249,7 +4249,7 @@ out:
out_no_progress:
blk_unref(blk);
qemu_opts_del(opts);
- qemu_opts_free(create_opts);
+ qemu_opts_free(amend_opts);
g_free(options);
if (ret) {
--
2.25.4
^ permalink raw reply related [flat|nested] 25+ messages in thread
* [PATCH v8 05/14] block/amend: refactor qcow2 amend options
2020-06-08 9:40 [PATCH v8 00/14] LUKS: encryption slot management using amend interface Maxim Levitsky
` (3 preceding siblings ...)
2020-06-08 9:40 ` [PATCH v8 04/14] block/amend: separate amend and create options for qemu-img Maxim Levitsky
@ 2020-06-08 9:40 ` Maxim Levitsky
2020-06-16 13:23 ` Max Reitz
2020-06-08 9:40 ` [PATCH v8 06/14] block/crypto: rename two functions Maxim Levitsky
` (10 subsequent siblings)
15 siblings, 1 reply; 25+ messages in thread
From: Maxim Levitsky @ 2020-06-08 9:40 UTC (permalink / raw)
To: qemu-devel
Cc: Kevin Wolf, Daniel P. Berrangé, qemu-block, John Snow,
Markus Armbruster, Max Reitz, Maxim Levitsky
Some qcow2 create options can't be used for amend.
Remove them from the qcow2 create options and add generic logic to detect
such options in qemu-img
Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
---
block/qcow2.c | 138 ++++++++------------------
qemu-img.c | 18 +++-
tests/qemu-iotests/049.out | 102 ++++++++++----------
tests/qemu-iotests/061.out | 12 ++-
tests/qemu-iotests/079.out | 18 ++--
tests/qemu-iotests/082.out | 158 ++++--------------------------
tests/qemu-iotests/085.out | 38 ++++----
tests/qemu-iotests/087.out | 6 +-
tests/qemu-iotests/115.out | 2 +-
tests/qemu-iotests/121.out | 4 +-
tests/qemu-iotests/125.out | 192 ++++++++++++++++++-------------------
tests/qemu-iotests/134.out | 2 +-
tests/qemu-iotests/144.out | 4 +-
tests/qemu-iotests/158.out | 4 +-
tests/qemu-iotests/182.out | 2 +-
tests/qemu-iotests/185.out | 8 +-
tests/qemu-iotests/188.out | 2 +-
tests/qemu-iotests/189.out | 4 +-
tests/qemu-iotests/198.out | 4 +-
tests/qemu-iotests/243.out | 16 ++--
tests/qemu-iotests/250.out | 2 +-
tests/qemu-iotests/255.out | 8 +-
tests/qemu-iotests/259.out | 2 +-
tests/qemu-iotests/263.out | 4 +-
tests/qemu-iotests/274.out | 46 ++++-----
tests/qemu-iotests/280.out | 2 +-
tests/qemu-iotests/284.out | 6 +-
27 files changed, 317 insertions(+), 487 deletions(-)
diff --git a/block/qcow2.c b/block/qcow2.c
index fcfd90f2e2..b3ed173a9b 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -3042,17 +3042,6 @@ static int qcow2_change_backing_file(BlockDriverState *bs,
return qcow2_update_header(bs);
}
-static int qcow2_crypt_method_from_format(const char *encryptfmt)
-{
- if (g_str_equal(encryptfmt, "luks")) {
- return QCOW_CRYPT_LUKS;
- } else if (g_str_equal(encryptfmt, "aes")) {
- return QCOW_CRYPT_AES;
- } else {
- return -EINVAL;
- }
-}
-
static int qcow2_set_up_encryption(BlockDriverState *bs,
QCryptoBlockCreateOptions *cryptoopts,
Error **errp)
@@ -5350,9 +5339,6 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
bool lazy_refcounts = s->use_lazy_refcounts;
bool data_file_raw = data_file_is_raw(bs);
const char *compat = NULL;
- uint64_t cluster_size = s->cluster_size;
- bool encrypt;
- int encformat;
int refcount_bits = s->refcount_bits;
int ret;
QemuOptDesc *desc = opts->list->desc;
@@ -5377,44 +5363,12 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
error_setg(errp, "Unknown compatibility level %s", compat);
return -EINVAL;
}
- } else if (!strcmp(desc->name, BLOCK_OPT_PREALLOC)) {
- error_setg(errp, "Cannot change preallocation mode");
- return -ENOTSUP;
} else if (!strcmp(desc->name, BLOCK_OPT_SIZE)) {
new_size = qemu_opt_get_size(opts, BLOCK_OPT_SIZE, 0);
} else if (!strcmp(desc->name, BLOCK_OPT_BACKING_FILE)) {
backing_file = qemu_opt_get(opts, BLOCK_OPT_BACKING_FILE);
} else if (!strcmp(desc->name, BLOCK_OPT_BACKING_FMT)) {
backing_format = qemu_opt_get(opts, BLOCK_OPT_BACKING_FMT);
- } else if (!strcmp(desc->name, BLOCK_OPT_ENCRYPT)) {
- encrypt = qemu_opt_get_bool(opts, BLOCK_OPT_ENCRYPT,
- !!s->crypto);
-
- if (encrypt != !!s->crypto) {
- error_setg(errp,
- "Changing the encryption flag is not supported");
- return -ENOTSUP;
- }
- } else if (!strcmp(desc->name, BLOCK_OPT_ENCRYPT_FORMAT)) {
- encformat = qcow2_crypt_method_from_format(
- qemu_opt_get(opts, BLOCK_OPT_ENCRYPT_FORMAT));
-
- if (encformat != s->crypt_method_header) {
- error_setg(errp,
- "Changing the encryption format is not supported");
- return -ENOTSUP;
- }
- } else if (g_str_has_prefix(desc->name, "encrypt.")) {
- error_setg(errp,
- "Changing the encryption parameters is not supported");
- return -ENOTSUP;
- } else if (!strcmp(desc->name, BLOCK_OPT_CLUSTER_SIZE)) {
- cluster_size = qemu_opt_get_size(opts, BLOCK_OPT_CLUSTER_SIZE,
- cluster_size);
- if (cluster_size != s->cluster_size) {
- error_setg(errp, "Changing the cluster size is not supported");
- return -ENOTSUP;
- }
} else if (!strcmp(desc->name, BLOCK_OPT_LAZY_REFCOUNTS)) {
lazy_refcounts = qemu_opt_get_bool(opts, BLOCK_OPT_LAZY_REFCOUNTS,
lazy_refcounts);
@@ -5444,22 +5398,6 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
"images");
return -EINVAL;
}
- } else if (!strcmp(desc->name, BLOCK_OPT_COMPRESSION_TYPE)) {
- const char *ct_name =
- qemu_opt_get(opts, BLOCK_OPT_COMPRESSION_TYPE);
- int compression_type =
- qapi_enum_parse(&Qcow2CompressionType_lookup, ct_name, -1,
- NULL);
- if (compression_type == -1) {
- error_setg(errp, "Unknown compression type: %s", ct_name);
- return -ENOTSUP;
- }
-
- if (compression_type != s->compression_type) {
- error_setg(errp, "Changing the compression type "
- "is not supported");
- return -ENOTSUP;
- }
} else {
/* if this point is reached, this probably means a new option was
* added without having it covered here */
@@ -5681,37 +5619,6 @@ void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset,
.help = "The external data file must stay valid " \
"as a raw image" \
}, \
- { \
- .name = BLOCK_OPT_ENCRYPT, \
- .type = QEMU_OPT_BOOL, \
- .help = "Encrypt the image with format 'aes'. (Deprecated " \
- "in favor of " BLOCK_OPT_ENCRYPT_FORMAT "=aes)", \
- }, \
- { \
- .name = BLOCK_OPT_ENCRYPT_FORMAT, \
- .type = QEMU_OPT_STRING, \
- .help = "Encrypt the image, format choices: 'aes', 'luks'", \
- }, \
- BLOCK_CRYPTO_OPT_DEF_KEY_SECRET("encrypt.", \
- "ID of secret providing qcow AES key or LUKS passphrase"), \
- BLOCK_CRYPTO_OPT_DEF_LUKS_CIPHER_ALG("encrypt."), \
- BLOCK_CRYPTO_OPT_DEF_LUKS_CIPHER_MODE("encrypt."), \
- BLOCK_CRYPTO_OPT_DEF_LUKS_IVGEN_ALG("encrypt."), \
- BLOCK_CRYPTO_OPT_DEF_LUKS_IVGEN_HASH_ALG("encrypt."), \
- BLOCK_CRYPTO_OPT_DEF_LUKS_HASH_ALG("encrypt."), \
- BLOCK_CRYPTO_OPT_DEF_LUKS_ITER_TIME("encrypt."), \
- { \
- .name = BLOCK_OPT_CLUSTER_SIZE, \
- .type = QEMU_OPT_SIZE, \
- .help = "qcow2 cluster size", \
- .def_value_str = stringify(DEFAULT_CLUSTER_SIZE) \
- }, \
- { \
- .name = BLOCK_OPT_PREALLOC, \
- .type = QEMU_OPT_STRING, \
- .help = "Preallocation mode (allowed values: off, " \
- "metadata, falloc, full)" \
- }, \
{ \
.name = BLOCK_OPT_LAZY_REFCOUNTS, \
.type = QEMU_OPT_BOOL, \
@@ -5723,19 +5630,50 @@ void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset,
.type = QEMU_OPT_NUMBER, \
.help = "Width of a reference count entry in bits", \
.def_value_str = "16" \
- }, \
- { \
- .name = BLOCK_OPT_COMPRESSION_TYPE, \
- .type = QEMU_OPT_STRING, \
- .help = "Compression method used for image cluster " \
- "compression", \
- .def_value_str = "zlib" \
}
static QemuOptsList qcow2_create_opts = {
.name = "qcow2-create-opts",
.head = QTAILQ_HEAD_INITIALIZER(qcow2_create_opts.head),
.desc = {
+ { \
+ .name = BLOCK_OPT_ENCRYPT, \
+ .type = QEMU_OPT_BOOL, \
+ .help = "Encrypt the image with format 'aes'. (Deprecated " \
+ "in favor of " BLOCK_OPT_ENCRYPT_FORMAT "=aes)", \
+ }, \
+ { \
+ .name = BLOCK_OPT_ENCRYPT_FORMAT, \
+ .type = QEMU_OPT_STRING, \
+ .help = "Encrypt the image, format choices: 'aes', 'luks'", \
+ }, \
+ BLOCK_CRYPTO_OPT_DEF_KEY_SECRET("encrypt.", \
+ "ID of secret providing qcow AES key or LUKS passphrase"), \
+ BLOCK_CRYPTO_OPT_DEF_LUKS_CIPHER_ALG("encrypt."), \
+ BLOCK_CRYPTO_OPT_DEF_LUKS_CIPHER_MODE("encrypt."), \
+ BLOCK_CRYPTO_OPT_DEF_LUKS_IVGEN_ALG("encrypt."), \
+ BLOCK_CRYPTO_OPT_DEF_LUKS_IVGEN_HASH_ALG("encrypt."), \
+ BLOCK_CRYPTO_OPT_DEF_LUKS_HASH_ALG("encrypt."), \
+ BLOCK_CRYPTO_OPT_DEF_LUKS_ITER_TIME("encrypt."), \
+ { \
+ .name = BLOCK_OPT_CLUSTER_SIZE, \
+ .type = QEMU_OPT_SIZE, \
+ .help = "qcow2 cluster size", \
+ .def_value_str = stringify(DEFAULT_CLUSTER_SIZE) \
+ }, \
+ { \
+ .name = BLOCK_OPT_PREALLOC, \
+ .type = QEMU_OPT_STRING, \
+ .help = "Preallocation mode (allowed values: off, " \
+ "metadata, falloc, full)" \
+ }, \
+ { \
+ .name = BLOCK_OPT_COMPRESSION_TYPE, \
+ .type = QEMU_OPT_STRING, \
+ .help = "Compression method used for image cluster " \
+ "compression", \
+ .def_value_str = "zlib" \
+ },
QCOW_COMMON_OPTIONS,
{ /* end of list */ }
}
diff --git a/qemu-img.c b/qemu-img.c
index 0c4541b017..381271a74e 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -4080,9 +4080,8 @@ static int print_amend_option_help(const char *format)
/* Every driver supporting amendment must have amend_opts */
assert(drv->amend_opts);
- printf("Creation options for '%s':\n", format);
+ printf("Amend options for '%s':\n", format);
qemu_opts_print_help(drv->amend_opts, false);
- printf("\nNote that not all of these options may be amendable.\n");
return 0;
}
@@ -4228,7 +4227,22 @@ static int img_amend(int argc, char **argv)
amend_opts = qemu_opts_append(amend_opts, bs->drv->amend_opts);
opts = qemu_opts_create(amend_opts, NULL, 0, &error_abort);
qemu_opts_do_parse(opts, options, NULL, &err);
+
if (err) {
+ /* Try to parse options using the create options */
+ Error *err1 = NULL;
+ amend_opts = qemu_opts_append(amend_opts, bs->drv->create_opts);
+ qemu_opts_del(opts);
+ opts = qemu_opts_create(amend_opts, NULL, 0, &error_abort);
+ qemu_opts_do_parse(opts, options, NULL, &err1);
+
+ if (!err1) {
+ error_append_hint(&err,
+ "This option is only supported for image creation\n");
+ } else {
+ error_free(err1);
+ }
+
error_report_err(err);
ret = -1;
goto out;
diff --git a/tests/qemu-iotests/049.out b/tests/qemu-iotests/049.out
index c54ae21b86..e77966446b 100644
--- a/tests/qemu-iotests/049.out
+++ b/tests/qemu-iotests/049.out
@@ -4,90 +4,90 @@ QA output created by 049
== 1. Traditional size parameter ==
qemu-img create -f qcow2 TEST_DIR/t.qcow2 1024
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 TEST_DIR/t.qcow2 1024b
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 TEST_DIR/t.qcow2 1k
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 TEST_DIR/t.qcow2 1K
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 TEST_DIR/t.qcow2 1M
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1048576 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1048576 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 TEST_DIR/t.qcow2 1G
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1073741824 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1073741824 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 TEST_DIR/t.qcow2 1T
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1099511627776 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1099511627776 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 TEST_DIR/t.qcow2 1024.0
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 TEST_DIR/t.qcow2 1024.0b
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 TEST_DIR/t.qcow2 1.5k
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1536 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1536 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 TEST_DIR/t.qcow2 1.5K
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1536 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1536 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 TEST_DIR/t.qcow2 1.5M
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1572864 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1572864 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 TEST_DIR/t.qcow2 1.5G
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1610612736 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1610612736 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 TEST_DIR/t.qcow2 1.5T
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1649267441664 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1649267441664 lazy_refcounts=off refcount_bits=16
== 2. Specifying size via -o ==
qemu-img create -f qcow2 -o size=1024 TEST_DIR/t.qcow2
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o size=1024b TEST_DIR/t.qcow2
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o size=1k TEST_DIR/t.qcow2
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o size=1K TEST_DIR/t.qcow2
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o size=1M TEST_DIR/t.qcow2
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1048576 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1048576 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o size=1G TEST_DIR/t.qcow2
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1073741824 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1073741824 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o size=1T TEST_DIR/t.qcow2
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1099511627776 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1099511627776 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o size=1024.0 TEST_DIR/t.qcow2
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o size=1024.0b TEST_DIR/t.qcow2
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1024 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1024 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o size=1.5k TEST_DIR/t.qcow2
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1536 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1536 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o size=1.5K TEST_DIR/t.qcow2
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1536 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1536 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o size=1.5M TEST_DIR/t.qcow2
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1572864 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1572864 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o size=1.5G TEST_DIR/t.qcow2
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1610612736 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1610612736 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o size=1.5T TEST_DIR/t.qcow2
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=1649267441664 cluster_size=65536 lazy_refcounts=off refcount_bits=16 compression_type=zlib
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 compression_type=zlib size=1649267441664 lazy_refcounts=off refcount_bits=16
== 3. Invalid sizes ==
@@ -129,84 +129,84 @@ qemu-img: TEST_DIR/t.qcow2: The image size must be specified only once
== Check correct interpretation of suffixes for cluster size ==
qemu-img create -f qcow2 -o cluster_size=1024 TEST_DIR/t.qcow2 64M
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 cluster_size=1024 lazy_refcounts=off refcount_bits=16 compression_type=zlib
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=1024 compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o cluster_size=1024b TEST_DIR/t.qcow2 64M
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 cluster_size=1024 lazy_refcounts=off refcount_bits=16 compression_type=zlib
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=1024 compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o cluster_size=1k TEST_DIR/t.qcow2 64M
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 cluster_size=1024 lazy_refcounts=off refcount_bits=16 compression_type=zlib
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=1024 compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o cluster_size=1K TEST_DIR/t.qcow2 64M
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 cluster_size=1024 lazy_refcounts=off refcount_bits=16 compression_type=zlib
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=1024 compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o cluster_size=1M TEST_DIR/t.qcow2 64M
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 cluster_size=1048576 lazy_refcounts=off refcount_bits=16 compression_type=zlib
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=1048576 compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16
qemu-img create -f qcow2 -o cluster_size=1024.0 TEST_DIR/t.qcow2 64M
-Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 cluster_size=1024 lazy_refcounts=off refcount_bits=16 compression_type=zlib
+Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=1024 co