summaryrefslogtreecommitdiffstats
path: root/block/qcow2.c
diff options
context:
space:
mode:
Diffstat (limited to 'block/qcow2.c')
-rw-r--r--block/qcow2.c350
1 files changed, 203 insertions, 147 deletions
diff --git a/block/qcow2.c b/block/qcow2.c
index 0cd2e6757e..38198b4e75 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -176,6 +176,19 @@ static ssize_t qcow2_crypto_hdr_write_func(QCryptoBlock *block, size_t offset,
return ret;
}
+static QDict*
+qcow2_extract_crypto_opts(QemuOpts *opts, const char *fmt, Error **errp)
+{
+ QDict *cryptoopts_qdict;
+ QDict *opts_qdict;
+
+ /* Extract "encrypt." options into a qdict */
+ opts_qdict = qemu_opts_to_qdict(opts, NULL);
+ qdict_extract_subqdict(opts_qdict, &cryptoopts_qdict, "encrypt.");
+ qobject_unref(opts_qdict);
+ qdict_put_str(cryptoopts_qdict, "format", fmt);
+ return cryptoopts_qdict;
+}
/*
* read qcow2 extension and fill bs
@@ -3042,17 +3055,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)
@@ -4239,8 +4241,8 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset,
old_file_size = ROUND_UP(old_file_size, s->cluster_size);
}
- nb_new_data_clusters = DIV_ROUND_UP(offset - old_length,
- s->cluster_size);
+ nb_new_data_clusters = (ROUND_UP(offset, s->cluster_size) -
+ start_of_cluster(s, old_length)) >> s->cluster_bits;
/* This is an overestimation; we will not actually allocate space for
* these in the file but just make sure the new refcount structures are
@@ -4317,10 +4319,21 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset,
int64_t nb_clusters = MIN(
nb_new_data_clusters,
s->l2_slice_size - offset_to_l2_slice_index(s, guest_offset));
- QCowL2Meta allocation = {
+ unsigned cow_start_length = offset_into_cluster(s, guest_offset);
+ QCowL2Meta allocation;
+ guest_offset = start_of_cluster(s, guest_offset);
+ allocation = (QCowL2Meta) {
.offset = guest_offset,
.alloc_offset = host_offset,
.nb_clusters = nb_clusters,
+ .cow_start = {
+ .offset = 0,
+ .nb_bytes = cow_start_length,
+ },
+ .cow_end = {
+ .offset = nb_clusters << s->cluster_bits,
+ .nb_bytes = 0,
+ },
};
qemu_co_queue_init(&allocation.dependent_requests);
@@ -4860,16 +4873,9 @@ static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs,
if (has_luks) {
g_autoptr(QCryptoBlockCreateOptions) create_opts = NULL;
- QDict *opts_qdict;
- QDict *cryptoopts;
+ QDict *cryptoopts = qcow2_extract_crypto_opts(opts, "luks", errp);
size_t headerlen;
- opts_qdict = qemu_opts_to_qdict(opts, NULL);
- qdict_extract_subqdict(opts_qdict, &cryptoopts, "encrypt.");
- qobject_unref(opts_qdict);
-
- qdict_put_str(cryptoopts, "format", "luks");
-
create_opts = block_crypto_create_opts_init(cryptoopts, errp);
qobject_unref(cryptoopts);
if (!create_opts) {
@@ -4981,7 +4987,6 @@ err:
static int qcow2_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
{
BDRVQcow2State *s = bs->opaque;
- bdi->unallocated_blocks_are_zero = true;
bdi->cluster_size = s->cluster_size;
bdi->vm_state_offset = qcow2_vm_state_offset(s);
return 0;
@@ -5273,6 +5278,7 @@ typedef enum Qcow2AmendOperation {
QCOW2_NO_OPERATION = 0,
QCOW2_UPGRADING,
+ QCOW2_UPDATING_ENCRYPTION,
QCOW2_CHANGING_REFCOUNT_ORDER,
QCOW2_DOWNGRADING,
} Qcow2AmendOperation;
@@ -5340,6 +5346,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;
@@ -5349,13 +5356,11 @@ 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;
Qcow2AmendHelperCBInfo helper_cb_info;
+ bool encryption_update = false;
while (desc && desc->name) {
if (!qemu_opt_find(opts, desc->name)) {
@@ -5376,44 +5381,24 @@ 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) {
+ } else if (g_str_has_prefix(desc->name, "encrypt.")) {
+ if (!s->crypto) {
error_setg(errp,
- "Changing the encryption flag is not supported");
- return -ENOTSUP;
+ "Can't amend encryption options - encryption not present");
+ return -EINVAL;
}
- } 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) {
+ if (s->crypt_method_header != QCOW_CRYPT_LUKS) {
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");
+ "Only LUKS encryption options can be amended");
return -ENOTSUP;
}
+ encryption_update = true;
} else if (!strcmp(desc->name, BLOCK_OPT_LAZY_REFCOUNTS)) {
lazy_refcounts = qemu_opt_get_bool(opts, BLOCK_OPT_LAZY_REFCOUNTS,
lazy_refcounts);
@@ -5443,22 +5428,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 */
@@ -5472,7 +5441,8 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
.original_status_cb = status_cb,
.original_cb_opaque = cb_opaque,
.total_operations = (new_version != old_version)
- + (s->refcount_bits != refcount_bits)
+ + (s->refcount_bits != refcount_bits) +
+ (encryption_update == true)
};
/* Upgrade first (some features may require compat=1.1) */
@@ -5485,6 +5455,33 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
}
}
+ if (encryption_update) {
+ QDict *amend_opts_dict;
+ QCryptoBlockAmendOptions *amend_opts;
+
+ helper_cb_info.current_operation = QCOW2_UPDATING_ENCRYPTION;
+ amend_opts_dict = qcow2_extract_crypto_opts(opts, "luks", errp);
+ if (!amend_opts_dict) {
+ return -EINVAL;
+ }
+ amend_opts = block_crypto_amend_opts_init(amend_opts_dict, errp);
+ qobject_unref(amend_opts_dict);
+ if (!amend_opts) {
+ return -EINVAL;
+ }
+ ret = qcrypto_block_amend_options(s->crypto,
+ qcow2_crypto_hdr_read_func,
+ qcow2_crypto_hdr_write_func,
+ bs,
+ amend_opts,
+ force,
+ errp);
+ qapi_free_QCryptoBlockAmendOptions(amend_opts);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
if (s->refcount_bits != refcount_bits) {
int refcount_order = ctz32(refcount_bits);
@@ -5598,6 +5595,44 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
return 0;
}
+static int coroutine_fn qcow2_co_amend(BlockDriverState *bs,
+ BlockdevAmendOptions *opts,
+ bool force,
+ Error **errp)
+{
+ BlockdevAmendOptionsQcow2 *qopts = &opts->u.qcow2;
+ BDRVQcow2State *s = bs->opaque;
+ int ret = 0;
+
+ if (qopts->has_encrypt) {
+ if (!s->crypto) {
+ error_setg(errp, "image is not encrypted, can't amend");
+ return -EOPNOTSUPP;
+ }
+
+ if (qopts->encrypt->format != Q_CRYPTO_BLOCK_FORMAT_LUKS) {
+ error_setg(errp,
+ "Amend can't be used to change the qcow2 encryption format");
+ return -EOPNOTSUPP;
+ }
+
+ if (s->crypt_method_header != QCOW_CRYPT_LUKS) {
+ error_setg(errp,
+ "Only LUKS encryption options can be amended for qcow2 with blockdev-amend");
+ return -EOPNOTSUPP;
+ }
+
+ ret = qcrypto_block_amend_options(s->crypto,
+ qcow2_crypto_hdr_read_func,
+ qcow2_crypto_hdr_write_func,
+ bs,
+ qopts->encrypt,
+ force,
+ errp);
+ }
+ return ret;
+}
+
/*
* If offset or size are negative, respectively, they will not be included in
* the BLOCK_IMAGE_CORRUPTED event emitted.
@@ -5648,89 +5683,108 @@ 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_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" \
+ }
+
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, \
+ .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" \
},
- {
- .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."),
+ QCOW_COMMON_OPTIONS,
+ { /* end of list */ }
+ }
+};
+
+static QemuOptsList qcow2_amend_opts = {
+ .name = "qcow2-amend-opts",
+ .head = QTAILQ_HEAD_INITIALIZER(qcow2_amend_opts.head),
+ .desc = {
+ BLOCK_CRYPTO_OPT_DEF_LUKS_STATE("encrypt."),
+ BLOCK_CRYPTO_OPT_DEF_LUKS_KEYSLOT("encrypt."),
+ BLOCK_CRYPTO_OPT_DEF_LUKS_OLD_SECRET("encrypt."),
+ BLOCK_CRYPTO_OPT_DEF_LUKS_NEW_SECRET("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 */ }
}
};
@@ -5791,10 +5845,12 @@ 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,
.bdrv_amend_options = qcow2_amend_options,
+ .bdrv_co_amend = qcow2_co_amend,
.bdrv_detach_aio_context = qcow2_detach_aio_context,
.bdrv_attach_aio_context = qcow2_attach_aio_context,