mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge remote-tracking branch 'remotes/maxreitz/tags/pull-block-2020-07-06' into staging
Block patches for 5.1:
- LUKS keyslot amendment
(+ patches to make the iotests pass on non-Linux systems, and to keep
the tests passing for qcow v1, and to skip LUKS tests (including
qcow2 LUKS) when the built qemu does not support it)
- Refactoring in the block layer: Drop the basically unnecessary
unallocated_blocks_are_zero field from BlockDriverInfo
- Fix qcow2 preallocation when the image size is not a multiple of the
cluster size
- Fix in block-copy code
# gpg: Signature made Mon 06 Jul 2020 11:02:53 BST
# gpg: using RSA key 91BEB60A30DB3E8857D11829F407DB0061D5CF40
# gpg: issuer "mreitz@redhat.com"
# gpg: Good signature from "Max Reitz <mreitz@redhat.com>" [full]
# Primary key fingerprint: 91BE B60A 30DB 3E88 57D1 1829 F407 DB00 61D5 CF40
* remotes/maxreitz/tags/pull-block-2020-07-06: (31 commits)
qed: Simplify backing reads
block: drop unallocated_blocks_are_zero
block/vhdx: drop unallocated_blocks_are_zero
block/file-posix: drop unallocated_blocks_are_zero
block/iscsi: drop unallocated_blocks_are_zero
block/crypto: drop unallocated_blocks_are_zero
block/vpc: return ZERO block-status when appropriate
block/vdi: return ZERO block-status when appropriate
block: inline bdrv_unallocated_blocks_are_zero()
qemu-img: convert: don't use unallocated_blocks_are_zero
iotests: add tests for blockdev-amend
block/qcow2: implement blockdev-amend
block/crypto: implement blockdev-amend
block/core: add generic infrastructure for x-blockdev-amend qmp command
iotests: qemu-img tests for luks key management
block/qcow2: extend qemu-img amend interface with crypto options
block/crypto: implement the encryption key management
block/crypto: rename two functions
block/amend: refactor qcow2 amend options
block/amend: separate amend and create options for qemu-img
...
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
@@ -5408,21 +5408,6 @@ int bdrv_has_zero_init(BlockDriverState *bs)
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool bdrv_unallocated_blocks_are_zero(BlockDriverState *bs)
|
||||
{
|
||||
BlockDriverInfo bdi;
|
||||
|
||||
if (bs->backing) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bdrv_get_info(bs, &bdi) == 0) {
|
||||
return bdi.unallocated_blocks_are_zero;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bdrv_can_write_zeroes_with_unmap(BlockDriverState *bs)
|
||||
{
|
||||
if (!(bs->open_flags & BDRV_O_UNMAP)) {
|
||||
@@ -6482,6 +6467,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 +6479,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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ block-obj-$(CONFIG_WIN32) += file-win32.o win32-aio.o
|
||||
block-obj-$(CONFIG_POSIX) += file-posix.o
|
||||
block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
|
||||
block-obj-$(CONFIG_LINUX_IO_URING) += io_uring.o
|
||||
block-obj-y += null.o mirror.o commit.o io.o create.o
|
||||
block-obj-y += null.o mirror.o commit.o io.o create.o amend.o
|
||||
block-obj-y += throttle-groups.o
|
||||
block-obj-$(CONFIG_LINUX) += nvme.o
|
||||
|
||||
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Block layer code related to image options amend
|
||||
*
|
||||
* Copyright (c) 2018 Kevin Wolf <kwolf@redhat.com>
|
||||
* Copyright (c) 2020 Red Hat. Inc
|
||||
*
|
||||
* Heavily based on create.c
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "block/block_int.h"
|
||||
#include "qemu/job.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qapi/qapi-commands-block-core.h"
|
||||
#include "qapi/qapi-visit-block-core.h"
|
||||
#include "qapi/clone-visitor.h"
|
||||
#include "qapi/error.h"
|
||||
|
||||
typedef struct BlockdevAmendJob {
|
||||
Job common;
|
||||
BlockdevAmendOptions *opts;
|
||||
BlockDriverState *bs;
|
||||
bool force;
|
||||
} BlockdevAmendJob;
|
||||
|
||||
static int coroutine_fn blockdev_amend_run(Job *job, Error **errp)
|
||||
{
|
||||
BlockdevAmendJob *s = container_of(job, BlockdevAmendJob, common);
|
||||
int ret;
|
||||
|
||||
job_progress_set_remaining(&s->common, 1);
|
||||
ret = s->bs->drv->bdrv_co_amend(s->bs, s->opts, s->force, errp);
|
||||
job_progress_update(&s->common, 1);
|
||||
qapi_free_BlockdevAmendOptions(s->opts);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const JobDriver blockdev_amend_job_driver = {
|
||||
.instance_size = sizeof(BlockdevAmendJob),
|
||||
.job_type = JOB_TYPE_AMEND,
|
||||
.run = blockdev_amend_run,
|
||||
};
|
||||
|
||||
void qmp_x_blockdev_amend(const char *job_id,
|
||||
const char *node_name,
|
||||
BlockdevAmendOptions *options,
|
||||
bool has_force,
|
||||
bool force,
|
||||
Error **errp)
|
||||
{
|
||||
BlockdevAmendJob *s;
|
||||
const char *fmt = BlockdevDriver_str(options->driver);
|
||||
BlockDriver *drv = bdrv_find_format(fmt);
|
||||
BlockDriverState *bs = bdrv_find_node(node_name);
|
||||
|
||||
|
||||
if (!drv) {
|
||||
error_setg(errp, "Block driver '%s' not found or not supported", fmt);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the driver is in the schema, we know that it exists. But it may not
|
||||
* be whitelisted.
|
||||
*/
|
||||
if (bdrv_uses_whitelist() && !bdrv_is_whitelisted(drv, false)) {
|
||||
error_setg(errp, "Driver is not whitelisted");
|
||||
return;
|
||||
}
|
||||
|
||||
if (bs->drv != drv) {
|
||||
error_setg(errp,
|
||||
"x-blockdev-amend doesn't support changing the block driver");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Error out if the driver doesn't support .bdrv_co_amend */
|
||||
if (!drv->bdrv_co_amend) {
|
||||
error_setg(errp, "Driver does not support x-blockdev-amend");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Create the block job */
|
||||
s = job_create(job_id, &blockdev_amend_job_driver, NULL,
|
||||
bdrv_get_aio_context(bs), JOB_DEFAULT | JOB_MANUAL_DISMISS,
|
||||
NULL, NULL, errp);
|
||||
if (!s) {
|
||||
return;
|
||||
}
|
||||
|
||||
s->bs = bs,
|
||||
s->opts = QAPI_CLONE(BlockdevAmendOptions, options),
|
||||
s->force = has_force ? force : false;
|
||||
job_start(&s->common);
|
||||
}
|
||||
+3
-1
@@ -622,8 +622,10 @@ out:
|
||||
* block_copy_task_run. If it fails, it means some task already failed
|
||||
* for real reason, let's return first failure.
|
||||
* Still, assert that we don't rewrite failure by success.
|
||||
*
|
||||
* Note: ret may be positive here because of block-status result.
|
||||
*/
|
||||
assert(ret == 0 || aio_task_pool_status(aio) < 0);
|
||||
assert(ret >= 0 || aio_task_pool_status(aio) < 0);
|
||||
ret = aio_task_pool_status(aio);
|
||||
|
||||
aio_task_pool_free(aio);
|
||||
|
||||
+190
-17
@@ -37,6 +37,7 @@ typedef struct BlockCrypto BlockCrypto;
|
||||
|
||||
struct BlockCrypto {
|
||||
QCryptoBlock *block;
|
||||
bool updating_keys;
|
||||
};
|
||||
|
||||
|
||||
@@ -71,6 +72,24 @@ static ssize_t block_crypto_read_func(QCryptoBlock *block,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t block_crypto_write_func(QCryptoBlock *block,
|
||||
size_t offset,
|
||||
const uint8_t *buf,
|
||||
size_t buflen,
|
||||
void *opaque,
|
||||
Error **errp)
|
||||
{
|
||||
BlockDriverState *bs = opaque;
|
||||
ssize_t ret;
|
||||
|
||||
ret = bdrv_pwrite(bs->file, offset, buf, buflen);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not write encryption header");
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
struct BlockCryptoCreateData {
|
||||
BlockBackend *blk;
|
||||
@@ -79,12 +98,12 @@ struct BlockCryptoCreateData {
|
||||
};
|
||||
|
||||
|
||||
static ssize_t block_crypto_write_func(QCryptoBlock *block,
|
||||
size_t offset,
|
||||
const uint8_t *buf,
|
||||
size_t buflen,
|
||||
void *opaque,
|
||||
Error **errp)
|
||||
static ssize_t block_crypto_create_write_func(QCryptoBlock *block,
|
||||
size_t offset,
|
||||
const uint8_t *buf,
|
||||
size_t buflen,
|
||||
void *opaque,
|
||||
Error **errp)
|
||||
{
|
||||
struct BlockCryptoCreateData *data = opaque;
|
||||
ssize_t ret;
|
||||
@@ -97,11 +116,10 @@ static ssize_t block_crypto_write_func(QCryptoBlock *block,
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static ssize_t block_crypto_init_func(QCryptoBlock *block,
|
||||
size_t headerlen,
|
||||
void *opaque,
|
||||
Error **errp)
|
||||
static ssize_t block_crypto_create_init_func(QCryptoBlock *block,
|
||||
size_t headerlen,
|
||||
void *opaque,
|
||||
Error **errp)
|
||||
{
|
||||
struct BlockCryptoCreateData *data = opaque;
|
||||
Error *local_error = NULL;
|
||||
@@ -167,6 +185,19 @@ static QemuOptsList block_crypto_create_opts_luks = {
|
||||
};
|
||||
|
||||
|
||||
static QemuOptsList block_crypto_amend_opts_luks = {
|
||||
.name = "crypto",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(block_crypto_create_opts_luks.head),
|
||||
.desc = {
|
||||
BLOCK_CRYPTO_OPT_DEF_LUKS_STATE(""),
|
||||
BLOCK_CRYPTO_OPT_DEF_LUKS_KEYSLOT(""),
|
||||
BLOCK_CRYPTO_OPT_DEF_LUKS_OLD_SECRET(""),
|
||||
BLOCK_CRYPTO_OPT_DEF_LUKS_NEW_SECRET(""),
|
||||
BLOCK_CRYPTO_OPT_DEF_LUKS_ITER_TIME(""),
|
||||
{ /* end of list */ }
|
||||
},
|
||||
};
|
||||
|
||||
QCryptoBlockOpenOptions *
|
||||
block_crypto_open_opts_init(QDict *opts, Error **errp)
|
||||
{
|
||||
@@ -202,6 +233,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,
|
||||
@@ -296,8 +344,8 @@ static int block_crypto_co_create_generic(BlockDriverState *bs,
|
||||
};
|
||||
|
||||
crypto = qcrypto_block_create(opts, NULL,
|
||||
block_crypto_init_func,
|
||||
block_crypto_write_func,
|
||||
block_crypto_create_init_func,
|
||||
block_crypto_create_write_func,
|
||||
&data,
|
||||
errp);
|
||||
|
||||
@@ -710,7 +758,6 @@ static int block_crypto_get_info_luks(BlockDriverState *bs,
|
||||
return ret;
|
||||
}
|
||||
|
||||
bdi->unallocated_blocks_are_zero = false;
|
||||
bdi->cluster_size = subbdi.cluster_size;
|
||||
|
||||
return 0;
|
||||
@@ -742,6 +789,131 @@ block_crypto_get_specific_info_luks(BlockDriverState *bs, Error **errp)
|
||||
return spec_info;
|
||||
}
|
||||
|
||||
static int
|
||||
block_crypto_amend_options_generic_luks(BlockDriverState *bs,
|
||||
QCryptoBlockAmendOptions *amend_options,
|
||||
bool force,
|
||||
Error **errp)
|
||||
{
|
||||
BlockCrypto *crypto = bs->opaque;
|
||||
int ret;
|
||||
|
||||
assert(crypto);
|
||||
assert(crypto->block);
|
||||
|
||||
/* apply for exclusive read/write permissions to the underlying file*/
|
||||
crypto->updating_keys = true;
|
||||
ret = bdrv_child_refresh_perms(bs, bs->file, errp);
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = qcrypto_block_amend_options(crypto->block,
|
||||
block_crypto_read_func,
|
||||
block_crypto_write_func,
|
||||
bs,
|
||||
amend_options,
|
||||
force,
|
||||
errp);
|
||||
cleanup:
|
||||
/* release exclusive read/write permissions to the underlying file*/
|
||||
crypto->updating_keys = false;
|
||||
bdrv_child_refresh_perms(bs, bs->file, errp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
block_crypto_amend_options_luks(BlockDriverState *bs,
|
||||
QemuOpts *opts,
|
||||
BlockDriverAmendStatusCB *status_cb,
|
||||
void *cb_opaque,
|
||||
bool force,
|
||||
Error **errp)
|
||||
{
|
||||
BlockCrypto *crypto = bs->opaque;
|
||||
QDict *cryptoopts = NULL;
|
||||
QCryptoBlockAmendOptions *amend_options = NULL;
|
||||
int ret = -EINVAL;
|
||||
|
||||
assert(crypto);
|
||||
assert(crypto->block);
|
||||
|
||||
cryptoopts = qemu_opts_to_qdict(opts, NULL);
|
||||
qdict_put_str(cryptoopts, "format", "luks");
|
||||
amend_options = block_crypto_amend_opts_init(cryptoopts, errp);
|
||||
qobject_unref(cryptoopts);
|
||||
if (!amend_options) {
|
||||
goto cleanup;
|
||||
}
|
||||
ret = block_crypto_amend_options_generic_luks(bs, amend_options,
|
||||
force, errp);
|
||||
cleanup:
|
||||
qapi_free_QCryptoBlockAmendOptions(amend_options);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
coroutine_fn block_crypto_co_amend_luks(BlockDriverState *bs,
|
||||
BlockdevAmendOptions *opts,
|
||||
bool force,
|
||||
Error **errp)
|
||||
{
|
||||
QCryptoBlockAmendOptions amend_opts;
|
||||
|
||||
amend_opts = (QCryptoBlockAmendOptions) {
|
||||
.format = Q_CRYPTO_BLOCK_FORMAT_LUKS,
|
||||
.u.luks = *qapi_BlockdevAmendOptionsLUKS_base(&opts->u.luks),
|
||||
};
|
||||
return block_crypto_amend_options_generic_luks(bs, &amend_opts,
|
||||
force, errp);
|
||||
}
|
||||
|
||||
static void
|
||||
block_crypto_child_perms(BlockDriverState *bs, BdrvChild *c,
|
||||
const BdrvChildRole role,
|
||||
BlockReopenQueue *reopen_queue,
|
||||
uint64_t perm, uint64_t shared,
|
||||
uint64_t *nperm, uint64_t *nshared)
|
||||
{
|
||||
|
||||
BlockCrypto *crypto = bs->opaque;
|
||||
|
||||
bdrv_default_perms(bs, c, role, reopen_queue, perm, shared, nperm, nshared);
|
||||
|
||||
/*
|
||||
* For backward compatibility, manually share the write
|
||||
* and resize permission
|
||||
*/
|
||||
*nshared |= (BLK_PERM_WRITE | BLK_PERM_RESIZE);
|
||||
/*
|
||||
* Since we are not fully a format driver, don't always request
|
||||
* the read/resize permission but only when explicitly
|
||||
* requested
|
||||
*/
|
||||
*nperm &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE);
|
||||
*nperm |= perm & (BLK_PERM_WRITE | BLK_PERM_RESIZE);
|
||||
|
||||
/*
|
||||
* This driver doesn't modify LUKS metadata except
|
||||
* when updating the encryption slots.
|
||||
* Thus unlike a proper format driver we don't ask for
|
||||
* shared write/read permission. However we need it
|
||||
* when we are updating the keys, to ensure that only we
|
||||
* have access to the device.
|
||||
*
|
||||
* Encryption update will set the crypto->updating_keys
|
||||
* during that period and refresh permissions
|
||||
*
|
||||
*/
|
||||
if (crypto->updating_keys) {
|
||||
/* need exclusive write access for header update */
|
||||
*nperm |= BLK_PERM_WRITE;
|
||||
/* unshare read and write permission */
|
||||
*nshared &= ~(BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static const char *const block_crypto_strong_runtime_opts[] = {
|
||||
BLOCK_CRYPTO_OPT_LUKS_KEY_SECRET,
|
||||
|
||||
@@ -754,13 +926,12 @@ static BlockDriver bdrv_crypto_luks = {
|
||||
.bdrv_probe = block_crypto_probe_luks,
|
||||
.bdrv_open = block_crypto_open_luks,
|
||||
.bdrv_close = block_crypto_close,
|
||||
/* This driver doesn't modify LUKS metadata except when creating image.
|
||||
* Allow share-rw=on as a special case. */
|
||||
.bdrv_child_perm = bdrv_default_perms,
|
||||
.bdrv_child_perm = block_crypto_child_perms,
|
||||
.bdrv_co_create = block_crypto_co_create_luks,
|
||||
.bdrv_co_create_opts = block_crypto_co_create_opts_luks,
|
||||
.bdrv_co_truncate = block_crypto_co_truncate,
|
||||
.create_opts = &block_crypto_create_opts_luks,
|
||||
.amend_opts = &block_crypto_amend_opts_luks,
|
||||
|
||||
.bdrv_reopen_prepare = block_crypto_reopen_prepare,
|
||||
.bdrv_refresh_limits = block_crypto_refresh_limits,
|
||||
@@ -770,6 +941,8 @@ static BlockDriver bdrv_crypto_luks = {
|
||||
.bdrv_measure = block_crypto_measure,
|
||||
.bdrv_get_info = block_crypto_get_info_luks,
|
||||
.bdrv_get_specific_info = block_crypto_get_specific_info_luks,
|
||||
.bdrv_amend_options = block_crypto_amend_options_luks,
|
||||
.bdrv_co_amend = block_crypto_co_amend_luks,
|
||||
|
||||
.is_format = true,
|
||||
|
||||
|
||||
@@ -41,6 +41,11 @@
|
||||
#define BLOCK_CRYPTO_OPT_LUKS_IVGEN_HASH_ALG "ivgen-hash-alg"
|
||||
#define BLOCK_CRYPTO_OPT_LUKS_HASH_ALG "hash-alg"
|
||||
#define BLOCK_CRYPTO_OPT_LUKS_ITER_TIME "iter-time"
|
||||
#define BLOCK_CRYPTO_OPT_LUKS_KEYSLOT "keyslot"
|
||||
#define BLOCK_CRYPTO_OPT_LUKS_STATE "state"
|
||||
#define BLOCK_CRYPTO_OPT_LUKS_OLD_SECRET "old-secret"
|
||||
#define BLOCK_CRYPTO_OPT_LUKS_NEW_SECRET "new-secret"
|
||||
|
||||
|
||||
#define BLOCK_CRYPTO_OPT_DEF_LUKS_KEY_SECRET(prefix) \
|
||||
BLOCK_CRYPTO_OPT_DEF_KEY_SECRET(prefix, \
|
||||
@@ -88,9 +93,41 @@
|
||||
.help = "Time to spend in PBKDF in milliseconds", \
|
||||
}
|
||||
|
||||
#define BLOCK_CRYPTO_OPT_DEF_LUKS_STATE(prefix) \
|
||||
{ \
|
||||
.name = prefix BLOCK_CRYPTO_OPT_LUKS_STATE, \
|
||||
.type = QEMU_OPT_STRING, \
|
||||
.help = "Select new state of affected keyslots (active/inactive)",\
|
||||
}
|
||||
|
||||
#define BLOCK_CRYPTO_OPT_DEF_LUKS_KEYSLOT(prefix) \
|
||||
{ \
|
||||
.name = prefix BLOCK_CRYPTO_OPT_LUKS_KEYSLOT, \
|
||||
.type = QEMU_OPT_NUMBER, \
|
||||
.help = "Select a single keyslot to modify explicitly",\
|
||||
}
|
||||
|
||||
#define BLOCK_CRYPTO_OPT_DEF_LUKS_OLD_SECRET(prefix) \
|
||||
{ \
|
||||
.name = prefix BLOCK_CRYPTO_OPT_LUKS_OLD_SECRET, \
|
||||
.type = QEMU_OPT_STRING, \
|
||||
.help = "Select all keyslots that match this password", \
|
||||
}
|
||||
|
||||
#define BLOCK_CRYPTO_OPT_DEF_LUKS_NEW_SECRET(prefix) \
|
||||
{ \
|
||||
.name = prefix BLOCK_CRYPTO_OPT_LUKS_NEW_SECRET, \
|
||||
.type = QEMU_OPT_STRING, \
|
||||
.help = "New secret to set in the matching keyslots. " \
|
||||
"Empty string to erase", \
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
||||
@@ -2878,9 +2878,6 @@ static int coroutine_fn raw_co_pwrite_zeroes(
|
||||
|
||||
static int raw_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
|
||||
bdi->unallocated_blocks_are_zero = s->discard_zeroes;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -2406,16 +2406,16 @@ static int coroutine_fn bdrv_co_block_status(BlockDriverState *bs,
|
||||
|
||||
if (ret & (BDRV_BLOCK_DATA | BDRV_BLOCK_ZERO)) {
|
||||
ret |= BDRV_BLOCK_ALLOCATED;
|
||||
} else if (want_zero) {
|
||||
if (bdrv_unallocated_blocks_are_zero(bs)) {
|
||||
ret |= BDRV_BLOCK_ZERO;
|
||||
} else if (bs->backing) {
|
||||
} else if (want_zero && bs->drv->supports_backing) {
|
||||
if (bs->backing) {
|
||||
BlockDriverState *bs2 = bs->backing->bs;
|
||||
int64_t size2 = bdrv_getlength(bs2);
|
||||
|
||||
if (size2 >= 0 && offset >= size2) {
|
||||
ret |= BDRV_BLOCK_ZERO;
|
||||
}
|
||||
} else {
|
||||
ret |= BDRV_BLOCK_ZERO;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2163,7 +2163,6 @@ static int coroutine_fn iscsi_co_truncate(BlockDriverState *bs, int64_t offset,
|
||||
static int iscsi_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
||||
{
|
||||
IscsiLun *iscsilun = bs->opaque;
|
||||
bdi->unallocated_blocks_are_zero = iscsilun->lbprz;
|
||||
bdi->cluster_size = iscsilun->cluster_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
+205
-149
@@ -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) {
|
||||
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");
|
||||
if (!s->crypto) {
|
||||
error_setg(errp,
|
||||
"Can't amend encryption options - encryption not present");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (s->crypt_method_header != QCOW_CRYPT_LUKS) {
|
||||
error_setg(errp,
|
||||
"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_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_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."),
|
||||
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,
|
||||
|
||||
+6
-59
@@ -849,56 +849,18 @@ static BDRVQEDState *acb_to_s(QEDAIOCB *acb)
|
||||
* @s: QED state
|
||||
* @pos: Byte position in device
|
||||
* @qiov: Destination I/O vector
|
||||
* @backing_qiov: Possibly shortened copy of qiov, to be allocated here
|
||||
* @cb: Completion function
|
||||
* @opaque: User data for completion function
|
||||
*
|
||||
* This function reads qiov->size bytes starting at pos from the backing file.
|
||||
* If there is no backing file then zeroes are read.
|
||||
*/
|
||||
static int coroutine_fn qed_read_backing_file(BDRVQEDState *s, uint64_t pos,
|
||||
QEMUIOVector *qiov,
|
||||
QEMUIOVector **backing_qiov)
|
||||
QEMUIOVector *qiov)
|
||||
{
|
||||
uint64_t backing_length = 0;
|
||||
size_t size;
|
||||
int ret;
|
||||
|
||||
/* If there is a backing file, get its length. Treat the absence of a
|
||||
* backing file like a zero length backing file.
|
||||
*/
|
||||
if (s->bs->backing) {
|
||||
int64_t l = bdrv_getlength(s->bs->backing->bs);
|
||||
if (l < 0) {
|
||||
return l;
|
||||
}
|
||||
backing_length = l;
|
||||
}
|
||||
|
||||
/* Zero all sectors if reading beyond the end of the backing file */
|
||||
if (pos >= backing_length ||
|
||||
pos + qiov->size > backing_length) {
|
||||
qemu_iovec_memset(qiov, 0, 0, qiov->size);
|
||||
}
|
||||
|
||||
/* Complete now if there are no backing file sectors to read */
|
||||
if (pos >= backing_length) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If the read straddles the end of the backing file, shorten it */
|
||||
size = MIN((uint64_t)backing_length - pos, qiov->size);
|
||||
|
||||
assert(*backing_qiov == NULL);
|
||||
*backing_qiov = g_new(QEMUIOVector, 1);
|
||||
qemu_iovec_init(*backing_qiov, qiov->niov);
|
||||
qemu_iovec_concat(*backing_qiov, qiov, 0, size);
|
||||
|
||||
BLKDBG_EVENT(s->bs->file, BLKDBG_READ_BACKING_AIO);
|
||||
ret = bdrv_co_preadv(s->bs->backing, pos, size, *backing_qiov, 0);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
BLKDBG_EVENT(s->bs->file, BLKDBG_READ_BACKING_AIO);
|
||||
return bdrv_co_preadv(s->bs->backing, pos, qiov->size, qiov, 0);
|
||||
}
|
||||
qemu_iovec_memset(qiov, 0, 0, qiov->size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -915,7 +877,6 @@ static int coroutine_fn qed_copy_from_backing_file(BDRVQEDState *s,
|
||||
uint64_t offset)
|
||||
{
|
||||
QEMUIOVector qiov;
|
||||
QEMUIOVector *backing_qiov = NULL;
|
||||
int ret;
|
||||
|
||||
/* Skip copy entirely if there is no work to do */
|
||||
@@ -925,13 +886,7 @@ static int coroutine_fn qed_copy_from_backing_file(BDRVQEDState *s,
|
||||
|
||||
qemu_iovec_init_buf(&qiov, qemu_blockalign(s->bs, len), len);
|
||||
|
||||
ret = qed_read_backing_file(s, pos, &qiov, &backing_qiov);
|
||||
|
||||
if (backing_qiov) {
|
||||
qemu_iovec_destroy(backing_qiov);
|
||||
g_free(backing_qiov);
|
||||
backing_qiov = NULL;
|
||||
}
|
||||
ret = qed_read_backing_file(s, pos, &qiov);
|
||||
|
||||
if (ret) {
|
||||
goto out;
|
||||
@@ -1339,8 +1294,7 @@ static int coroutine_fn qed_aio_read_data(void *opaque, int ret,
|
||||
qemu_iovec_memset(&acb->cur_qiov, 0, 0, acb->cur_qiov.size);
|
||||
r = 0;
|
||||
} else if (ret != QED_CLUSTER_FOUND) {
|
||||
r = qed_read_backing_file(s, acb->cur_pos, &acb->cur_qiov,
|
||||
&acb->backing_qiov);
|
||||
r = qed_read_backing_file(s, acb->cur_pos, &acb->cur_qiov);
|
||||
} else {
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
|
||||
r = bdrv_co_preadv(bs->file, offset, acb->cur_qiov.size,
|
||||
@@ -1365,12 +1319,6 @@ static int coroutine_fn qed_aio_next_io(QEDAIOCB *acb)
|
||||
while (1) {
|
||||
trace_qed_aio_next_io(s, acb, 0, acb->cur_pos + acb->cur_qiov.size);
|
||||
|
||||
if (acb->backing_qiov) {
|
||||
qemu_iovec_destroy(acb->backing_qiov);
|
||||
g_free(acb->backing_qiov);
|
||||
acb->backing_qiov = NULL;
|
||||
}
|
||||
|
||||
acb->qiov_offset += acb->cur_qiov.size;
|
||||
acb->cur_pos += acb->cur_qiov.size;
|
||||
qemu_iovec_reset(&acb->cur_qiov);
|
||||
@@ -1514,7 +1462,6 @@ static int bdrv_qed_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
||||
memset(bdi, 0, sizeof(*bdi));
|
||||
bdi->cluster_size = s->header.cluster_size;
|
||||
bdi->is_dirty = s->header.features & QED_F_NEED_CHECK;
|
||||
bdi->unallocated_blocks_are_zero = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -140,7 +140,6 @@ typedef struct QEDAIOCB {
|
||||
|
||||
/* Current cluster scatter-gather list */
|
||||
QEMUIOVector cur_qiov;
|
||||
QEMUIOVector *backing_qiov;
|
||||
uint64_t cur_pos; /* position on block device, in bytes */
|
||||
uint64_t cur_cluster; /* cluster offset in image file */
|
||||
unsigned int cur_nclusters; /* number of clusters being accessed */
|
||||
|
||||
+1
-2
@@ -334,7 +334,6 @@ static int vdi_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
||||
logout("\n");
|
||||
bdi->cluster_size = s->block_size;
|
||||
bdi->vm_state_offset = 0;
|
||||
bdi->unallocated_blocks_are_zero = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -536,7 +535,7 @@ static int coroutine_fn vdi_co_block_status(BlockDriverState *bs,
|
||||
*pnum = MIN(s->block_size - index_in_block, bytes);
|
||||
result = VDI_IS_ALLOCATED(bmap_entry);
|
||||
if (!result) {
|
||||
return 0;
|
||||
return BDRV_BLOCK_ZERO;
|
||||
}
|
||||
|
||||
*map = s->header.offset_data + (uint64_t)bmap_entry * s->block_size +
|
||||
|
||||
@@ -1164,9 +1164,6 @@ static int vhdx_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
||||
|
||||
bdi->cluster_size = s->block_size;
|
||||
|
||||
bdi->unallocated_blocks_are_zero =
|
||||
(s->params.data_bits & VHDX_PARAMS_HAS_PARENT) == 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+1
-2
@@ -606,7 +606,6 @@ static int vpc_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
||||
bdi->cluster_size = s->block_size;
|
||||
}
|
||||
|
||||
bdi->unallocated_blocks_are_zero = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -745,7 +744,7 @@ static int coroutine_fn vpc_co_block_status(BlockDriverState *bs,
|
||||
image_offset = get_image_offset(bs, offset, false, NULL);
|
||||
allocated = (image_offset != -1);
|
||||
*pnum = 0;
|
||||
ret = 0;
|
||||
ret = BDRV_BLOCK_ZERO;
|
||||
|
||||
do {
|
||||
/* All sectors in a block are contiguous (without using the bitmap) */
|
||||
|
||||
+412
-4
File diff suppressed because it is too large
Load Diff
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -21,11 +21,6 @@ typedef struct BlockDriverInfo {
|
||||
/* offset at which the VM state can be saved (0 if not possible) */
|
||||
int64_t vm_state_offset;
|
||||
bool is_dirty;
|
||||
/*
|
||||
* True if unallocated blocks read back as zeroes. This is equivalent
|
||||
* to the LBPRZ flag in the SCSI logical block provisioning page.
|
||||
*/
|
||||
bool unallocated_blocks_are_zero;
|
||||
/*
|
||||
* True if this block driver only supports compressed writes
|
||||
*/
|
||||
@@ -450,6 +445,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 */
|
||||
@@ -488,7 +484,6 @@ int bdrv_pdiscard(BdrvChild *child, int64_t offset, int64_t bytes);
|
||||
int bdrv_co_pdiscard(BdrvChild *child, int64_t offset, int64_t bytes);
|
||||
int bdrv_has_zero_init_1(BlockDriverState *bs);
|
||||
int bdrv_has_zero_init(BlockDriverState *bs);
|
||||
bool bdrv_unallocated_blocks_are_zero(BlockDriverState *bs);
|
||||
bool bdrv_can_write_zeroes_with_unmap(BlockDriverState *bs);
|
||||
int bdrv_block_status(BlockDriverState *bs, int64_t offset,
|
||||
int64_t bytes, int64_t *pnum, int64_t *map,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user