mirror of https://gitee.com/openkylin/qemu.git
Block layer patches (v2)
-----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iQIcBAABAgAGBQJV9uA2AAoJEH8JsnLIjy/WPa0P/0tjyUtcp+rTd2yAzC+BQlOA dxjq3c3P2HbJKnKa74PwgIBqt7w20TRa8OtMXuJ9XB75iuVRs51dXUDYHUCNvYbW dse33PRAUoSYfiaJ3UrsstM5PJH9sDvPHCBekP9CrUa+S9AdcX/7GUiXaiIwB+sj X2aur6muwFMK6hIHnUTYypx11/pYYvxVOm5xDMHQWtzbtXHeyVyxJvZkLZzT/DJ2 1sP3P65Ku0gZQA3rMOnKV6iYhAxrApgAJzhDzPdjKiD7nfxiatIauTvxXhMM2h6Y bHHAXAHbf8/kBPbklltwuihXX6/OdMM02S7dU42cPp5TFSPYDLLfRoF34pVy8Ycj 9BK8H9NNUg/TbHxWv8JLKcuTvk0wv7TDa+zah/Rt7o6jTSn50sxOWnMbj1KbP+IK 9zkg0hwvUhqDCbkqd1iFYe/5DfVA7eUu5MwhE0Dkncqflmmytw5BZAYFWuPOP4u8 rH66kg8JFIhLp+H0R3lqSBTezLh8GwMQRTNfrbemiDkA8Pd3GXhNHg3tGPTXK+FS 4YwUTL2AaJgDRXzz3CpaYh2Pku5t7LsXKRCG3BR7corkhmTBNiHn6V07D6d1kxHa cnzsG2gvJqDzELzG3tfsTGkfCtNJrqD0Uj+bB+f7V3K7TiN4RcC2b0Nejn54Jp94 YZMLP101bpYIPTkVDnRe =R3AS -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/kevin/tags/for-upstream' into staging Block layer patches (v2) # gpg: Signature made Mon 14 Sep 2015 15:56:54 BST using RSA key ID C88F2FD6 # gpg: Good signature from "Kevin Wolf <kwolf@redhat.com>" * remotes/kevin/tags/for-upstream: (23 commits) qcow2: Make qcow2_alloc_bytes() more explicit vmdk: Fix next_cluster_sector for compressed write iotests: Add test for checking large image files qcow2: Make size_to_clusters() return uint64_t qemu-iotests: More qcow2 reopen tests qemu-iotests: Reopen qcow2 with lazy-refcounts change qcow2: Support updating driver-specific options in reopen qcow2: Make qcow2_update_options() suitable for transactions qcow2: Fix memory leak in qcow2_update_options() error path qcow2: Leave s unchanged on qcow2_update_options() failure qcow2: Move rest of option handling to qcow2_update_options() qcow2: Move qcow2_update_options() call up qcow2: Factor out qcow2_update_options() qcow2: Improve error message qemu-io: Add command 'reopen' qemu-io: Remove duplicate 'open' error message block: Allow specifying driver-specific options to reopen qcow2: Rename BDRVQcowState to BDRVQcow2State block: Drop bdrv_find_whitelisted_format() block: Drop drv parameter from bdrv_fill_options() ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
007e620a75
150
block.c
150
block.c
|
@ -85,8 +85,7 @@ static QLIST_HEAD(, BlockDriver) bdrv_drivers =
|
||||||
static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename,
|
static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename,
|
||||||
const char *reference, QDict *options, int flags,
|
const char *reference, QDict *options, int flags,
|
||||||
BlockDriverState *parent,
|
BlockDriverState *parent,
|
||||||
const BdrvChildRole *child_role,
|
const BdrvChildRole *child_role, Error **errp);
|
||||||
BlockDriver *drv, Error **errp);
|
|
||||||
|
|
||||||
static void bdrv_dirty_bitmap_truncate(BlockDriverState *bs);
|
static void bdrv_dirty_bitmap_truncate(BlockDriverState *bs);
|
||||||
/* If non-zero, use only whitelisted block drivers */
|
/* If non-zero, use only whitelisted block drivers */
|
||||||
|
@ -314,13 +313,6 @@ static int bdrv_is_whitelisted(BlockDriver *drv, bool read_only)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockDriver *bdrv_find_whitelisted_format(const char *format_name,
|
|
||||||
bool read_only)
|
|
||||||
{
|
|
||||||
BlockDriver *drv = bdrv_find_format(format_name);
|
|
||||||
return drv && bdrv_is_whitelisted(drv, read_only) ? drv : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct CreateCo {
|
typedef struct CreateCo {
|
||||||
BlockDriver *drv;
|
BlockDriver *drv;
|
||||||
char *filename;
|
char *filename;
|
||||||
|
@ -997,13 +989,13 @@ static QDict *parse_json_filename(const char *filename, Error **errp)
|
||||||
* block driver has been specified explicitly.
|
* block driver has been specified explicitly.
|
||||||
*/
|
*/
|
||||||
static int bdrv_fill_options(QDict **options, const char **pfilename,
|
static int bdrv_fill_options(QDict **options, const char **pfilename,
|
||||||
int *flags, BlockDriver *drv, Error **errp)
|
int *flags, Error **errp)
|
||||||
{
|
{
|
||||||
const char *filename = *pfilename;
|
const char *filename = *pfilename;
|
||||||
const char *drvname;
|
const char *drvname;
|
||||||
bool protocol = *flags & BDRV_O_PROTOCOL;
|
bool protocol = *flags & BDRV_O_PROTOCOL;
|
||||||
bool parse_filename = false;
|
bool parse_filename = false;
|
||||||
BlockDriver *tmp_drv;
|
BlockDriver *drv = NULL;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
|
|
||||||
/* Parse json: pseudo-protocol */
|
/* Parse json: pseudo-protocol */
|
||||||
|
@ -1022,15 +1014,15 @@ static int bdrv_fill_options(QDict **options, const char **pfilename,
|
||||||
}
|
}
|
||||||
|
|
||||||
drvname = qdict_get_try_str(*options, "driver");
|
drvname = qdict_get_try_str(*options, "driver");
|
||||||
|
if (drvname) {
|
||||||
/* If the user has explicitly specified the driver, this choice should
|
drv = bdrv_find_format(drvname);
|
||||||
* override the BDRV_O_PROTOCOL flag */
|
if (!drv) {
|
||||||
tmp_drv = drv;
|
error_setg(errp, "Unknown driver '%s'", drvname);
|
||||||
if (!tmp_drv && drvname) {
|
return -ENOENT;
|
||||||
tmp_drv = bdrv_find_format(drvname);
|
}
|
||||||
}
|
/* If the user has explicitly specified the driver, this choice should
|
||||||
if (tmp_drv) {
|
* override the BDRV_O_PROTOCOL flag */
|
||||||
protocol = tmp_drv->bdrv_file_open;
|
protocol = drv->bdrv_file_open;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (protocol) {
|
if (protocol) {
|
||||||
|
@ -1054,33 +1046,18 @@ static int bdrv_fill_options(QDict **options, const char **pfilename,
|
||||||
/* Find the right block driver */
|
/* Find the right block driver */
|
||||||
filename = qdict_get_try_str(*options, "filename");
|
filename = qdict_get_try_str(*options, "filename");
|
||||||
|
|
||||||
if (drv) {
|
if (!drvname && protocol) {
|
||||||
if (drvname) {
|
if (filename) {
|
||||||
error_setg(errp, "Driver specified twice");
|
drv = bdrv_find_protocol(filename, parse_filename, errp);
|
||||||
return -EINVAL;
|
if (!drv) {
|
||||||
}
|
|
||||||
drvname = drv->format_name;
|
|
||||||
qdict_put(*options, "driver", qstring_from_str(drvname));
|
|
||||||
} else {
|
|
||||||
if (!drvname && protocol) {
|
|
||||||
if (filename) {
|
|
||||||
drv = bdrv_find_protocol(filename, parse_filename, errp);
|
|
||||||
if (!drv) {
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
drvname = drv->format_name;
|
|
||||||
qdict_put(*options, "driver", qstring_from_str(drvname));
|
|
||||||
} else {
|
|
||||||
error_setg(errp, "Must specify either driver or file");
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
} else if (drvname) {
|
|
||||||
drv = bdrv_find_format(drvname);
|
drvname = drv->format_name;
|
||||||
if (!drv) {
|
qdict_put(*options, "driver", qstring_from_str(drvname));
|
||||||
error_setg(errp, "Unknown driver '%s'", drvname);
|
} else {
|
||||||
return -ENOENT;
|
error_setg(errp, "Must specify either driver or file");
|
||||||
}
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1227,8 +1204,7 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp)
|
||||||
assert(bs->backing_hd == NULL);
|
assert(bs->backing_hd == NULL);
|
||||||
ret = bdrv_open_inherit(&backing_hd,
|
ret = bdrv_open_inherit(&backing_hd,
|
||||||
*backing_filename ? backing_filename : NULL,
|
*backing_filename ? backing_filename : NULL,
|
||||||
NULL, options, 0, bs, &child_backing,
|
NULL, options, 0, bs, &child_backing, &local_err);
|
||||||
NULL, &local_err);
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
bdrv_unref(backing_hd);
|
bdrv_unref(backing_hd);
|
||||||
backing_hd = NULL;
|
backing_hd = NULL;
|
||||||
|
@ -1291,7 +1267,7 @@ BdrvChild *bdrv_open_child(const char *filename,
|
||||||
|
|
||||||
bs = NULL;
|
bs = NULL;
|
||||||
ret = bdrv_open_inherit(&bs, filename, reference, image_options, 0,
|
ret = bdrv_open_inherit(&bs, filename, reference, image_options, 0,
|
||||||
parent, child_role, NULL, errp);
|
parent, child_role, errp);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
@ -1385,11 +1361,13 @@ int bdrv_append_temp_snapshot(BlockDriverState *bs, int flags, Error **errp)
|
||||||
qstring_from_str("file"));
|
qstring_from_str("file"));
|
||||||
qdict_put(snapshot_options, "file.filename",
|
qdict_put(snapshot_options, "file.filename",
|
||||||
qstring_from_str(tmp_filename));
|
qstring_from_str(tmp_filename));
|
||||||
|
qdict_put(snapshot_options, "driver",
|
||||||
|
qstring_from_str("qcow2"));
|
||||||
|
|
||||||
bs_snapshot = bdrv_new();
|
bs_snapshot = bdrv_new();
|
||||||
|
|
||||||
ret = bdrv_open(&bs_snapshot, NULL, NULL, snapshot_options,
|
ret = bdrv_open(&bs_snapshot, NULL, NULL, snapshot_options,
|
||||||
flags, &bdrv_qcow2, &local_err);
|
flags, &local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -1420,11 +1398,11 @@ out:
|
||||||
static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename,
|
static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename,
|
||||||
const char *reference, QDict *options, int flags,
|
const char *reference, QDict *options, int flags,
|
||||||
BlockDriverState *parent,
|
BlockDriverState *parent,
|
||||||
const BdrvChildRole *child_role,
|
const BdrvChildRole *child_role, Error **errp)
|
||||||
BlockDriver *drv, Error **errp)
|
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
BlockDriverState *file = NULL, *bs;
|
BlockDriverState *file = NULL, *bs;
|
||||||
|
BlockDriver *drv = NULL;
|
||||||
const char *drvname;
|
const char *drvname;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
int snapshot_flags = 0;
|
int snapshot_flags = 0;
|
||||||
|
@ -1474,13 +1452,12 @@ static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename,
|
||||||
flags = child_role->inherit_flags(parent->open_flags);
|
flags = child_role->inherit_flags(parent->open_flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = bdrv_fill_options(&options, &filename, &flags, drv, &local_err);
|
ret = bdrv_fill_options(&options, &filename, &flags, &local_err);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Find the right image format driver */
|
/* Find the right image format driver */
|
||||||
drv = NULL;
|
|
||||||
drvname = qdict_get_try_str(options, "driver");
|
drvname = qdict_get_try_str(options, "driver");
|
||||||
if (drvname) {
|
if (drvname) {
|
||||||
drv = bdrv_find_format(drvname);
|
drv = bdrv_find_format(drvname);
|
||||||
|
@ -1635,11 +1612,10 @@ close_and_fail:
|
||||||
}
|
}
|
||||||
|
|
||||||
int bdrv_open(BlockDriverState **pbs, const char *filename,
|
int bdrv_open(BlockDriverState **pbs, const char *filename,
|
||||||
const char *reference, QDict *options, int flags,
|
const char *reference, QDict *options, int flags, Error **errp)
|
||||||
BlockDriver *drv, Error **errp)
|
|
||||||
{
|
{
|
||||||
return bdrv_open_inherit(pbs, filename, reference, options, flags, NULL,
|
return bdrv_open_inherit(pbs, filename, reference, options, flags, NULL,
|
||||||
NULL, drv, errp);
|
NULL, errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct BlockReopenQueueEntry {
|
typedef struct BlockReopenQueueEntry {
|
||||||
|
@ -1660,6 +1636,9 @@ typedef struct BlockReopenQueueEntry {
|
||||||
*
|
*
|
||||||
* bs is the BlockDriverState to add to the reopen queue.
|
* bs is the BlockDriverState to add to the reopen queue.
|
||||||
*
|
*
|
||||||
|
* options contains the changed options for the associated bs
|
||||||
|
* (the BlockReopenQueue takes ownership)
|
||||||
|
*
|
||||||
* flags contains the open flags for the associated bs
|
* flags contains the open flags for the associated bs
|
||||||
*
|
*
|
||||||
* returns a pointer to bs_queue, which is either the newly allocated
|
* returns a pointer to bs_queue, which is either the newly allocated
|
||||||
|
@ -1667,18 +1646,28 @@ typedef struct BlockReopenQueueEntry {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
|
BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
|
||||||
BlockDriverState *bs, int flags)
|
BlockDriverState *bs,
|
||||||
|
QDict *options, int flags)
|
||||||
{
|
{
|
||||||
assert(bs != NULL);
|
assert(bs != NULL);
|
||||||
|
|
||||||
BlockReopenQueueEntry *bs_entry;
|
BlockReopenQueueEntry *bs_entry;
|
||||||
BdrvChild *child;
|
BdrvChild *child;
|
||||||
|
QDict *old_options;
|
||||||
|
|
||||||
if (bs_queue == NULL) {
|
if (bs_queue == NULL) {
|
||||||
bs_queue = g_new0(BlockReopenQueue, 1);
|
bs_queue = g_new0(BlockReopenQueue, 1);
|
||||||
QSIMPLEQ_INIT(bs_queue);
|
QSIMPLEQ_INIT(bs_queue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!options) {
|
||||||
|
options = qdict_new();
|
||||||
|
}
|
||||||
|
|
||||||
|
old_options = qdict_clone_shallow(bs->options);
|
||||||
|
qdict_join(options, old_options, false);
|
||||||
|
QDECREF(old_options);
|
||||||
|
|
||||||
/* bdrv_open() masks this flag out */
|
/* bdrv_open() masks this flag out */
|
||||||
flags &= ~BDRV_O_PROTOCOL;
|
flags &= ~BDRV_O_PROTOCOL;
|
||||||
|
|
||||||
|
@ -1690,13 +1679,15 @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
|
||||||
}
|
}
|
||||||
|
|
||||||
child_flags = child->role->inherit_flags(flags);
|
child_flags = child->role->inherit_flags(flags);
|
||||||
bdrv_reopen_queue(bs_queue, child->bs, child_flags);
|
/* TODO Pass down child flags (backing.*, extents.*, ...) */
|
||||||
|
bdrv_reopen_queue(bs_queue, child->bs, NULL, child_flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
bs_entry = g_new0(BlockReopenQueueEntry, 1);
|
bs_entry = g_new0(BlockReopenQueueEntry, 1);
|
||||||
QSIMPLEQ_INSERT_TAIL(bs_queue, bs_entry, entry);
|
QSIMPLEQ_INSERT_TAIL(bs_queue, bs_entry, entry);
|
||||||
|
|
||||||
bs_entry->state.bs = bs;
|
bs_entry->state.bs = bs;
|
||||||
|
bs_entry->state.options = options;
|
||||||
bs_entry->state.flags = flags;
|
bs_entry->state.flags = flags;
|
||||||
|
|
||||||
return bs_queue;
|
return bs_queue;
|
||||||
|
@ -1749,6 +1740,7 @@ cleanup:
|
||||||
if (ret && bs_entry->prepared) {
|
if (ret && bs_entry->prepared) {
|
||||||
bdrv_reopen_abort(&bs_entry->state);
|
bdrv_reopen_abort(&bs_entry->state);
|
||||||
}
|
}
|
||||||
|
QDECREF(bs_entry->state.options);
|
||||||
g_free(bs_entry);
|
g_free(bs_entry);
|
||||||
}
|
}
|
||||||
g_free(bs_queue);
|
g_free(bs_queue);
|
||||||
|
@ -1761,7 +1753,7 @@ int bdrv_reopen(BlockDriverState *bs, int bdrv_flags, Error **errp)
|
||||||
{
|
{
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
BlockReopenQueue *queue = bdrv_reopen_queue(NULL, bs, bdrv_flags);
|
BlockReopenQueue *queue = bdrv_reopen_queue(NULL, bs, NULL, bdrv_flags);
|
||||||
|
|
||||||
ret = bdrv_reopen_multiple(queue, &local_err);
|
ret = bdrv_reopen_multiple(queue, &local_err);
|
||||||
if (local_err != NULL) {
|
if (local_err != NULL) {
|
||||||
|
@ -1837,6 +1829,26 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Options that are not handled are only okay if they are unchanged
|
||||||
|
* compared to the old state. It is expected that some options are only
|
||||||
|
* used for the initial open, but not reopen (e.g. filename) */
|
||||||
|
if (qdict_size(reopen_state->options)) {
|
||||||
|
const QDictEntry *entry = qdict_first(reopen_state->options);
|
||||||
|
|
||||||
|
do {
|
||||||
|
QString *new_obj = qobject_to_qstring(entry->value);
|
||||||
|
const char *new = qstring_get_str(new_obj);
|
||||||
|
const char *old = qdict_get_try_str(reopen_state->bs->options,
|
||||||
|
entry->key);
|
||||||
|
|
||||||
|
if (!old || strcmp(new, old)) {
|
||||||
|
error_setg(errp, "Cannot change the option '%s'", entry->key);
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
} while ((entry = qdict_next(reopen_state->options, entry)));
|
||||||
|
}
|
||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
|
@ -3739,7 +3751,6 @@ void bdrv_img_create(const char *filename, const char *fmt,
|
||||||
const char *backing_fmt, *backing_file;
|
const char *backing_fmt, *backing_file;
|
||||||
int64_t size;
|
int64_t size;
|
||||||
BlockDriver *drv, *proto_drv;
|
BlockDriver *drv, *proto_drv;
|
||||||
BlockDriver *backing_drv = NULL;
|
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
|
@ -3813,14 +3824,6 @@ void bdrv_img_create(const char *filename, const char *fmt,
|
||||||
}
|
}
|
||||||
|
|
||||||
backing_fmt = qemu_opt_get(opts, BLOCK_OPT_BACKING_FMT);
|
backing_fmt = qemu_opt_get(opts, BLOCK_OPT_BACKING_FMT);
|
||||||
if (backing_fmt) {
|
|
||||||
backing_drv = bdrv_find_format(backing_fmt);
|
|
||||||
if (!backing_drv) {
|
|
||||||
error_setg(errp, "Unknown backing file format '%s'",
|
|
||||||
backing_fmt);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The size for the image must always be specified, with one exception:
|
// The size for the image must always be specified, with one exception:
|
||||||
// If we are using a backing file, we can obtain the size from there
|
// If we are using a backing file, we can obtain the size from there
|
||||||
|
@ -3831,6 +3834,7 @@ void bdrv_img_create(const char *filename, const char *fmt,
|
||||||
char *full_backing = g_new0(char, PATH_MAX);
|
char *full_backing = g_new0(char, PATH_MAX);
|
||||||
int64_t size;
|
int64_t size;
|
||||||
int back_flags;
|
int back_flags;
|
||||||
|
QDict *backing_options = NULL;
|
||||||
|
|
||||||
bdrv_get_full_backing_filename_from_filename(filename, backing_file,
|
bdrv_get_full_backing_filename_from_filename(filename, backing_file,
|
||||||
full_backing, PATH_MAX,
|
full_backing, PATH_MAX,
|
||||||
|
@ -3844,9 +3848,15 @@ void bdrv_img_create(const char *filename, const char *fmt,
|
||||||
back_flags =
|
back_flags =
|
||||||
flags & ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING);
|
flags & ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING);
|
||||||
|
|
||||||
|
if (backing_fmt) {
|
||||||
|
backing_options = qdict_new();
|
||||||
|
qdict_put(backing_options, "driver",
|
||||||
|
qstring_from_str(backing_fmt));
|
||||||
|
}
|
||||||
|
|
||||||
bs = NULL;
|
bs = NULL;
|
||||||
ret = bdrv_open(&bs, full_backing, NULL, NULL, back_flags,
|
ret = bdrv_open(&bs, full_backing, NULL, backing_options,
|
||||||
backing_drv, &local_err);
|
back_flags, &local_err);
|
||||||
g_free(full_backing);
|
g_free(full_backing);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto out;
|
goto out;
|
||||||
|
|
|
@ -126,7 +126,7 @@ BlockBackend *blk_new_open(const char *name, const char *filename,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = bdrv_open(&blk->bs, filename, reference, options, flags, NULL, errp);
|
ret = bdrv_open(&blk->bs, filename, reference, options, flags, errp);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
blk_unref(blk);
|
blk_unref(blk);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
|
@ -236,11 +236,11 @@ void commit_start(BlockDriverState *bs, BlockDriverState *base,
|
||||||
|
|
||||||
/* convert base & overlay_bs to r/w, if necessary */
|
/* convert base & overlay_bs to r/w, if necessary */
|
||||||
if (!(orig_base_flags & BDRV_O_RDWR)) {
|
if (!(orig_base_flags & BDRV_O_RDWR)) {
|
||||||
reopen_queue = bdrv_reopen_queue(reopen_queue, base,
|
reopen_queue = bdrv_reopen_queue(reopen_queue, base, NULL,
|
||||||
orig_base_flags | BDRV_O_RDWR);
|
orig_base_flags | BDRV_O_RDWR);
|
||||||
}
|
}
|
||||||
if (!(orig_overlay_flags & BDRV_O_RDWR)) {
|
if (!(orig_overlay_flags & BDRV_O_RDWR)) {
|
||||||
reopen_queue = bdrv_reopen_queue(reopen_queue, overlay_bs,
|
reopen_queue = bdrv_reopen_queue(reopen_queue, overlay_bs, NULL,
|
||||||
orig_overlay_flags | BDRV_O_RDWR);
|
orig_overlay_flags | BDRV_O_RDWR);
|
||||||
}
|
}
|
||||||
if (reopen_queue) {
|
if (reopen_queue) {
|
||||||
|
|
|
@ -476,7 +476,7 @@ static int parallels_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||||
|
|
||||||
file = NULL;
|
file = NULL;
|
||||||
ret = bdrv_open(&file, filename, NULL, NULL,
|
ret = bdrv_open(&file, filename, NULL, NULL,
|
||||||
BDRV_O_RDWR | BDRV_O_PROTOCOL, NULL, &local_err);
|
BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -793,7 +793,7 @@ static int qcow_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||||
|
|
||||||
qcow_bs = NULL;
|
qcow_bs = NULL;
|
||||||
ret = bdrv_open(&qcow_bs, filename, NULL, NULL,
|
ret = bdrv_open(&qcow_bs, filename, NULL, NULL,
|
||||||
BDRV_O_RDWR | BDRV_O_PROTOCOL, NULL, &local_err);
|
BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
|
@ -55,14 +55,14 @@ struct Qcow2Cache {
|
||||||
static inline void *qcow2_cache_get_table_addr(BlockDriverState *bs,
|
static inline void *qcow2_cache_get_table_addr(BlockDriverState *bs,
|
||||||
Qcow2Cache *c, int table)
|
Qcow2Cache *c, int table)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
return (uint8_t *) c->table_array + (size_t) table * s->cluster_size;
|
return (uint8_t *) c->table_array + (size_t) table * s->cluster_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int qcow2_cache_get_table_idx(BlockDriverState *bs,
|
static inline int qcow2_cache_get_table_idx(BlockDriverState *bs,
|
||||||
Qcow2Cache *c, void *table)
|
Qcow2Cache *c, void *table)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
ptrdiff_t table_offset = (uint8_t *) table - (uint8_t *) c->table_array;
|
ptrdiff_t table_offset = (uint8_t *) table - (uint8_t *) c->table_array;
|
||||||
int idx = table_offset / s->cluster_size;
|
int idx = table_offset / s->cluster_size;
|
||||||
assert(idx >= 0 && idx < c->size && table_offset % s->cluster_size == 0);
|
assert(idx >= 0 && idx < c->size && table_offset % s->cluster_size == 0);
|
||||||
|
@ -73,7 +73,7 @@ static void qcow2_cache_table_release(BlockDriverState *bs, Qcow2Cache *c,
|
||||||
int i, int num_tables)
|
int i, int num_tables)
|
||||||
{
|
{
|
||||||
#if QEMU_MADV_DONTNEED != QEMU_MADV_INVALID
|
#if QEMU_MADV_DONTNEED != QEMU_MADV_INVALID
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
void *t = qcow2_cache_get_table_addr(bs, c, i);
|
void *t = qcow2_cache_get_table_addr(bs, c, i);
|
||||||
int align = getpagesize();
|
int align = getpagesize();
|
||||||
size_t mem_size = (size_t) s->cluster_size * num_tables;
|
size_t mem_size = (size_t) s->cluster_size * num_tables;
|
||||||
|
@ -121,7 +121,7 @@ void qcow2_cache_clean_unused(BlockDriverState *bs, Qcow2Cache *c)
|
||||||
|
|
||||||
Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables)
|
Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
Qcow2Cache *c;
|
Qcow2Cache *c;
|
||||||
|
|
||||||
c = g_new0(Qcow2Cache, 1);
|
c = g_new0(Qcow2Cache, 1);
|
||||||
|
@ -172,7 +172,7 @@ static int qcow2_cache_flush_dependency(BlockDriverState *bs, Qcow2Cache *c)
|
||||||
|
|
||||||
static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i)
|
static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
if (!c->entries[i].dirty || !c->entries[i].offset) {
|
if (!c->entries[i].dirty || !c->entries[i].offset) {
|
||||||
|
@ -229,7 +229,7 @@ static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i)
|
||||||
|
|
||||||
int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c)
|
int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
int result = 0;
|
int result = 0;
|
||||||
int ret;
|
int ret;
|
||||||
int i;
|
int i;
|
||||||
|
@ -306,7 +306,7 @@ int qcow2_cache_empty(BlockDriverState *bs, Qcow2Cache *c)
|
||||||
static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c,
|
static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c,
|
||||||
uint64_t offset, void **table, bool read_from_disk)
|
uint64_t offset, void **table, bool read_from_disk)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
int i;
|
int i;
|
||||||
int ret;
|
int ret;
|
||||||
int lookup_index;
|
int lookup_index;
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
|
int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
|
||||||
bool exact_size)
|
bool exact_size)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
int new_l1_size2, ret, i;
|
int new_l1_size2, ret, i;
|
||||||
uint64_t *new_l1_table;
|
uint64_t *new_l1_table;
|
||||||
int64_t old_l1_table_offset, old_l1_size;
|
int64_t old_l1_table_offset, old_l1_size;
|
||||||
|
@ -148,7 +148,7 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
|
||||||
static int l2_load(BlockDriverState *bs, uint64_t l2_offset,
|
static int l2_load(BlockDriverState *bs, uint64_t l2_offset,
|
||||||
uint64_t **l2_table)
|
uint64_t **l2_table)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset, (void**) l2_table);
|
ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset, (void**) l2_table);
|
||||||
|
@ -163,7 +163,7 @@ static int l2_load(BlockDriverState *bs, uint64_t l2_offset,
|
||||||
#define L1_ENTRIES_PER_SECTOR (512 / 8)
|
#define L1_ENTRIES_PER_SECTOR (512 / 8)
|
||||||
int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index)
|
int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
uint64_t buf[L1_ENTRIES_PER_SECTOR] = { 0 };
|
uint64_t buf[L1_ENTRIES_PER_SECTOR] = { 0 };
|
||||||
int l1_start_index;
|
int l1_start_index;
|
||||||
int i, ret;
|
int i, ret;
|
||||||
|
@ -203,7 +203,7 @@ int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index)
|
||||||
|
|
||||||
static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table)
|
static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
uint64_t old_l2_offset;
|
uint64_t old_l2_offset;
|
||||||
uint64_t *l2_table = NULL;
|
uint64_t *l2_table = NULL;
|
||||||
int64_t l2_offset;
|
int64_t l2_offset;
|
||||||
|
@ -298,7 +298,7 @@ fail:
|
||||||
* as contiguous. (This allows it, for example, to stop at the first compressed
|
* as contiguous. (This allows it, for example, to stop at the first compressed
|
||||||
* cluster which may require a different handling)
|
* cluster which may require a different handling)
|
||||||
*/
|
*/
|
||||||
static int count_contiguous_clusters(uint64_t nb_clusters, int cluster_size,
|
static int count_contiguous_clusters(int nb_clusters, int cluster_size,
|
||||||
uint64_t *l2_table, uint64_t stop_flags)
|
uint64_t *l2_table, uint64_t stop_flags)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
@ -321,7 +321,7 @@ static int count_contiguous_clusters(uint64_t nb_clusters, int cluster_size,
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int count_contiguous_free_clusters(uint64_t nb_clusters, uint64_t *l2_table)
|
static int count_contiguous_free_clusters(int nb_clusters, uint64_t *l2_table)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
@ -339,7 +339,7 @@ static int count_contiguous_free_clusters(uint64_t nb_clusters, uint64_t *l2_tab
|
||||||
/* The crypt function is compatible with the linux cryptoloop
|
/* The crypt function is compatible with the linux cryptoloop
|
||||||
algorithm for < 4 GB images. NOTE: out_buf == in_buf is
|
algorithm for < 4 GB images. NOTE: out_buf == in_buf is
|
||||||
supported */
|
supported */
|
||||||
int qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
|
int qcow2_encrypt_sectors(BDRVQcow2State *s, int64_t sector_num,
|
||||||
uint8_t *out_buf, const uint8_t *in_buf,
|
uint8_t *out_buf, const uint8_t *in_buf,
|
||||||
int nb_sectors, bool enc,
|
int nb_sectors, bool enc,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
|
@ -387,7 +387,7 @@ static int coroutine_fn copy_sectors(BlockDriverState *bs,
|
||||||
uint64_t cluster_offset,
|
uint64_t cluster_offset,
|
||||||
int n_start, int n_end)
|
int n_start, int n_end)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
QEMUIOVector qiov;
|
QEMUIOVector qiov;
|
||||||
struct iovec iov;
|
struct iovec iov;
|
||||||
int n, ret;
|
int n, ret;
|
||||||
|
@ -469,7 +469,7 @@ out:
|
||||||
int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
||||||
int *num, uint64_t *cluster_offset)
|
int *num, uint64_t *cluster_offset)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
unsigned int l2_index;
|
unsigned int l2_index;
|
||||||
uint64_t l1_index, l2_offset, *l2_table;
|
uint64_t l1_index, l2_offset, *l2_table;
|
||||||
int l1_bits, c;
|
int l1_bits, c;
|
||||||
|
@ -495,6 +495,7 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
||||||
if (nb_needed > nb_available) {
|
if (nb_needed > nb_available) {
|
||||||
nb_needed = nb_available;
|
nb_needed = nb_available;
|
||||||
}
|
}
|
||||||
|
assert(nb_needed <= INT_MAX);
|
||||||
|
|
||||||
*cluster_offset = 0;
|
*cluster_offset = 0;
|
||||||
|
|
||||||
|
@ -530,6 +531,8 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
||||||
|
|
||||||
l2_index = (offset >> s->cluster_bits) & (s->l2_size - 1);
|
l2_index = (offset >> s->cluster_bits) & (s->l2_size - 1);
|
||||||
*cluster_offset = be64_to_cpu(l2_table[l2_index]);
|
*cluster_offset = be64_to_cpu(l2_table[l2_index]);
|
||||||
|
|
||||||
|
/* nb_needed <= INT_MAX, thus nb_clusters <= INT_MAX, too */
|
||||||
nb_clusters = size_to_clusters(s, nb_needed << 9);
|
nb_clusters = size_to_clusters(s, nb_needed << 9);
|
||||||
|
|
||||||
ret = qcow2_get_cluster_type(*cluster_offset);
|
ret = qcow2_get_cluster_type(*cluster_offset);
|
||||||
|
@ -606,7 +609,7 @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset,
|
||||||
uint64_t **new_l2_table,
|
uint64_t **new_l2_table,
|
||||||
int *new_l2_index)
|
int *new_l2_index)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
unsigned int l2_index;
|
unsigned int l2_index;
|
||||||
uint64_t l1_index, l2_offset;
|
uint64_t l1_index, l2_offset;
|
||||||
uint64_t *l2_table = NULL;
|
uint64_t *l2_table = NULL;
|
||||||
|
@ -680,7 +683,7 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
|
||||||
uint64_t offset,
|
uint64_t offset,
|
||||||
int compressed_size)
|
int compressed_size)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
int l2_index, ret;
|
int l2_index, ret;
|
||||||
uint64_t *l2_table;
|
uint64_t *l2_table;
|
||||||
int64_t cluster_offset;
|
int64_t cluster_offset;
|
||||||
|
@ -725,7 +728,7 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
|
||||||
|
|
||||||
static int perform_cow(BlockDriverState *bs, QCowL2Meta *m, Qcow2COWRegion *r)
|
static int perform_cow(BlockDriverState *bs, QCowL2Meta *m, Qcow2COWRegion *r)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (r->nb_sectors == 0) {
|
if (r->nb_sectors == 0) {
|
||||||
|
@ -754,7 +757,7 @@ static int perform_cow(BlockDriverState *bs, QCowL2Meta *m, Qcow2COWRegion *r)
|
||||||
|
|
||||||
int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
|
int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
int i, j = 0, l2_index, ret;
|
int i, j = 0, l2_index, ret;
|
||||||
uint64_t *old_cluster, *l2_table;
|
uint64_t *old_cluster, *l2_table;
|
||||||
uint64_t cluster_offset = m->alloc_offset;
|
uint64_t cluster_offset = m->alloc_offset;
|
||||||
|
@ -837,7 +840,7 @@ err:
|
||||||
* write, but require COW to be performed (this includes yet unallocated space,
|
* write, but require COW to be performed (this includes yet unallocated space,
|
||||||
* which must copy from the backing file)
|
* which must copy from the backing file)
|
||||||
*/
|
*/
|
||||||
static int count_cow_clusters(BDRVQcowState *s, int nb_clusters,
|
static int count_cow_clusters(BDRVQcow2State *s, int nb_clusters,
|
||||||
uint64_t *l2_table, int l2_index)
|
uint64_t *l2_table, int l2_index)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
@ -883,7 +886,7 @@ out:
|
||||||
static int handle_dependencies(BlockDriverState *bs, uint64_t guest_offset,
|
static int handle_dependencies(BlockDriverState *bs, uint64_t guest_offset,
|
||||||
uint64_t *cur_bytes, QCowL2Meta **m)
|
uint64_t *cur_bytes, QCowL2Meta **m)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
QCowL2Meta *old_alloc;
|
QCowL2Meta *old_alloc;
|
||||||
uint64_t bytes = *cur_bytes;
|
uint64_t bytes = *cur_bytes;
|
||||||
|
|
||||||
|
@ -956,11 +959,11 @@ static int handle_dependencies(BlockDriverState *bs, uint64_t guest_offset,
|
||||||
static int handle_copied(BlockDriverState *bs, uint64_t guest_offset,
|
static int handle_copied(BlockDriverState *bs, uint64_t guest_offset,
|
||||||
uint64_t *host_offset, uint64_t *bytes, QCowL2Meta **m)
|
uint64_t *host_offset, uint64_t *bytes, QCowL2Meta **m)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
int l2_index;
|
int l2_index;
|
||||||
uint64_t cluster_offset;
|
uint64_t cluster_offset;
|
||||||
uint64_t *l2_table;
|
uint64_t *l2_table;
|
||||||
unsigned int nb_clusters;
|
uint64_t nb_clusters;
|
||||||
unsigned int keep_clusters;
|
unsigned int keep_clusters;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -979,6 +982,7 @@ static int handle_copied(BlockDriverState *bs, uint64_t guest_offset,
|
||||||
|
|
||||||
l2_index = offset_to_l2_index(s, guest_offset);
|
l2_index = offset_to_l2_index(s, guest_offset);
|
||||||
nb_clusters = MIN(nb_clusters, s->l2_size - l2_index);
|
nb_clusters = MIN(nb_clusters, s->l2_size - l2_index);
|
||||||
|
assert(nb_clusters <= INT_MAX);
|
||||||
|
|
||||||
/* Find L2 entry for the first involved cluster */
|
/* Find L2 entry for the first involved cluster */
|
||||||
ret = get_cluster_table(bs, guest_offset, &l2_table, &l2_index);
|
ret = get_cluster_table(bs, guest_offset, &l2_table, &l2_index);
|
||||||
|
@ -1061,9 +1065,9 @@ out:
|
||||||
* restarted, but the whole request should not be failed.
|
* restarted, but the whole request should not be failed.
|
||||||
*/
|
*/
|
||||||
static int do_alloc_cluster_offset(BlockDriverState *bs, uint64_t guest_offset,
|
static int do_alloc_cluster_offset(BlockDriverState *bs, uint64_t guest_offset,
|
||||||
uint64_t *host_offset, unsigned int *nb_clusters)
|
uint64_t *host_offset, uint64_t *nb_clusters)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
|
|
||||||
trace_qcow2_do_alloc_clusters_offset(qemu_coroutine_self(), guest_offset,
|
trace_qcow2_do_alloc_clusters_offset(qemu_coroutine_self(), guest_offset,
|
||||||
*host_offset, *nb_clusters);
|
*host_offset, *nb_clusters);
|
||||||
|
@ -1079,7 +1083,7 @@ static int do_alloc_cluster_offset(BlockDriverState *bs, uint64_t guest_offset,
|
||||||
*host_offset = cluster_offset;
|
*host_offset = cluster_offset;
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
int ret = qcow2_alloc_clusters_at(bs, *host_offset, *nb_clusters);
|
int64_t ret = qcow2_alloc_clusters_at(bs, *host_offset, *nb_clusters);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -1111,11 +1115,11 @@ static int do_alloc_cluster_offset(BlockDriverState *bs, uint64_t guest_offset,
|
||||||
static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset,
|
static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset,
|
||||||
uint64_t *host_offset, uint64_t *bytes, QCowL2Meta **m)
|
uint64_t *host_offset, uint64_t *bytes, QCowL2Meta **m)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
int l2_index;
|
int l2_index;
|
||||||
uint64_t *l2_table;
|
uint64_t *l2_table;
|
||||||
uint64_t entry;
|
uint64_t entry;
|
||||||
unsigned int nb_clusters;
|
uint64_t nb_clusters;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
uint64_t alloc_cluster_offset;
|
uint64_t alloc_cluster_offset;
|
||||||
|
@ -1133,6 +1137,7 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset,
|
||||||
|
|
||||||
l2_index = offset_to_l2_index(s, guest_offset);
|
l2_index = offset_to_l2_index(s, guest_offset);
|
||||||
nb_clusters = MIN(nb_clusters, s->l2_size - l2_index);
|
nb_clusters = MIN(nb_clusters, s->l2_size - l2_index);
|
||||||
|
assert(nb_clusters <= INT_MAX);
|
||||||
|
|
||||||
/* Find L2 entry for the first involved cluster */
|
/* Find L2 entry for the first involved cluster */
|
||||||
ret = get_cluster_table(bs, guest_offset, &l2_table, &l2_index);
|
ret = get_cluster_table(bs, guest_offset, &l2_table, &l2_index);
|
||||||
|
@ -1263,7 +1268,7 @@ fail:
|
||||||
int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
||||||
int *num, uint64_t *host_offset, QCowL2Meta **m)
|
int *num, uint64_t *host_offset, QCowL2Meta **m)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
uint64_t start, remaining;
|
uint64_t start, remaining;
|
||||||
uint64_t cluster_offset;
|
uint64_t cluster_offset;
|
||||||
uint64_t cur_bytes;
|
uint64_t cur_bytes;
|
||||||
|
@ -1397,7 +1402,7 @@ static int decompress_buffer(uint8_t *out_buf, int out_buf_size,
|
||||||
|
|
||||||
int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset)
|
int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
int ret, csize, nb_csectors, sector_offset;
|
int ret, csize, nb_csectors, sector_offset;
|
||||||
uint64_t coffset;
|
uint64_t coffset;
|
||||||
|
|
||||||
|
@ -1426,9 +1431,10 @@ int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset)
|
||||||
* clusters.
|
* clusters.
|
||||||
*/
|
*/
|
||||||
static int discard_single_l2(BlockDriverState *bs, uint64_t offset,
|
static int discard_single_l2(BlockDriverState *bs, uint64_t offset,
|
||||||
unsigned int nb_clusters, enum qcow2_discard_type type, bool full_discard)
|
uint64_t nb_clusters, enum qcow2_discard_type type,
|
||||||
|
bool full_discard)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
uint64_t *l2_table;
|
uint64_t *l2_table;
|
||||||
int l2_index;
|
int l2_index;
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -1441,6 +1447,7 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset,
|
||||||
|
|
||||||
/* Limit nb_clusters to one L2 table */
|
/* Limit nb_clusters to one L2 table */
|
||||||
nb_clusters = MIN(nb_clusters, s->l2_size - l2_index);
|
nb_clusters = MIN(nb_clusters, s->l2_size - l2_index);
|
||||||
|
assert(nb_clusters <= INT_MAX);
|
||||||
|
|
||||||
for (i = 0; i < nb_clusters; i++) {
|
for (i = 0; i < nb_clusters; i++) {
|
||||||
uint64_t old_l2_entry;
|
uint64_t old_l2_entry;
|
||||||
|
@ -1501,9 +1508,9 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset,
|
||||||
int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset,
|
int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset,
|
||||||
int nb_sectors, enum qcow2_discard_type type, bool full_discard)
|
int nb_sectors, enum qcow2_discard_type type, bool full_discard)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
uint64_t end_offset;
|
uint64_t end_offset;
|
||||||
unsigned int nb_clusters;
|
uint64_t nb_clusters;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
end_offset = offset + (nb_sectors << BDRV_SECTOR_BITS);
|
end_offset = offset + (nb_sectors << BDRV_SECTOR_BITS);
|
||||||
|
@ -1545,9 +1552,9 @@ fail:
|
||||||
* clusters.
|
* clusters.
|
||||||
*/
|
*/
|
||||||
static int zero_single_l2(BlockDriverState *bs, uint64_t offset,
|
static int zero_single_l2(BlockDriverState *bs, uint64_t offset,
|
||||||
unsigned int nb_clusters)
|
uint64_t nb_clusters)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
uint64_t *l2_table;
|
uint64_t *l2_table;
|
||||||
int l2_index;
|
int l2_index;
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -1560,6 +1567,7 @@ static int zero_single_l2(BlockDriverState *bs, uint64_t offset,
|
||||||
|
|
||||||
/* Limit nb_clusters to one L2 table */
|
/* Limit nb_clusters to one L2 table */
|
||||||
nb_clusters = MIN(nb_clusters, s->l2_size - l2_index);
|
nb_clusters = MIN(nb_clusters, s->l2_size - l2_index);
|
||||||
|
assert(nb_clusters <= INT_MAX);
|
||||||
|
|
||||||
for (i = 0; i < nb_clusters; i++) {
|
for (i = 0; i < nb_clusters; i++) {
|
||||||
uint64_t old_offset;
|
uint64_t old_offset;
|
||||||
|
@ -1583,8 +1591,8 @@ static int zero_single_l2(BlockDriverState *bs, uint64_t offset,
|
||||||
|
|
||||||
int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors)
|
int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
unsigned int nb_clusters;
|
uint64_t nb_clusters;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* The zero flag is only supported by version 3 and newer */
|
/* The zero flag is only supported by version 3 and newer */
|
||||||
|
@ -1628,7 +1636,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
|
||||||
int64_t l1_entries,
|
int64_t l1_entries,
|
||||||
BlockDriverAmendStatusCB *status_cb)
|
BlockDriverAmendStatusCB *status_cb)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
bool is_active_l1 = (l1_table == s->l1_table);
|
bool is_active_l1 = (l1_table == s->l1_table);
|
||||||
uint64_t *l2_table = NULL;
|
uint64_t *l2_table = NULL;
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -1815,7 +1823,7 @@ fail:
|
||||||
int qcow2_expand_zero_clusters(BlockDriverState *bs,
|
int qcow2_expand_zero_clusters(BlockDriverState *bs,
|
||||||
BlockDriverAmendStatusCB *status_cb)
|
BlockDriverAmendStatusCB *status_cb)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
uint64_t *l1_table = NULL;
|
uint64_t *l1_table = NULL;
|
||||||
int64_t l1_entries = 0, visited_l1_entries = 0;
|
int64_t l1_entries = 0, visited_l1_entries = 0;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
|
@ -82,7 +82,7 @@ static Qcow2SetRefcountFunc *const set_refcount_funcs[] = {
|
||||||
|
|
||||||
int qcow2_refcount_init(BlockDriverState *bs)
|
int qcow2_refcount_init(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
unsigned int refcount_table_size2, i;
|
unsigned int refcount_table_size2, i;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -116,7 +116,7 @@ int qcow2_refcount_init(BlockDriverState *bs)
|
||||||
|
|
||||||
void qcow2_refcount_close(BlockDriverState *bs)
|
void qcow2_refcount_close(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
g_free(s->refcount_table);
|
g_free(s->refcount_table);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,7 +214,7 @@ static int load_refcount_block(BlockDriverState *bs,
|
||||||
int64_t refcount_block_offset,
|
int64_t refcount_block_offset,
|
||||||
void **refcount_block)
|
void **refcount_block)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_LOAD);
|
BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_LOAD);
|
||||||
|
@ -231,7 +231,7 @@ static int load_refcount_block(BlockDriverState *bs,
|
||||||
int qcow2_get_refcount(BlockDriverState *bs, int64_t cluster_index,
|
int qcow2_get_refcount(BlockDriverState *bs, int64_t cluster_index,
|
||||||
uint64_t *refcount)
|
uint64_t *refcount)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
uint64_t refcount_table_index, block_index;
|
uint64_t refcount_table_index, block_index;
|
||||||
int64_t refcount_block_offset;
|
int64_t refcount_block_offset;
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -274,7 +274,7 @@ int qcow2_get_refcount(BlockDriverState *bs, int64_t cluster_index,
|
||||||
* Rounds the refcount table size up to avoid growing the table for each single
|
* Rounds the refcount table size up to avoid growing the table for each single
|
||||||
* refcount block that is allocated.
|
* refcount block that is allocated.
|
||||||
*/
|
*/
|
||||||
static unsigned int next_refcount_table_size(BDRVQcowState *s,
|
static unsigned int next_refcount_table_size(BDRVQcow2State *s,
|
||||||
unsigned int min_size)
|
unsigned int min_size)
|
||||||
{
|
{
|
||||||
unsigned int min_clusters = (min_size >> (s->cluster_bits - 3)) + 1;
|
unsigned int min_clusters = (min_size >> (s->cluster_bits - 3)) + 1;
|
||||||
|
@ -290,7 +290,7 @@ static unsigned int next_refcount_table_size(BDRVQcowState *s,
|
||||||
|
|
||||||
|
|
||||||
/* Checks if two offsets are described by the same refcount block */
|
/* Checks if two offsets are described by the same refcount block */
|
||||||
static int in_same_refcount_block(BDRVQcowState *s, uint64_t offset_a,
|
static int in_same_refcount_block(BDRVQcow2State *s, uint64_t offset_a,
|
||||||
uint64_t offset_b)
|
uint64_t offset_b)
|
||||||
{
|
{
|
||||||
uint64_t block_a = offset_a >> (s->cluster_bits + s->refcount_block_bits);
|
uint64_t block_a = offset_a >> (s->cluster_bits + s->refcount_block_bits);
|
||||||
|
@ -308,7 +308,7 @@ static int in_same_refcount_block(BDRVQcowState *s, uint64_t offset_a,
|
||||||
static int alloc_refcount_block(BlockDriverState *bs,
|
static int alloc_refcount_block(BlockDriverState *bs,
|
||||||
int64_t cluster_index, void **refcount_block)
|
int64_t cluster_index, void **refcount_block)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
unsigned int refcount_table_index;
|
unsigned int refcount_table_index;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -605,7 +605,7 @@ fail_block:
|
||||||
|
|
||||||
void qcow2_process_discards(BlockDriverState *bs, int ret)
|
void qcow2_process_discards(BlockDriverState *bs, int ret)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
Qcow2DiscardRegion *d, *next;
|
Qcow2DiscardRegion *d, *next;
|
||||||
|
|
||||||
QTAILQ_FOREACH_SAFE(d, &s->discards, next, next) {
|
QTAILQ_FOREACH_SAFE(d, &s->discards, next, next) {
|
||||||
|
@ -625,7 +625,7 @@ void qcow2_process_discards(BlockDriverState *bs, int ret)
|
||||||
static void update_refcount_discard(BlockDriverState *bs,
|
static void update_refcount_discard(BlockDriverState *bs,
|
||||||
uint64_t offset, uint64_t length)
|
uint64_t offset, uint64_t length)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
Qcow2DiscardRegion *d, *p, *next;
|
Qcow2DiscardRegion *d, *p, *next;
|
||||||
|
|
||||||
QTAILQ_FOREACH(d, &s->discards, next) {
|
QTAILQ_FOREACH(d, &s->discards, next) {
|
||||||
|
@ -682,7 +682,7 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
|
||||||
bool decrease,
|
bool decrease,
|
||||||
enum qcow2_discard_type type)
|
enum qcow2_discard_type type)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
int64_t start, last, cluster_offset;
|
int64_t start, last, cluster_offset;
|
||||||
void *refcount_block = NULL;
|
void *refcount_block = NULL;
|
||||||
int64_t old_table_index = -1;
|
int64_t old_table_index = -1;
|
||||||
|
@ -793,7 +793,7 @@ int qcow2_update_cluster_refcount(BlockDriverState *bs,
|
||||||
uint64_t addend, bool decrease,
|
uint64_t addend, bool decrease,
|
||||||
enum qcow2_discard_type type)
|
enum qcow2_discard_type type)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = update_refcount(bs, cluster_index << s->cluster_bits, 1, addend,
|
ret = update_refcount(bs, cluster_index << s->cluster_bits, 1, addend,
|
||||||
|
@ -815,7 +815,7 @@ int qcow2_update_cluster_refcount(BlockDriverState *bs,
|
||||||
/* return < 0 if error */
|
/* return < 0 if error */
|
||||||
static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size)
|
static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
uint64_t i, nb_clusters, refcount;
|
uint64_t i, nb_clusters, refcount;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -875,10 +875,10 @@ int64_t qcow2_alloc_clusters(BlockDriverState *bs, uint64_t size)
|
||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset,
|
int64_t qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset,
|
||||||
int nb_clusters)
|
int64_t nb_clusters)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
uint64_t cluster_index, refcount;
|
uint64_t cluster_index, refcount;
|
||||||
uint64_t i;
|
uint64_t i;
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -916,7 +916,7 @@ int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset,
|
||||||
contiguous sectors. size must be <= cluster_size */
|
contiguous sectors. size must be <= cluster_size */
|
||||||
int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size)
|
int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
int64_t offset;
|
int64_t offset;
|
||||||
size_t free_in_cluster;
|
size_t free_in_cluster;
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -949,11 +949,17 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size)
|
||||||
|
|
||||||
if (!offset || ROUND_UP(offset, s->cluster_size) != new_cluster) {
|
if (!offset || ROUND_UP(offset, s->cluster_size) != new_cluster) {
|
||||||
offset = new_cluster;
|
offset = new_cluster;
|
||||||
|
free_in_cluster = s->cluster_size;
|
||||||
|
} else {
|
||||||
|
free_in_cluster += s->cluster_size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(offset);
|
assert(offset);
|
||||||
ret = update_refcount(bs, offset, size, 1, false, QCOW2_DISCARD_NEVER);
|
ret = update_refcount(bs, offset, size, 1, false, QCOW2_DISCARD_NEVER);
|
||||||
|
if (ret < 0) {
|
||||||
|
offset = 0;
|
||||||
|
}
|
||||||
} while (ret == -EAGAIN);
|
} while (ret == -EAGAIN);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -992,7 +998,7 @@ void qcow2_free_clusters(BlockDriverState *bs,
|
||||||
void qcow2_free_any_clusters(BlockDriverState *bs, uint64_t l2_entry,
|
void qcow2_free_any_clusters(BlockDriverState *bs, uint64_t l2_entry,
|
||||||
int nb_clusters, enum qcow2_discard_type type)
|
int nb_clusters, enum qcow2_discard_type type)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
|
|
||||||
switch (qcow2_get_cluster_type(l2_entry)) {
|
switch (qcow2_get_cluster_type(l2_entry)) {
|
||||||
case QCOW2_CLUSTER_COMPRESSED:
|
case QCOW2_CLUSTER_COMPRESSED:
|
||||||
|
@ -1036,7 +1042,7 @@ void qcow2_free_any_clusters(BlockDriverState *bs, uint64_t l2_entry,
|
||||||
int qcow2_update_snapshot_refcount(BlockDriverState *bs,
|
int qcow2_update_snapshot_refcount(BlockDriverState *bs,
|
||||||
int64_t l1_table_offset, int l1_size, int addend)
|
int64_t l1_table_offset, int l1_size, int addend)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
uint64_t *l1_table, *l2_table, l2_offset, offset, l1_size2, refcount;
|
uint64_t *l1_table, *l2_table, l2_offset, offset, l1_size2, refcount;
|
||||||
bool l1_allocated = false;
|
bool l1_allocated = false;
|
||||||
int64_t old_offset, old_l2_offset;
|
int64_t old_offset, old_l2_offset;
|
||||||
|
@ -1233,7 +1239,7 @@ fail:
|
||||||
/* refcount checking functions */
|
/* refcount checking functions */
|
||||||
|
|
||||||
|
|
||||||
static size_t refcount_array_byte_size(BDRVQcowState *s, uint64_t entries)
|
static size_t refcount_array_byte_size(BDRVQcow2State *s, uint64_t entries)
|
||||||
{
|
{
|
||||||
/* This assertion holds because there is no way we can address more than
|
/* This assertion holds because there is no way we can address more than
|
||||||
* 2^(64 - 9) clusters at once (with cluster size 512 = 2^9, and because
|
* 2^(64 - 9) clusters at once (with cluster size 512 = 2^9, and because
|
||||||
|
@ -1256,10 +1262,10 @@ static size_t refcount_array_byte_size(BDRVQcowState *s, uint64_t entries)
|
||||||
* refcount array buffer will be aligned to a cluster boundary, and the newly
|
* refcount array buffer will be aligned to a cluster boundary, and the newly
|
||||||
* allocated area will be zeroed.
|
* allocated area will be zeroed.
|
||||||
*/
|
*/
|
||||||
static int realloc_refcount_array(BDRVQcowState *s, void **array,
|
static int realloc_refcount_array(BDRVQcow2State *s, void **array,
|
||||||
int64_t *size, int64_t new_size)
|
int64_t *size, int64_t new_size)
|
||||||
{
|
{
|
||||||
size_t old_byte_size, new_byte_size;
|
int64_t old_byte_size, new_byte_size;
|
||||||
void *new_ptr;
|
void *new_ptr;
|
||||||
|
|
||||||
/* Round to clusters so the array can be directly written to disk */
|
/* Round to clusters so the array can be directly written to disk */
|
||||||
|
@ -1275,13 +1281,17 @@ static int realloc_refcount_array(BDRVQcowState *s, void **array,
|
||||||
|
|
||||||
assert(new_byte_size > 0);
|
assert(new_byte_size > 0);
|
||||||
|
|
||||||
|
if (new_byte_size > SIZE_MAX) {
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
new_ptr = g_try_realloc(*array, new_byte_size);
|
new_ptr = g_try_realloc(*array, new_byte_size);
|
||||||
if (!new_ptr) {
|
if (!new_ptr) {
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (new_byte_size > old_byte_size) {
|
if (new_byte_size > old_byte_size) {
|
||||||
memset((void *)((uintptr_t)new_ptr + old_byte_size), 0,
|
memset((char *)new_ptr + old_byte_size, 0,
|
||||||
new_byte_size - old_byte_size);
|
new_byte_size - old_byte_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1304,7 +1314,7 @@ static int inc_refcounts(BlockDriverState *bs,
|
||||||
int64_t *refcount_table_size,
|
int64_t *refcount_table_size,
|
||||||
int64_t offset, int64_t size)
|
int64_t offset, int64_t size)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
uint64_t start, last, cluster_offset, k, refcount;
|
uint64_t start, last, cluster_offset, k, refcount;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -1357,7 +1367,7 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
|
||||||
int64_t *refcount_table_size, int64_t l2_offset,
|
int64_t *refcount_table_size, int64_t l2_offset,
|
||||||
int flags)
|
int flags)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
uint64_t *l2_table, l2_entry;
|
uint64_t *l2_table, l2_entry;
|
||||||
uint64_t next_contiguous_offset = 0;
|
uint64_t next_contiguous_offset = 0;
|
||||||
int i, l2_size, nb_csectors, ret;
|
int i, l2_size, nb_csectors, ret;
|
||||||
|
@ -1477,7 +1487,7 @@ static int check_refcounts_l1(BlockDriverState *bs,
|
||||||
int64_t l1_table_offset, int l1_size,
|
int64_t l1_table_offset, int l1_size,
|
||||||
int flags)
|
int flags)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
uint64_t *l1_table = NULL, l2_offset, l1_size2;
|
uint64_t *l1_table = NULL, l2_offset, l1_size2;
|
||||||
int i, ret;
|
int i, ret;
|
||||||
|
|
||||||
|
@ -1554,7 +1564,7 @@ fail:
|
||||||
static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
|
static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
|
||||||
BdrvCheckMode fix)
|
BdrvCheckMode fix)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
uint64_t *l2_table = qemu_blockalign(bs, s->cluster_size);
|
uint64_t *l2_table = qemu_blockalign(bs, s->cluster_size);
|
||||||
int ret;
|
int ret;
|
||||||
uint64_t refcount;
|
uint64_t refcount;
|
||||||
|
@ -1673,7 +1683,7 @@ static int check_refblocks(BlockDriverState *bs, BdrvCheckResult *res,
|
||||||
BdrvCheckMode fix, bool *rebuild,
|
BdrvCheckMode fix, bool *rebuild,
|
||||||
void **refcount_table, int64_t *nb_clusters)
|
void **refcount_table, int64_t *nb_clusters)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
int64_t i, size;
|
int64_t i, size;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -1776,7 +1786,7 @@ static int calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
|
||||||
BdrvCheckMode fix, bool *rebuild,
|
BdrvCheckMode fix, bool *rebuild,
|
||||||
void **refcount_table, int64_t *nb_clusters)
|
void **refcount_table, int64_t *nb_clusters)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
int64_t i;
|
int64_t i;
|
||||||
QCowSnapshot *sn;
|
QCowSnapshot *sn;
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -1840,7 +1850,7 @@ static void compare_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
|
||||||
int64_t *highest_cluster,
|
int64_t *highest_cluster,
|
||||||
void *refcount_table, int64_t nb_clusters)
|
void *refcount_table, int64_t nb_clusters)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
int64_t i;
|
int64_t i;
|
||||||
uint64_t refcount1, refcount2;
|
uint64_t refcount1, refcount2;
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -1917,7 +1927,7 @@ static int64_t alloc_clusters_imrt(BlockDriverState *bs,
|
||||||
int64_t *imrt_nb_clusters,
|
int64_t *imrt_nb_clusters,
|
||||||
int64_t *first_free_cluster)
|
int64_t *first_free_cluster)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
int64_t cluster = *first_free_cluster, i;
|
int64_t cluster = *first_free_cluster, i;
|
||||||
bool first_gap = true;
|
bool first_gap = true;
|
||||||
int contiguous_free_clusters;
|
int contiguous_free_clusters;
|
||||||
|
@ -1987,7 +1997,7 @@ static int rebuild_refcount_structure(BlockDriverState *bs,
|
||||||
void **refcount_table,
|
void **refcount_table,
|
||||||
int64_t *nb_clusters)
|
int64_t *nb_clusters)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
int64_t first_free_cluster = 0, reftable_offset = -1, cluster = 0;
|
int64_t first_free_cluster = 0, reftable_offset = -1, cluster = 0;
|
||||||
int64_t refblock_offset, refblock_start, refblock_index;
|
int64_t refblock_offset, refblock_start, refblock_index;
|
||||||
uint32_t reftable_size = 0;
|
uint32_t reftable_size = 0;
|
||||||
|
@ -2174,7 +2184,7 @@ fail:
|
||||||
int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
|
int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
|
||||||
BdrvCheckMode fix)
|
BdrvCheckMode fix)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
BdrvCheckResult pre_compare_res;
|
BdrvCheckResult pre_compare_res;
|
||||||
int64_t size, highest_cluster, nb_clusters;
|
int64_t size, highest_cluster, nb_clusters;
|
||||||
void *refcount_table = NULL;
|
void *refcount_table = NULL;
|
||||||
|
@ -2311,7 +2321,7 @@ fail:
|
||||||
int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset,
|
int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset,
|
||||||
int64_t size)
|
int64_t size)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
int chk = s->overlap_check & ~ign;
|
int chk = s->overlap_check & ~ign;
|
||||||
int i, j;
|
int i, j;
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
|
|
||||||
void qcow2_free_snapshots(BlockDriverState *bs)
|
void qcow2_free_snapshots(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for(i = 0; i < s->nb_snapshots; i++) {
|
for(i = 0; i < s->nb_snapshots; i++) {
|
||||||
|
@ -43,7 +43,7 @@ void qcow2_free_snapshots(BlockDriverState *bs)
|
||||||
|
|
||||||
int qcow2_read_snapshots(BlockDriverState *bs)
|
int qcow2_read_snapshots(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
QCowSnapshotHeader h;
|
QCowSnapshotHeader h;
|
||||||
QCowSnapshotExtraData extra;
|
QCowSnapshotExtraData extra;
|
||||||
QCowSnapshot *sn;
|
QCowSnapshot *sn;
|
||||||
|
@ -136,7 +136,7 @@ fail:
|
||||||
/* add at the end of the file a new list of snapshots */
|
/* add at the end of the file a new list of snapshots */
|
||||||
static int qcow2_write_snapshots(BlockDriverState *bs)
|
static int qcow2_write_snapshots(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
QCowSnapshot *sn;
|
QCowSnapshot *sn;
|
||||||
QCowSnapshotHeader h;
|
QCowSnapshotHeader h;
|
||||||
QCowSnapshotExtraData extra;
|
QCowSnapshotExtraData extra;
|
||||||
|
@ -278,7 +278,7 @@ fail:
|
||||||
static void find_new_snapshot_id(BlockDriverState *bs,
|
static void find_new_snapshot_id(BlockDriverState *bs,
|
||||||
char *id_str, int id_str_size)
|
char *id_str, int id_str_size)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
QCowSnapshot *sn;
|
QCowSnapshot *sn;
|
||||||
int i;
|
int i;
|
||||||
unsigned long id, id_max = 0;
|
unsigned long id, id_max = 0;
|
||||||
|
@ -296,7 +296,7 @@ static int find_snapshot_by_id_and_name(BlockDriverState *bs,
|
||||||
const char *id,
|
const char *id,
|
||||||
const char *name)
|
const char *name)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (id && name) {
|
if (id && name) {
|
||||||
|
@ -338,7 +338,7 @@ static int find_snapshot_by_id_or_name(BlockDriverState *bs,
|
||||||
/* if no id is provided, a new one is constructed */
|
/* if no id is provided, a new one is constructed */
|
||||||
int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
|
int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
QCowSnapshot *new_snapshot_list = NULL;
|
QCowSnapshot *new_snapshot_list = NULL;
|
||||||
QCowSnapshot *old_snapshot_list = NULL;
|
QCowSnapshot *old_snapshot_list = NULL;
|
||||||
QCowSnapshot sn1, *sn = &sn1;
|
QCowSnapshot sn1, *sn = &sn1;
|
||||||
|
@ -461,7 +461,7 @@ fail:
|
||||||
/* copy the snapshot 'snapshot_name' into the current disk image */
|
/* copy the snapshot 'snapshot_name' into the current disk image */
|
||||||
int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
|
int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
QCowSnapshot *sn;
|
QCowSnapshot *sn;
|
||||||
int i, snapshot_index;
|
int i, snapshot_index;
|
||||||
int cur_l1_bytes, sn_l1_bytes;
|
int cur_l1_bytes, sn_l1_bytes;
|
||||||
|
@ -587,7 +587,7 @@ int qcow2_snapshot_delete(BlockDriverState *bs,
|
||||||
const char *name,
|
const char *name,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
QCowSnapshot sn;
|
QCowSnapshot sn;
|
||||||
int snapshot_index, ret;
|
int snapshot_index, ret;
|
||||||
|
|
||||||
|
@ -650,7 +650,7 @@ int qcow2_snapshot_delete(BlockDriverState *bs,
|
||||||
|
|
||||||
int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
|
int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
QEMUSnapshotInfo *sn_tab, *sn_info;
|
QEMUSnapshotInfo *sn_tab, *sn_info;
|
||||||
QCowSnapshot *sn;
|
QCowSnapshot *sn;
|
||||||
int i;
|
int i;
|
||||||
|
@ -683,7 +683,7 @@ int qcow2_snapshot_load_tmp(BlockDriverState *bs,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
int i, snapshot_index;
|
int i, snapshot_index;
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
QCowSnapshot *sn;
|
QCowSnapshot *sn;
|
||||||
uint64_t *new_l1_table;
|
uint64_t *new_l1_table;
|
||||||
int new_l1_bytes;
|
int new_l1_bytes;
|
||||||
|
|
494
block/qcow2.c
494
block/qcow2.c
|
@ -85,7 +85,7 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset,
|
||||||
uint64_t end_offset, void **p_feature_table,
|
uint64_t end_offset, void **p_feature_table,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
QCowExtension ext;
|
QCowExtension ext;
|
||||||
uint64_t offset;
|
uint64_t offset;
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -187,7 +187,7 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset,
|
||||||
|
|
||||||
static void cleanup_unknown_header_ext(BlockDriverState *bs)
|
static void cleanup_unknown_header_ext(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
Qcow2UnknownHeaderExtension *uext, *next;
|
Qcow2UnknownHeaderExtension *uext, *next;
|
||||||
|
|
||||||
QLIST_FOREACH_SAFE(uext, &s->unknown_header_ext, next, next) {
|
QLIST_FOREACH_SAFE(uext, &s->unknown_header_ext, next, next) {
|
||||||
|
@ -249,7 +249,7 @@ static void report_unsupported_feature(BlockDriverState *bs,
|
||||||
*/
|
*/
|
||||||
int qcow2_mark_dirty(BlockDriverState *bs)
|
int qcow2_mark_dirty(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
uint64_t val;
|
uint64_t val;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -282,7 +282,7 @@ int qcow2_mark_dirty(BlockDriverState *bs)
|
||||||
*/
|
*/
|
||||||
static int qcow2_mark_clean(BlockDriverState *bs)
|
static int qcow2_mark_clean(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
|
|
||||||
if (s->incompatible_features & QCOW2_INCOMPAT_DIRTY) {
|
if (s->incompatible_features & QCOW2_INCOMPAT_DIRTY) {
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -304,7 +304,7 @@ static int qcow2_mark_clean(BlockDriverState *bs)
|
||||||
*/
|
*/
|
||||||
int qcow2_mark_corrupt(BlockDriverState *bs)
|
int qcow2_mark_corrupt(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
|
|
||||||
s->incompatible_features |= QCOW2_INCOMPAT_CORRUPT;
|
s->incompatible_features |= QCOW2_INCOMPAT_CORRUPT;
|
||||||
return qcow2_update_header(bs);
|
return qcow2_update_header(bs);
|
||||||
|
@ -316,7 +316,7 @@ int qcow2_mark_corrupt(BlockDriverState *bs)
|
||||||
*/
|
*/
|
||||||
int qcow2_mark_consistent(BlockDriverState *bs)
|
int qcow2_mark_consistent(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
|
|
||||||
if (s->incompatible_features & QCOW2_INCOMPAT_CORRUPT) {
|
if (s->incompatible_features & QCOW2_INCOMPAT_CORRUPT) {
|
||||||
int ret = bdrv_flush(bs);
|
int ret = bdrv_flush(bs);
|
||||||
|
@ -351,7 +351,7 @@ static int qcow2_check(BlockDriverState *bs, BdrvCheckResult *result,
|
||||||
static int validate_table_offset(BlockDriverState *bs, uint64_t offset,
|
static int validate_table_offset(BlockDriverState *bs, uint64_t offset,
|
||||||
uint64_t entries, size_t entry_len)
|
uint64_t entries, size_t entry_len)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
uint64_t size;
|
uint64_t size;
|
||||||
|
|
||||||
/* Use signed INT64_MAX as the maximum even for uint64_t header fields,
|
/* Use signed INT64_MAX as the maximum even for uint64_t header fields,
|
||||||
|
@ -490,7 +490,7 @@ static const char *overlap_bool_option_names[QCOW2_OL_MAX_BITNR] = {
|
||||||
static void cache_clean_timer_cb(void *opaque)
|
static void cache_clean_timer_cb(void *opaque)
|
||||||
{
|
{
|
||||||
BlockDriverState *bs = opaque;
|
BlockDriverState *bs = opaque;
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
qcow2_cache_clean_unused(bs, s->l2_table_cache);
|
qcow2_cache_clean_unused(bs, s->l2_table_cache);
|
||||||
qcow2_cache_clean_unused(bs, s->refcount_block_cache);
|
qcow2_cache_clean_unused(bs, s->refcount_block_cache);
|
||||||
timer_mod(s->cache_clean_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) +
|
timer_mod(s->cache_clean_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) +
|
||||||
|
@ -499,7 +499,7 @@ static void cache_clean_timer_cb(void *opaque)
|
||||||
|
|
||||||
static void cache_clean_timer_init(BlockDriverState *bs, AioContext *context)
|
static void cache_clean_timer_init(BlockDriverState *bs, AioContext *context)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
if (s->cache_clean_interval > 0) {
|
if (s->cache_clean_interval > 0) {
|
||||||
s->cache_clean_timer = aio_timer_new(context, QEMU_CLOCK_VIRTUAL,
|
s->cache_clean_timer = aio_timer_new(context, QEMU_CLOCK_VIRTUAL,
|
||||||
SCALE_MS, cache_clean_timer_cb,
|
SCALE_MS, cache_clean_timer_cb,
|
||||||
|
@ -511,7 +511,7 @@ static void cache_clean_timer_init(BlockDriverState *bs, AioContext *context)
|
||||||
|
|
||||||
static void cache_clean_timer_del(BlockDriverState *bs)
|
static void cache_clean_timer_del(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
if (s->cache_clean_timer) {
|
if (s->cache_clean_timer) {
|
||||||
timer_del(s->cache_clean_timer);
|
timer_del(s->cache_clean_timer);
|
||||||
timer_free(s->cache_clean_timer);
|
timer_free(s->cache_clean_timer);
|
||||||
|
@ -534,7 +534,7 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts,
|
||||||
uint64_t *l2_cache_size,
|
uint64_t *l2_cache_size,
|
||||||
uint64_t *refcount_cache_size, Error **errp)
|
uint64_t *refcount_cache_size, Error **errp)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
uint64_t combined_cache_size;
|
uint64_t combined_cache_size;
|
||||||
bool l2_cache_size_set, refcount_cache_size_set, combined_cache_size_set;
|
bool l2_cache_size_set, refcount_cache_size_set, combined_cache_size_set;
|
||||||
|
|
||||||
|
@ -589,21 +589,244 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
|
typedef struct Qcow2ReopenState {
|
||||||
Error **errp)
|
Qcow2Cache *l2_table_cache;
|
||||||
|
Qcow2Cache *refcount_block_cache;
|
||||||
|
bool use_lazy_refcounts;
|
||||||
|
int overlap_check;
|
||||||
|
bool discard_passthrough[QCOW2_DISCARD_MAX];
|
||||||
|
uint64_t cache_clean_interval;
|
||||||
|
} Qcow2ReopenState;
|
||||||
|
|
||||||
|
static int qcow2_update_options_prepare(BlockDriverState *bs,
|
||||||
|
Qcow2ReopenState *r,
|
||||||
|
QDict *options, int flags,
|
||||||
|
Error **errp)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
unsigned int len, i;
|
|
||||||
int ret = 0;
|
|
||||||
QCowHeader header;
|
|
||||||
QemuOpts *opts = NULL;
|
QemuOpts *opts = NULL;
|
||||||
Error *local_err = NULL;
|
|
||||||
uint64_t ext_end;
|
|
||||||
uint64_t l1_vm_state_index;
|
|
||||||
const char *opt_overlap_check, *opt_overlap_check_template;
|
const char *opt_overlap_check, *opt_overlap_check_template;
|
||||||
int overlap_check_template = 0;
|
int overlap_check_template = 0;
|
||||||
uint64_t l2_cache_size, refcount_cache_size;
|
uint64_t l2_cache_size, refcount_cache_size;
|
||||||
uint64_t cache_clean_interval;
|
int i;
|
||||||
|
Error *local_err = NULL;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
opts = qemu_opts_create(&qcow2_runtime_opts, NULL, 0, &error_abort);
|
||||||
|
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get L2 table/refcount block cache size from command line options */
|
||||||
|
read_cache_sizes(bs, opts, &l2_cache_size, &refcount_cache_size,
|
||||||
|
&local_err);
|
||||||
|
if (local_err) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
l2_cache_size /= s->cluster_size;
|
||||||
|
if (l2_cache_size < MIN_L2_CACHE_SIZE) {
|
||||||
|
l2_cache_size = MIN_L2_CACHE_SIZE;
|
||||||
|
}
|
||||||
|
if (l2_cache_size > INT_MAX) {
|
||||||
|
error_setg(errp, "L2 cache size too big");
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
refcount_cache_size /= s->cluster_size;
|
||||||
|
if (refcount_cache_size < MIN_REFCOUNT_CACHE_SIZE) {
|
||||||
|
refcount_cache_size = MIN_REFCOUNT_CACHE_SIZE;
|
||||||
|
}
|
||||||
|
if (refcount_cache_size > INT_MAX) {
|
||||||
|
error_setg(errp, "Refcount cache size too big");
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* alloc new L2 table/refcount block cache, flush old one */
|
||||||
|
if (s->l2_table_cache) {
|
||||||
|
ret = qcow2_cache_flush(bs, s->l2_table_cache);
|
||||||
|
if (ret) {
|
||||||
|
error_setg_errno(errp, -ret, "Failed to flush the L2 table cache");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s->refcount_block_cache) {
|
||||||
|
ret = qcow2_cache_flush(bs, s->refcount_block_cache);
|
||||||
|
if (ret) {
|
||||||
|
error_setg_errno(errp, -ret,
|
||||||
|
"Failed to flush the refcount block cache");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r->l2_table_cache = qcow2_cache_create(bs, l2_cache_size);
|
||||||
|
r->refcount_block_cache = qcow2_cache_create(bs, refcount_cache_size);
|
||||||
|
if (r->l2_table_cache == NULL || r->refcount_block_cache == NULL) {
|
||||||
|
error_setg(errp, "Could not allocate metadata caches");
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* New interval for cache cleanup timer */
|
||||||
|
r->cache_clean_interval =
|
||||||
|
qemu_opt_get_number(opts, QCOW2_OPT_CACHE_CLEAN_INTERVAL,
|
||||||
|
s->cache_clean_interval);
|
||||||
|
if (r->cache_clean_interval > UINT_MAX) {
|
||||||
|
error_setg(errp, "Cache clean interval too big");
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* lazy-refcounts; flush if going from enabled to disabled */
|
||||||
|
r->use_lazy_refcounts = qemu_opt_get_bool(opts, QCOW2_OPT_LAZY_REFCOUNTS,
|
||||||
|
(s->compatible_features & QCOW2_COMPAT_LAZY_REFCOUNTS));
|
||||||
|
if (r->use_lazy_refcounts && s->qcow_version < 3) {
|
||||||
|
error_setg(errp, "Lazy refcounts require a qcow2 image with at least "
|
||||||
|
"qemu 1.1 compatibility level");
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s->use_lazy_refcounts && !r->use_lazy_refcounts) {
|
||||||
|
ret = qcow2_mark_clean(bs);
|
||||||
|
if (ret < 0) {
|
||||||
|
error_setg_errno(errp, -ret, "Failed to disable lazy refcounts");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Overlap check options */
|
||||||
|
opt_overlap_check = qemu_opt_get(opts, QCOW2_OPT_OVERLAP);
|
||||||
|
opt_overlap_check_template = qemu_opt_get(opts, QCOW2_OPT_OVERLAP_TEMPLATE);
|
||||||
|
if (opt_overlap_check_template && opt_overlap_check &&
|
||||||
|
strcmp(opt_overlap_check_template, opt_overlap_check))
|
||||||
|
{
|
||||||
|
error_setg(errp, "Conflicting values for qcow2 options '"
|
||||||
|
QCOW2_OPT_OVERLAP "' ('%s') and '" QCOW2_OPT_OVERLAP_TEMPLATE
|
||||||
|
"' ('%s')", opt_overlap_check, opt_overlap_check_template);
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
if (!opt_overlap_check) {
|
||||||
|
opt_overlap_check = opt_overlap_check_template ?: "cached";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strcmp(opt_overlap_check, "none")) {
|
||||||
|
overlap_check_template = 0;
|
||||||
|
} else if (!strcmp(opt_overlap_check, "constant")) {
|
||||||
|
overlap_check_template = QCOW2_OL_CONSTANT;
|
||||||
|
} else if (!strcmp(opt_overlap_check, "cached")) {
|
||||||
|
overlap_check_template = QCOW2_OL_CACHED;
|
||||||
|
} else if (!strcmp(opt_overlap_check, "all")) {
|
||||||
|
overlap_check_template = QCOW2_OL_ALL;
|
||||||
|
} else {
|
||||||
|
error_setg(errp, "Unsupported value '%s' for qcow2 option "
|
||||||
|
"'overlap-check'. Allowed are any of the following: "
|
||||||
|
"none, constant, cached, all", opt_overlap_check);
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
r->overlap_check = 0;
|
||||||
|
for (i = 0; i < QCOW2_OL_MAX_BITNR; i++) {
|
||||||
|
/* overlap-check defines a template bitmask, but every flag may be
|
||||||
|
* overwritten through the associated boolean option */
|
||||||
|
r->overlap_check |=
|
||||||
|
qemu_opt_get_bool(opts, overlap_bool_option_names[i],
|
||||||
|
overlap_check_template & (1 << i)) << i;
|
||||||
|
}
|
||||||
|
|
||||||
|
r->discard_passthrough[QCOW2_DISCARD_NEVER] = false;
|
||||||
|
r->discard_passthrough[QCOW2_DISCARD_ALWAYS] = true;
|
||||||
|
r->discard_passthrough[QCOW2_DISCARD_REQUEST] =
|
||||||
|
qemu_opt_get_bool(opts, QCOW2_OPT_DISCARD_REQUEST,
|
||||||
|
flags & BDRV_O_UNMAP);
|
||||||
|
r->discard_passthrough[QCOW2_DISCARD_SNAPSHOT] =
|
||||||
|
qemu_opt_get_bool(opts, QCOW2_OPT_DISCARD_SNAPSHOT, true);
|
||||||
|
r->discard_passthrough[QCOW2_DISCARD_OTHER] =
|
||||||
|
qemu_opt_get_bool(opts, QCOW2_OPT_DISCARD_OTHER, false);
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
fail:
|
||||||
|
qemu_opts_del(opts);
|
||||||
|
opts = NULL;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void qcow2_update_options_commit(BlockDriverState *bs,
|
||||||
|
Qcow2ReopenState *r)
|
||||||
|
{
|
||||||
|
BDRVQcow2State *s = bs->opaque;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (s->l2_table_cache) {
|
||||||
|
qcow2_cache_destroy(bs, s->l2_table_cache);
|
||||||
|
}
|
||||||
|
if (s->refcount_block_cache) {
|
||||||
|
qcow2_cache_destroy(bs, s->refcount_block_cache);
|
||||||
|
}
|
||||||
|
s->l2_table_cache = r->l2_table_cache;
|
||||||
|
s->refcount_block_cache = r->refcount_block_cache;
|
||||||
|
|
||||||
|
s->overlap_check = r->overlap_check;
|
||||||
|
s->use_lazy_refcounts = r->use_lazy_refcounts;
|
||||||
|
|
||||||
|
for (i = 0; i < QCOW2_DISCARD_MAX; i++) {
|
||||||
|
s->discard_passthrough[i] = r->discard_passthrough[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s->cache_clean_interval != r->cache_clean_interval) {
|
||||||
|
cache_clean_timer_del(bs);
|
||||||
|
s->cache_clean_interval = r->cache_clean_interval;
|
||||||
|
cache_clean_timer_init(bs, bdrv_get_aio_context(bs));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void qcow2_update_options_abort(BlockDriverState *bs,
|
||||||
|
Qcow2ReopenState *r)
|
||||||
|
{
|
||||||
|
if (r->l2_table_cache) {
|
||||||
|
qcow2_cache_destroy(bs, r->l2_table_cache);
|
||||||
|
}
|
||||||
|
if (r->refcount_block_cache) {
|
||||||
|
qcow2_cache_destroy(bs, r->refcount_block_cache);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int qcow2_update_options(BlockDriverState *bs, QDict *options,
|
||||||
|
int flags, Error **errp)
|
||||||
|
{
|
||||||
|
Qcow2ReopenState r = {};
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = qcow2_update_options_prepare(bs, &r, options, flags, errp);
|
||||||
|
if (ret >= 0) {
|
||||||
|
qcow2_update_options_commit(bs, &r);
|
||||||
|
} else {
|
||||||
|
qcow2_update_options_abort(bs, &r);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
BDRVQcow2State *s = bs->opaque;
|
||||||
|
unsigned int len, i;
|
||||||
|
int ret = 0;
|
||||||
|
QCowHeader header;
|
||||||
|
Error *local_err = NULL;
|
||||||
|
uint64_t ext_end;
|
||||||
|
uint64_t l1_vm_state_index;
|
||||||
|
|
||||||
ret = bdrv_pread(bs->file, 0, &header, sizeof(header));
|
ret = bdrv_pread(bs->file, 0, &header, sizeof(header));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
@ -851,62 +1074,12 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* get L2 table/refcount block cache size from command line options */
|
/* Parse driver-specific options */
|
||||||
opts = qemu_opts_create(&qcow2_runtime_opts, NULL, 0, &error_abort);
|
ret = qcow2_update_options(bs, options, flags, errp);
|
||||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
if (ret < 0) {
|
||||||
if (local_err) {
|
|
||||||
error_propagate(errp, local_err);
|
|
||||||
ret = -EINVAL;
|
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
read_cache_sizes(bs, opts, &l2_cache_size, &refcount_cache_size,
|
|
||||||
&local_err);
|
|
||||||
if (local_err) {
|
|
||||||
error_propagate(errp, local_err);
|
|
||||||
ret = -EINVAL;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
l2_cache_size /= s->cluster_size;
|
|
||||||
if (l2_cache_size < MIN_L2_CACHE_SIZE) {
|
|
||||||
l2_cache_size = MIN_L2_CACHE_SIZE;
|
|
||||||
}
|
|
||||||
if (l2_cache_size > INT_MAX) {
|
|
||||||
error_setg(errp, "L2 cache size too big");
|
|
||||||
ret = -EINVAL;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
refcount_cache_size /= s->cluster_size;
|
|
||||||
if (refcount_cache_size < MIN_REFCOUNT_CACHE_SIZE) {
|
|
||||||
refcount_cache_size = MIN_REFCOUNT_CACHE_SIZE;
|
|
||||||
}
|
|
||||||
if (refcount_cache_size > INT_MAX) {
|
|
||||||
error_setg(errp, "Refcount cache size too big");
|
|
||||||
ret = -EINVAL;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* alloc L2 table/refcount block cache */
|
|
||||||
s->l2_table_cache = qcow2_cache_create(bs, l2_cache_size);
|
|
||||||
s->refcount_block_cache = qcow2_cache_create(bs, refcount_cache_size);
|
|
||||||
if (s->l2_table_cache == NULL || s->refcount_block_cache == NULL) {
|
|
||||||
error_setg(errp, "Could not allocate metadata caches");
|
|
||||||
ret = -ENOMEM;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
cache_clean_interval =
|
|
||||||
qemu_opt_get_number(opts, QCOW2_OPT_CACHE_CLEAN_INTERVAL, 0);
|
|
||||||
if (cache_clean_interval > UINT_MAX) {
|
|
||||||
error_setg(errp, "Cache clean interval too big");
|
|
||||||
ret = -EINVAL;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
s->cache_clean_interval = cache_clean_interval;
|
|
||||||
cache_clean_timer_init(bs, bdrv_get_aio_context(bs));
|
|
||||||
|
|
||||||
s->cluster_cache = g_malloc(s->cluster_size);
|
s->cluster_cache = g_malloc(s->cluster_size);
|
||||||
/* one more sector for decompressed data alignment */
|
/* one more sector for decompressed data alignment */
|
||||||
s->cluster_data = qemu_try_blockalign(bs->file, QCOW_MAX_CRYPT_CLUSTERS
|
s->cluster_data = qemu_try_blockalign(bs->file, QCOW_MAX_CRYPT_CLUSTERS
|
||||||
|
@ -991,70 +1164,6 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Enable lazy_refcounts according to image and command line options */
|
|
||||||
s->use_lazy_refcounts = qemu_opt_get_bool(opts, QCOW2_OPT_LAZY_REFCOUNTS,
|
|
||||||
(s->compatible_features & QCOW2_COMPAT_LAZY_REFCOUNTS));
|
|
||||||
|
|
||||||
s->discard_passthrough[QCOW2_DISCARD_NEVER] = false;
|
|
||||||
s->discard_passthrough[QCOW2_DISCARD_ALWAYS] = true;
|
|
||||||
s->discard_passthrough[QCOW2_DISCARD_REQUEST] =
|
|
||||||
qemu_opt_get_bool(opts, QCOW2_OPT_DISCARD_REQUEST,
|
|
||||||
flags & BDRV_O_UNMAP);
|
|
||||||
s->discard_passthrough[QCOW2_DISCARD_SNAPSHOT] =
|
|
||||||
qemu_opt_get_bool(opts, QCOW2_OPT_DISCARD_SNAPSHOT, true);
|
|
||||||
s->discard_passthrough[QCOW2_DISCARD_OTHER] =
|
|
||||||
qemu_opt_get_bool(opts, QCOW2_OPT_DISCARD_OTHER, false);
|
|
||||||
|
|
||||||
opt_overlap_check = qemu_opt_get(opts, QCOW2_OPT_OVERLAP);
|
|
||||||
opt_overlap_check_template = qemu_opt_get(opts, QCOW2_OPT_OVERLAP_TEMPLATE);
|
|
||||||
if (opt_overlap_check_template && opt_overlap_check &&
|
|
||||||
strcmp(opt_overlap_check_template, opt_overlap_check))
|
|
||||||
{
|
|
||||||
error_setg(errp, "Conflicting values for qcow2 options '"
|
|
||||||
QCOW2_OPT_OVERLAP "' ('%s') and '" QCOW2_OPT_OVERLAP_TEMPLATE
|
|
||||||
"' ('%s')", opt_overlap_check, opt_overlap_check_template);
|
|
||||||
ret = -EINVAL;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
if (!opt_overlap_check) {
|
|
||||||
opt_overlap_check = opt_overlap_check_template ?: "cached";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!strcmp(opt_overlap_check, "none")) {
|
|
||||||
overlap_check_template = 0;
|
|
||||||
} else if (!strcmp(opt_overlap_check, "constant")) {
|
|
||||||
overlap_check_template = QCOW2_OL_CONSTANT;
|
|
||||||
} else if (!strcmp(opt_overlap_check, "cached")) {
|
|
||||||
overlap_check_template = QCOW2_OL_CACHED;
|
|
||||||
} else if (!strcmp(opt_overlap_check, "all")) {
|
|
||||||
overlap_check_template = QCOW2_OL_ALL;
|
|
||||||
} else {
|
|
||||||
error_setg(errp, "Unsupported value '%s' for qcow2 option "
|
|
||||||
"'overlap-check'. Allowed are either of the following: "
|
|
||||||
"none, constant, cached, all", opt_overlap_check);
|
|
||||||
ret = -EINVAL;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
s->overlap_check = 0;
|
|
||||||
for (i = 0; i < QCOW2_OL_MAX_BITNR; i++) {
|
|
||||||
/* overlap-check defines a template bitmask, but every flag may be
|
|
||||||
* overwritten through the associated boolean option */
|
|
||||||
s->overlap_check |=
|
|
||||||
qemu_opt_get_bool(opts, overlap_bool_option_names[i],
|
|
||||||
overlap_check_template & (1 << i)) << i;
|
|
||||||
}
|
|
||||||
|
|
||||||
qemu_opts_del(opts);
|
|
||||||
opts = NULL;
|
|
||||||
|
|
||||||
if (s->use_lazy_refcounts && s->qcow_version < 3) {
|
|
||||||
error_setg(errp, "Lazy refcounts require a qcow2 image with at least "
|
|
||||||
"qemu 1.1 compatibility level");
|
|
||||||
ret = -EINVAL;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DEBUG_ALLOC
|
#ifdef DEBUG_ALLOC
|
||||||
{
|
{
|
||||||
BdrvCheckResult result = {0};
|
BdrvCheckResult result = {0};
|
||||||
|
@ -1064,7 +1173,6 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
qemu_opts_del(opts);
|
|
||||||
g_free(s->unknown_header_fields);
|
g_free(s->unknown_header_fields);
|
||||||
cleanup_unknown_header_ext(bs);
|
cleanup_unknown_header_ext(bs);
|
||||||
qcow2_free_snapshots(bs);
|
qcow2_free_snapshots(bs);
|
||||||
|
@ -1086,14 +1194,14 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
|
|
||||||
static void qcow2_refresh_limits(BlockDriverState *bs, Error **errp)
|
static void qcow2_refresh_limits(BlockDriverState *bs, Error **errp)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
|
|
||||||
bs->bl.write_zeroes_alignment = s->cluster_sectors;
|
bs->bl.write_zeroes_alignment = s->cluster_sectors;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int qcow2_set_key(BlockDriverState *bs, const char *key)
|
static int qcow2_set_key(BlockDriverState *bs, const char *key)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
uint8_t keybuf[16];
|
uint8_t keybuf[16];
|
||||||
int len, i;
|
int len, i;
|
||||||
Error *err = NULL;
|
Error *err = NULL;
|
||||||
|
@ -1126,32 +1234,58 @@ static int qcow2_set_key(BlockDriverState *bs, const char *key)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We have no actual commit/abort logic for qcow2, but we need to write out any
|
|
||||||
* unwritten data if we reopen read-only. */
|
|
||||||
static int qcow2_reopen_prepare(BDRVReopenState *state,
|
static int qcow2_reopen_prepare(BDRVReopenState *state,
|
||||||
BlockReopenQueue *queue, Error **errp)
|
BlockReopenQueue *queue, Error **errp)
|
||||||
{
|
{
|
||||||
|
Qcow2ReopenState *r;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
r = g_new0(Qcow2ReopenState, 1);
|
||||||
|
state->opaque = r;
|
||||||
|
|
||||||
|
ret = qcow2_update_options_prepare(state->bs, r, state->options,
|
||||||
|
state->flags, errp);
|
||||||
|
if (ret < 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We need to write out any unwritten data if we reopen read-only. */
|
||||||
if ((state->flags & BDRV_O_RDWR) == 0) {
|
if ((state->flags & BDRV_O_RDWR) == 0) {
|
||||||
ret = bdrv_flush(state->bs);
|
ret = bdrv_flush(state->bs);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return ret;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = qcow2_mark_clean(state->bs);
|
ret = qcow2_mark_clean(state->bs);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return ret;
|
goto fail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
qcow2_update_options_abort(state->bs, r);
|
||||||
|
g_free(r);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void qcow2_reopen_commit(BDRVReopenState *state)
|
||||||
|
{
|
||||||
|
qcow2_update_options_commit(state->bs, state->opaque);
|
||||||
|
g_free(state->opaque);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void qcow2_reopen_abort(BDRVReopenState *state)
|
||||||
|
{
|
||||||
|
qcow2_update_options_abort(state->bs, state->opaque);
|
||||||
|
g_free(state->opaque);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int64_t coroutine_fn qcow2_co_get_block_status(BlockDriverState *bs,
|
static int64_t coroutine_fn qcow2_co_get_block_status(BlockDriverState *bs,
|
||||||
int64_t sector_num, int nb_sectors, int *pnum)
|
int64_t sector_num, int nb_sectors, int *pnum)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
uint64_t cluster_offset;
|
uint64_t cluster_offset;
|
||||||
int index_in_cluster, ret;
|
int index_in_cluster, ret;
|
||||||
int64_t status = 0;
|
int64_t status = 0;
|
||||||
|
@ -1198,7 +1332,7 @@ int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov,
|
||||||
static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num,
|
static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num,
|
||||||
int remaining_sectors, QEMUIOVector *qiov)
|
int remaining_sectors, QEMUIOVector *qiov)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
int index_in_cluster, n1;
|
int index_in_cluster, n1;
|
||||||
int ret;
|
int ret;
|
||||||
int cur_nr_sectors; /* number of sectors in current iteration */
|
int cur_nr_sectors; /* number of sectors in current iteration */
|
||||||
|
@ -1360,7 +1494,7 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
|
||||||
int remaining_sectors,
|
int remaining_sectors,
|
||||||
QEMUIOVector *qiov)
|
QEMUIOVector *qiov)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
int index_in_cluster;
|
int index_in_cluster;
|
||||||
int ret;
|
int ret;
|
||||||
int cur_nr_sectors; /* number of sectors in current iteration */
|
int cur_nr_sectors; /* number of sectors in current iteration */
|
||||||
|
@ -1506,7 +1640,7 @@ fail:
|
||||||
|
|
||||||
static void qcow2_close(BlockDriverState *bs)
|
static void qcow2_close(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
qemu_vfree(s->l1_table);
|
qemu_vfree(s->l1_table);
|
||||||
/* else pre-write overlap checks in cache_destroy may crash */
|
/* else pre-write overlap checks in cache_destroy may crash */
|
||||||
s->l1_table = NULL;
|
s->l1_table = NULL;
|
||||||
|
@ -1552,7 +1686,7 @@ static void qcow2_close(BlockDriverState *bs)
|
||||||
|
|
||||||
static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp)
|
static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
int flags = s->flags;
|
int flags = s->flags;
|
||||||
QCryptoCipher *cipher = NULL;
|
QCryptoCipher *cipher = NULL;
|
||||||
QDict *options;
|
QDict *options;
|
||||||
|
@ -1575,7 +1709,7 @@ static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(s, 0, sizeof(BDRVQcowState));
|
memset(s, 0, sizeof(BDRVQcow2State));
|
||||||
options = qdict_clone_shallow(bs->options);
|
options = qdict_clone_shallow(bs->options);
|
||||||
|
|
||||||
ret = qcow2_open(bs, options, flags, &local_err);
|
ret = qcow2_open(bs, options, flags, &local_err);
|
||||||
|
@ -1622,7 +1756,7 @@ static size_t header_ext_add(char *buf, uint32_t magic, const void *s,
|
||||||
*/
|
*/
|
||||||
int qcow2_update_header(BlockDriverState *bs)
|
int qcow2_update_header(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
QCowHeader *header;
|
QCowHeader *header;
|
||||||
char *buf;
|
char *buf;
|
||||||
size_t buflen = s->cluster_size;
|
size_t buflen = s->cluster_size;
|
||||||
|
@ -1791,7 +1925,7 @@ fail:
|
||||||
static int qcow2_change_backing_file(BlockDriverState *bs,
|
static int qcow2_change_backing_file(BlockDriverState *bs,
|
||||||
const char *backing_file, const char *backing_fmt)
|
const char *backing_file, const char *backing_fmt)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
|
|
||||||
pstrcpy(bs->backing_file, sizeof(bs->backing_file), backing_file ?: "");
|
pstrcpy(bs->backing_file, sizeof(bs->backing_file), backing_file ?: "");
|
||||||
pstrcpy(bs->backing_format, sizeof(bs->backing_format), backing_fmt ?: "");
|
pstrcpy(bs->backing_format, sizeof(bs->backing_format), backing_fmt ?: "");
|
||||||
|
@ -1873,8 +2007,10 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
||||||
QemuOpts *opts, int version, int refcount_order,
|
QemuOpts *opts, int version, int refcount_order,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
/* Calculate cluster_bits */
|
|
||||||
int cluster_bits;
|
int cluster_bits;
|
||||||
|
QDict *options;
|
||||||
|
|
||||||
|
/* Calculate cluster_bits */
|
||||||
cluster_bits = ctz32(cluster_size);
|
cluster_bits = ctz32(cluster_size);
|
||||||
if (cluster_bits < MIN_CLUSTER_BITS || cluster_bits > MAX_CLUSTER_BITS ||
|
if (cluster_bits < MIN_CLUSTER_BITS || cluster_bits > MAX_CLUSTER_BITS ||
|
||||||
(1 << cluster_bits) != cluster_size)
|
(1 << cluster_bits) != cluster_size)
|
||||||
|
@ -1973,7 +2109,7 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
||||||
|
|
||||||
bs = NULL;
|
bs = NULL;
|
||||||
ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
|
ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
|
||||||
NULL, &local_err);
|
&local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -2032,9 +2168,11 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
||||||
* refcount of the cluster that is occupied by the header and the refcount
|
* refcount of the cluster that is occupied by the header and the refcount
|
||||||
* table)
|
* table)
|
||||||
*/
|
*/
|
||||||
ret = bdrv_open(&bs, filename, NULL, NULL,
|
options = qdict_new();
|
||||||
|
qdict_put(options, "driver", qstring_from_str("qcow2"));
|
||||||
|
ret = bdrv_open(&bs, filename, NULL, options,
|
||||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH,
|
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH,
|
||||||
&bdrv_qcow2, &local_err);
|
&local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -2070,7 +2208,7 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
||||||
|
|
||||||
/* And if we're supposed to preallocate metadata, do that now */
|
/* And if we're supposed to preallocate metadata, do that now */
|
||||||
if (prealloc != PREALLOC_MODE_OFF) {
|
if (prealloc != PREALLOC_MODE_OFF) {
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
qemu_co_mutex_lock(&s->lock);
|
qemu_co_mutex_lock(&s->lock);
|
||||||
ret = preallocate(bs);
|
ret = preallocate(bs);
|
||||||
qemu_co_mutex_unlock(&s->lock);
|
qemu_co_mutex_unlock(&s->lock);
|
||||||
|
@ -2084,9 +2222,11 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
||||||
bs = NULL;
|
bs = NULL;
|
||||||
|
|
||||||
/* Reopen the image without BDRV_O_NO_FLUSH to flush it before returning */
|
/* Reopen the image without BDRV_O_NO_FLUSH to flush it before returning */
|
||||||
ret = bdrv_open(&bs, filename, NULL, NULL,
|
options = qdict_new();
|
||||||
|
qdict_put(options, "driver", qstring_from_str("qcow2"));
|
||||||
|
ret = bdrv_open(&bs, filename, NULL, options,
|
||||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_BACKING,
|
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_BACKING,
|
||||||
&bdrv_qcow2, &local_err);
|
&local_err);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -2203,7 +2343,7 @@ static coroutine_fn int qcow2_co_write_zeroes(BlockDriverState *bs,
|
||||||
int64_t sector_num, int nb_sectors, BdrvRequestFlags flags)
|
int64_t sector_num, int nb_sectors, BdrvRequestFlags flags)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
|
|
||||||
/* Emulate misaligned zero writes */
|
/* Emulate misaligned zero writes */
|
||||||
if (sector_num % s->cluster_sectors || nb_sectors % s->cluster_sectors) {
|
if (sector_num % s->cluster_sectors || nb_sectors % s->cluster_sectors) {
|
||||||
|
@ -2223,7 +2363,7 @@ static coroutine_fn int qcow2_co_discard(BlockDriverState *bs,
|
||||||
int64_t sector_num, int nb_sectors)
|
int64_t sector_num, int nb_sectors)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
|
|
||||||
qemu_co_mutex_lock(&s->lock);
|
qemu_co_mutex_lock(&s->lock);
|
||||||
ret = qcow2_discard_clusters(bs, sector_num << BDRV_SECTOR_BITS,
|
ret = qcow2_discard_clusters(bs, sector_num << BDRV_SECTOR_BITS,
|
||||||
|
@ -2234,7 +2374,7 @@ static coroutine_fn int qcow2_co_discard(BlockDriverState *bs,
|
||||||
|
|
||||||
static int qcow2_truncate(BlockDriverState *bs, int64_t offset)
|
static int qcow2_truncate(BlockDriverState *bs, int64_t offset)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
int64_t new_l1_size;
|
int64_t new_l1_size;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -2278,7 +2418,7 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset)
|
||||||
static int qcow2_write_compressed(BlockDriverState *bs, int64_t sector_num,
|
static int qcow2_write_compressed(BlockDriverState *bs, int64_t sector_num,
|
||||||
const uint8_t *buf, int nb_sectors)
|
const uint8_t *buf, int nb_sectors)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
z_stream strm;
|
z_stream strm;
|
||||||
int ret, out_len;
|
int ret, out_len;
|
||||||
uint8_t *out_buf;
|
uint8_t *out_buf;
|
||||||
|
@ -2369,7 +2509,7 @@ fail:
|
||||||
|
|
||||||
static int make_completely_empty(BlockDriverState *bs)
|
static int make_completely_empty(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
int ret, l1_clusters;
|
int ret, l1_clusters;
|
||||||
int64_t offset;
|
int64_t offset;
|
||||||
uint64_t *new_reftable = NULL;
|
uint64_t *new_reftable = NULL;
|
||||||
|
@ -2517,7 +2657,7 @@ fail:
|
||||||
|
|
||||||
static int qcow2_make_empty(BlockDriverState *bs)
|
static int qcow2_make_empty(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
uint64_t start_sector;
|
uint64_t start_sector;
|
||||||
int sector_step = INT_MAX / BDRV_SECTOR_SIZE;
|
int sector_step = INT_MAX / BDRV_SECTOR_SIZE;
|
||||||
int l1_clusters, ret = 0;
|
int l1_clusters, ret = 0;
|
||||||
|
@ -2558,7 +2698,7 @@ static int qcow2_make_empty(BlockDriverState *bs)
|
||||||
|
|
||||||
static coroutine_fn int qcow2_co_flush_to_os(BlockDriverState *bs)
|
static coroutine_fn int qcow2_co_flush_to_os(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
qemu_co_mutex_lock(&s->lock);
|
qemu_co_mutex_lock(&s->lock);
|
||||||
|
@ -2582,7 +2722,7 @@ static coroutine_fn int qcow2_co_flush_to_os(BlockDriverState *bs)
|
||||||
|
|
||||||
static int qcow2_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
static int qcow2_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
bdi->unallocated_blocks_are_zero = true;
|
bdi->unallocated_blocks_are_zero = true;
|
||||||
bdi->can_write_zeroes_with_unmap = (s->qcow_version >= 3);
|
bdi->can_write_zeroes_with_unmap = (s->qcow_version >= 3);
|
||||||
bdi->cluster_size = s->cluster_size;
|
bdi->cluster_size = s->cluster_size;
|
||||||
|
@ -2592,7 +2732,7 @@ static int qcow2_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
||||||
|
|
||||||
static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs)
|
static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
ImageInfoSpecific *spec_info = g_new(ImageInfoSpecific, 1);
|
ImageInfoSpecific *spec_info = g_new(ImageInfoSpecific, 1);
|
||||||
|
|
||||||
*spec_info = (ImageInfoSpecific){
|
*spec_info = (ImageInfoSpecific){
|
||||||
|
@ -2625,7 +2765,7 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs)
|
||||||
#if 0
|
#if 0
|
||||||
static void dump_refcounts(BlockDriverState *bs)
|
static void dump_refcounts(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
int64_t nb_clusters, k, k1, size;
|
int64_t nb_clusters, k, k1, size;
|
||||||
int refcount;
|
int refcount;
|
||||||
|
|
||||||
|
@ -2646,7 +2786,7 @@ static void dump_refcounts(BlockDriverState *bs)
|
||||||
static int qcow2_save_vmstate(BlockDriverState *bs, QEMUIOVector *qiov,
|
static int qcow2_save_vmstate(BlockDriverState *bs, QEMUIOVector *qiov,
|
||||||
int64_t pos)
|
int64_t pos)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
int64_t total_sectors = bs->total_sectors;
|
int64_t total_sectors = bs->total_sectors;
|
||||||
bool zero_beyond_eof = bs->zero_beyond_eof;
|
bool zero_beyond_eof = bs->zero_beyond_eof;
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -2667,7 +2807,7 @@ static int qcow2_save_vmstate(BlockDriverState *bs, QEMUIOVector *qiov,
|
||||||
static int qcow2_load_vmstate(BlockDriverState *bs, uint8_t *buf,
|
static int qcow2_load_vmstate(BlockDriverState *bs, uint8_t *buf,
|
||||||
int64_t pos, int size)
|
int64_t pos, int size)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
bool zero_beyond_eof = bs->zero_beyond_eof;
|
bool zero_beyond_eof = bs->zero_beyond_eof;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -2686,7 +2826,7 @@ static int qcow2_load_vmstate(BlockDriverState *bs, uint8_t *buf,
|
||||||
static int qcow2_downgrade(BlockDriverState *bs, int target_version,
|
static int qcow2_downgrade(BlockDriverState *bs, int target_version,
|
||||||
BlockDriverAmendStatusCB *status_cb)
|
BlockDriverAmendStatusCB *status_cb)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
int current_version = s->qcow_version;
|
int current_version = s->qcow_version;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -2750,7 +2890,7 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version,
|
||||||
static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
|
static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
|
||||||
BlockDriverAmendStatusCB *status_cb)
|
BlockDriverAmendStatusCB *status_cb)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
int old_version = s->qcow_version, new_version = old_version;
|
int old_version = s->qcow_version, new_version = old_version;
|
||||||
uint64_t new_size = 0;
|
uint64_t new_size = 0;
|
||||||
const char *backing_file = NULL, *backing_format = NULL;
|
const char *backing_file = NULL, *backing_format = NULL;
|
||||||
|
@ -2897,7 +3037,7 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
|
||||||
void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset,
|
void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset,
|
||||||
int64_t size, const char *message_format, ...)
|
int64_t size, const char *message_format, ...)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcow2State *s = bs->opaque;
|
||||||
const char *node_name;
|
const char *node_name;
|
||||||
char *message;
|
char *message;
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
@ -2998,11 +3138,13 @@ static QemuOptsList qcow2_create_opts = {
|
||||||
|
|
||||||
BlockDriver bdrv_qcow2 = {
|
BlockDriver bdrv_qcow2 = {
|
||||||
.format_name = "qcow2",
|
.format_name = "qcow2",
|
||||||
.instance_size = sizeof(BDRVQcowState),
|
.instance_size = sizeof(BDRVQcow2State),
|
||||||
.bdrv_probe = qcow2_probe,
|
.bdrv_probe = qcow2_probe,
|
||||||
.bdrv_open = qcow2_open,
|
.bdrv_open = qcow2_open,
|
||||||
.bdrv_close = qcow2_close,
|
.bdrv_close = qcow2_close,
|
||||||
.bdrv_reopen_prepare = qcow2_reopen_prepare,
|
.bdrv_reopen_prepare = qcow2_reopen_prepare,
|
||||||
|
.bdrv_reopen_commit = qcow2_reopen_commit,
|
||||||
|
.bdrv_reopen_abort = qcow2_reopen_abort,
|
||||||
.bdrv_create = qcow2_create,
|
.bdrv_create = qcow2_create,
|
||||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||||
.bdrv_co_get_block_status = qcow2_co_get_block_status,
|
.bdrv_co_get_block_status = qcow2_co_get_block_status,
|
||||||
|
|
|
@ -222,7 +222,7 @@ typedef uint64_t Qcow2GetRefcountFunc(const void *refcount_array,
|
||||||
typedef void Qcow2SetRefcountFunc(void *refcount_array,
|
typedef void Qcow2SetRefcountFunc(void *refcount_array,
|
||||||
uint64_t index, uint64_t value);
|
uint64_t index, uint64_t value);
|
||||||
|
|
||||||
typedef struct BDRVQcowState {
|
typedef struct BDRVQcow2State {
|
||||||
int cluster_bits;
|
int cluster_bits;
|
||||||
int cluster_size;
|
int cluster_size;
|
||||||
int cluster_sectors;
|
int cluster_sectors;
|
||||||
|
@ -293,7 +293,7 @@ typedef struct BDRVQcowState {
|
||||||
* override) */
|
* override) */
|
||||||
char *image_backing_file;
|
char *image_backing_file;
|
||||||
char *image_backing_format;
|
char *image_backing_format;
|
||||||
} BDRVQcowState;
|
} BDRVQcow2State;
|
||||||
|
|
||||||
struct QCowAIOCB;
|
struct QCowAIOCB;
|
||||||
|
|
||||||
|
@ -405,28 +405,28 @@ typedef enum QCow2MetadataOverlap {
|
||||||
|
|
||||||
#define REFT_OFFSET_MASK 0xfffffffffffffe00ULL
|
#define REFT_OFFSET_MASK 0xfffffffffffffe00ULL
|
||||||
|
|
||||||
static inline int64_t start_of_cluster(BDRVQcowState *s, int64_t offset)
|
static inline int64_t start_of_cluster(BDRVQcow2State *s, int64_t offset)
|
||||||
{
|
{
|
||||||
return offset & ~(s->cluster_size - 1);
|
return offset & ~(s->cluster_size - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int64_t offset_into_cluster(BDRVQcowState *s, int64_t offset)
|
static inline int64_t offset_into_cluster(BDRVQcow2State *s, int64_t offset)
|
||||||
{
|
{
|
||||||
return offset & (s->cluster_size - 1);
|
return offset & (s->cluster_size - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int size_to_clusters(BDRVQcowState *s, int64_t size)
|
static inline uint64_t size_to_clusters(BDRVQcow2State *s, uint64_t size)
|
||||||
{
|
{
|
||||||
return (size + (s->cluster_size - 1)) >> s->cluster_bits;
|
return (size + (s->cluster_size - 1)) >> s->cluster_bits;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int64_t size_to_l1(BDRVQcowState *s, int64_t size)
|
static inline int64_t size_to_l1(BDRVQcow2State *s, int64_t size)
|
||||||
{
|
{
|
||||||
int shift = s->cluster_bits + s->l2_bits;
|
int shift = s->cluster_bits + s->l2_bits;
|
||||||
return (size + (1ULL << shift) - 1) >> shift;
|
return (size + (1ULL << shift) - 1) >> shift;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int offset_to_l2_index(BDRVQcowState *s, int64_t offset)
|
static inline int offset_to_l2_index(BDRVQcow2State *s, int64_t offset)
|
||||||
{
|
{
|
||||||
return (offset >> s->cluster_bits) & (s->l2_size - 1);
|
return (offset >> s->cluster_bits) & (s->l2_size - 1);
|
||||||
}
|
}
|
||||||
|
@ -437,12 +437,12 @@ static inline int64_t align_offset(int64_t offset, int n)
|
||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int64_t qcow2_vm_state_offset(BDRVQcowState *s)
|
static inline int64_t qcow2_vm_state_offset(BDRVQcow2State *s)
|
||||||
{
|
{
|
||||||
return (int64_t)s->l1_vm_state_index << (s->cluster_bits + s->l2_bits);
|
return (int64_t)s->l1_vm_state_index << (s->cluster_bits + s->l2_bits);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline uint64_t qcow2_max_refcount_clusters(BDRVQcowState *s)
|
static inline uint64_t qcow2_max_refcount_clusters(BDRVQcow2State *s)
|
||||||
{
|
{
|
||||||
return QCOW_MAX_REFTABLE_SIZE >> s->cluster_bits;
|
return QCOW_MAX_REFTABLE_SIZE >> s->cluster_bits;
|
||||||
}
|
}
|
||||||
|
@ -461,7 +461,7 @@ static inline int qcow2_get_cluster_type(uint64_t l2_entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check whether refcounts are eager or lazy */
|
/* Check whether refcounts are eager or lazy */
|
||||||
static inline bool qcow2_need_accurate_refcounts(BDRVQcowState *s)
|
static inline bool qcow2_need_accurate_refcounts(BDRVQcow2State *s)
|
||||||
{
|
{
|
||||||
return !(s->incompatible_features & QCOW2_INCOMPAT_DIRTY);
|
return !(s->incompatible_features & QCOW2_INCOMPAT_DIRTY);
|
||||||
}
|
}
|
||||||
|
@ -509,8 +509,8 @@ int qcow2_update_cluster_refcount(BlockDriverState *bs, int64_t cluster_index,
|
||||||
enum qcow2_discard_type type);
|
enum qcow2_discard_type type);
|
||||||
|
|
||||||
int64_t qcow2_alloc_clusters(BlockDriverState *bs, uint64_t size);
|
int64_t qcow2_alloc_clusters(BlockDriverState *bs, uint64_t size);
|
||||||
int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset,
|
int64_t qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset,
|
||||||
int nb_clusters);
|
int64_t nb_clusters);
|
||||||
int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size);
|
int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size);
|
||||||
void qcow2_free_clusters(BlockDriverState *bs,
|
void qcow2_free_clusters(BlockDriverState *bs,
|
||||||
int64_t offset, int64_t size,
|
int64_t offset, int64_t size,
|
||||||
|
@ -537,7 +537,7 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
|
||||||
int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index);
|
int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index);
|
||||||
void qcow2_l2_cache_reset(BlockDriverState *bs);
|
void qcow2_l2_cache_reset(BlockDriverState *bs);
|
||||||
int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset);
|
int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset);
|
||||||
int qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
|
int qcow2_encrypt_sectors(BDRVQcow2State *s, int64_t sector_num,
|
||||||
uint8_t *out_buf, const uint8_t *in_buf,
|
uint8_t *out_buf, const uint8_t *in_buf,
|
||||||
int nb_sectors, bool enc, Error **errp);
|
int nb_sectors, bool enc, Error **errp);
|
||||||
|
|
||||||
|
|
|
@ -583,7 +583,7 @@ static int qed_create(const char *filename, uint32_t cluster_size,
|
||||||
|
|
||||||
bs = NULL;
|
bs = NULL;
|
||||||
ret = bdrv_open(&bs, filename, NULL, NULL,
|
ret = bdrv_open(&bs, filename, NULL, NULL,
|
||||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL, NULL,
|
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_PROTOCOL,
|
||||||
&local_err);
|
&local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
|
|
|
@ -1554,7 +1554,7 @@ static int sd_prealloc(const char *filename, Error **errp)
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
|
ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
|
||||||
NULL, errp);
|
errp);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto out_with_err_set;
|
goto out_with_err_set;
|
||||||
}
|
}
|
||||||
|
@ -1746,8 +1746,7 @@ static int sd_create(const char *filename, QemuOpts *opts,
|
||||||
}
|
}
|
||||||
|
|
||||||
bs = NULL;
|
bs = NULL;
|
||||||
ret = bdrv_open(&bs, backing_file, NULL, NULL, BDRV_O_PROTOCOL, NULL,
|
ret = bdrv_open(&bs, backing_file, NULL, NULL, BDRV_O_PROTOCOL, errp);
|
||||||
errp);
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
|
@ -764,7 +764,7 @@ static int vdi_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
|
ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
|
||||||
NULL, &local_err);
|
&local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
goto exit;
|
goto exit;
|
||||||
|
|
|
@ -1842,7 +1842,7 @@ static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||||
|
|
||||||
bs = NULL;
|
bs = NULL;
|
||||||
ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
|
ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
|
||||||
NULL, &local_err);
|
&local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
goto exit;
|
goto exit;
|
||||||
|
|
15
block/vmdk.c
15
block/vmdk.c
|
@ -1324,8 +1324,12 @@ static int vmdk_write_extent(VmdkExtent *extent, int64_t cluster_offset,
|
||||||
|
|
||||||
write_end_sector = DIV_ROUND_UP(write_offset + write_len, BDRV_SECTOR_SIZE);
|
write_end_sector = DIV_ROUND_UP(write_offset + write_len, BDRV_SECTOR_SIZE);
|
||||||
|
|
||||||
extent->next_cluster_sector = MAX(extent->next_cluster_sector,
|
if (extent->compressed) {
|
||||||
write_end_sector);
|
extent->next_cluster_sector = write_end_sector;
|
||||||
|
} else {
|
||||||
|
extent->next_cluster_sector = MAX(extent->next_cluster_sector,
|
||||||
|
write_end_sector);
|
||||||
|
}
|
||||||
|
|
||||||
if (ret != write_len) {
|
if (ret != write_len) {
|
||||||
ret = ret < 0 ? ret : -EIO;
|
ret = ret < 0 ? ret : -EIO;
|
||||||
|
@ -1632,7 +1636,7 @@ static int vmdk_create_extent(const char *filename, int64_t filesize,
|
||||||
|
|
||||||
assert(bs == NULL);
|
assert(bs == NULL);
|
||||||
ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
|
ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
|
||||||
NULL, &local_err);
|
&local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
goto exit;
|
goto exit;
|
||||||
|
@ -1905,8 +1909,7 @@ static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||||
ret = -ENOENT;
|
ret = -ENOENT;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
ret = bdrv_open(&bs, full_backing, NULL, NULL, BDRV_O_NO_BACKING, NULL,
|
ret = bdrv_open(&bs, full_backing, NULL, NULL, BDRV_O_NO_BACKING, errp);
|
||||||
errp);
|
|
||||||
g_free(full_backing);
|
g_free(full_backing);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
goto exit;
|
goto exit;
|
||||||
|
@ -1977,7 +1980,7 @@ static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||||
}
|
}
|
||||||
assert(new_bs == NULL);
|
assert(new_bs == NULL);
|
||||||
ret = bdrv_open(&new_bs, filename, NULL, NULL,
|
ret = bdrv_open(&new_bs, filename, NULL, NULL,
|
||||||
BDRV_O_RDWR | BDRV_O_PROTOCOL, NULL, &local_err);
|
BDRV_O_RDWR | BDRV_O_PROTOCOL, &local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
goto exit;
|
goto exit;
|
||||||
|
|
|
@ -794,7 +794,7 @@ static int vpc_create(const char *filename, QemuOpts *opts, Error **errp)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
|
ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL,
|
||||||
NULL, &local_err);
|
&local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
goto out;
|
goto out;
|
||||||
|
|
|
@ -2926,6 +2926,8 @@ static int enable_write_target(BDRVVVFATState *s, Error **errp)
|
||||||
QemuOpts *opts = NULL;
|
QemuOpts *opts = NULL;
|
||||||
int ret;
|
int ret;
|
||||||
int size = sector2cluster(s, s->sector_count);
|
int size = sector2cluster(s, s->sector_count);
|
||||||
|
QDict *options;
|
||||||
|
|
||||||
s->used_clusters = calloc(size, 1);
|
s->used_clusters = calloc(size, 1);
|
||||||
|
|
||||||
array_init(&(s->commits), sizeof(commit_t));
|
array_init(&(s->commits), sizeof(commit_t));
|
||||||
|
@ -2956,9 +2958,11 @@ static int enable_write_target(BDRVVVFATState *s, Error **errp)
|
||||||
}
|
}
|
||||||
|
|
||||||
s->qcow = NULL;
|
s->qcow = NULL;
|
||||||
ret = bdrv_open(&s->qcow, s->qcow_filename, NULL, NULL,
|
options = qdict_new();
|
||||||
|
qdict_put(options, "driver", qstring_from_str("qcow"));
|
||||||
|
ret = bdrv_open(&s->qcow, s->qcow_filename, NULL, options,
|
||||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH,
|
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH,
|
||||||
bdrv_qcow, errp);
|
errp);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
72
blockdev.c
72
blockdev.c
|
@ -1422,9 +1422,8 @@ typedef struct ExternalSnapshotState {
|
||||||
static void external_snapshot_prepare(BlkTransactionState *common,
|
static void external_snapshot_prepare(BlkTransactionState *common,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
BlockDriver *drv;
|
|
||||||
int flags, ret;
|
int flags, ret;
|
||||||
QDict *options = NULL;
|
QDict *options;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
bool has_device = false;
|
bool has_device = false;
|
||||||
const char *device;
|
const char *device;
|
||||||
|
@ -1459,12 +1458,6 @@ static void external_snapshot_prepare(BlkTransactionState *common,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* start processing */
|
/* start processing */
|
||||||
drv = bdrv_find_format(format);
|
|
||||||
if (!drv) {
|
|
||||||
error_setg(errp, QERR_INVALID_BLOCK_FORMAT, format);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
state->old_bs = bdrv_lookup_bs(has_device ? device : NULL,
|
state->old_bs = bdrv_lookup_bs(has_device ? device : NULL,
|
||||||
has_node_name ? node_name : NULL,
|
has_node_name ? node_name : NULL,
|
||||||
&local_err);
|
&local_err);
|
||||||
|
@ -1523,17 +1516,18 @@ static void external_snapshot_prepare(BlkTransactionState *common,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
options = qdict_new();
|
||||||
if (has_snapshot_node_name) {
|
if (has_snapshot_node_name) {
|
||||||
options = qdict_new();
|
|
||||||
qdict_put(options, "node-name",
|
qdict_put(options, "node-name",
|
||||||
qstring_from_str(snapshot_node_name));
|
qstring_from_str(snapshot_node_name));
|
||||||
}
|
}
|
||||||
|
qdict_put(options, "driver", qstring_from_str(format));
|
||||||
|
|
||||||
/* TODO Inherit bs->options or only take explicit options with an
|
/* TODO Inherit bs->options or only take explicit options with an
|
||||||
* extended QMP command? */
|
* extended QMP command? */
|
||||||
assert(state->new_bs == NULL);
|
assert(state->new_bs == NULL);
|
||||||
ret = bdrv_open(&state->new_bs, new_image_file, NULL, options,
|
ret = bdrv_open(&state->new_bs, new_image_file, NULL, options,
|
||||||
flags | BDRV_O_NO_BACKING, drv, &local_err);
|
flags | BDRV_O_NO_BACKING, &local_err);
|
||||||
/* We will manually add the backing_hd field to the bs later */
|
/* We will manually add the backing_hd field to the bs later */
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
|
@ -1895,13 +1889,19 @@ void qmp_block_passwd(bool has_device, const char *device,
|
||||||
|
|
||||||
/* Assumes AioContext is held */
|
/* Assumes AioContext is held */
|
||||||
static void qmp_bdrv_open_encrypted(BlockDriverState *bs, const char *filename,
|
static void qmp_bdrv_open_encrypted(BlockDriverState *bs, const char *filename,
|
||||||
int bdrv_flags, BlockDriver *drv,
|
int bdrv_flags, const char *format,
|
||||||
const char *password, Error **errp)
|
const char *password, Error **errp)
|
||||||
{
|
{
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
|
QDict *options = NULL;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = bdrv_open(&bs, filename, NULL, NULL, bdrv_flags, drv, &local_err);
|
if (format) {
|
||||||
|
options = qdict_new();
|
||||||
|
qdict_put(options, "driver", qstring_from_str(format));
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = bdrv_open(&bs, filename, NULL, options, bdrv_flags, &local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
return;
|
return;
|
||||||
|
@ -1916,7 +1916,6 @@ void qmp_change_blockdev(const char *device, const char *filename,
|
||||||
BlockBackend *blk;
|
BlockBackend *blk;
|
||||||
BlockDriverState *bs;
|
BlockDriverState *bs;
|
||||||
AioContext *aio_context;
|
AioContext *aio_context;
|
||||||
BlockDriver *drv = NULL;
|
|
||||||
int bdrv_flags;
|
int bdrv_flags;
|
||||||
Error *err = NULL;
|
Error *err = NULL;
|
||||||
|
|
||||||
|
@ -1931,14 +1930,6 @@ void qmp_change_blockdev(const char *device, const char *filename,
|
||||||
aio_context = bdrv_get_aio_context(bs);
|
aio_context = bdrv_get_aio_context(bs);
|
||||||
aio_context_acquire(aio_context);
|
aio_context_acquire(aio_context);
|
||||||
|
|
||||||
if (format) {
|
|
||||||
drv = bdrv_find_whitelisted_format(format, bs->read_only);
|
|
||||||
if (!drv) {
|
|
||||||
error_setg(errp, QERR_INVALID_BLOCK_FORMAT, format);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
eject_device(blk, 0, &err);
|
eject_device(blk, 0, &err);
|
||||||
if (err) {
|
if (err) {
|
||||||
error_propagate(errp, err);
|
error_propagate(errp, err);
|
||||||
|
@ -1948,7 +1939,7 @@ void qmp_change_blockdev(const char *device, const char *filename,
|
||||||
bdrv_flags = bdrv_is_read_only(bs) ? 0 : BDRV_O_RDWR;
|
bdrv_flags = bdrv_is_read_only(bs) ? 0 : BDRV_O_RDWR;
|
||||||
bdrv_flags |= bdrv_is_snapshot(bs) ? BDRV_O_SNAPSHOT : 0;
|
bdrv_flags |= bdrv_is_snapshot(bs) ? BDRV_O_SNAPSHOT : 0;
|
||||||
|
|
||||||
qmp_bdrv_open_encrypted(bs, filename, bdrv_flags, drv, NULL, errp);
|
qmp_bdrv_open_encrypted(bs, filename, bdrv_flags, format, NULL, errp);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
aio_context_release(aio_context);
|
aio_context_release(aio_context);
|
||||||
|
@ -2466,7 +2457,7 @@ void qmp_drive_backup(const char *device, const char *target,
|
||||||
BlockDriverState *source = NULL;
|
BlockDriverState *source = NULL;
|
||||||
BdrvDirtyBitmap *bmap = NULL;
|
BdrvDirtyBitmap *bmap = NULL;
|
||||||
AioContext *aio_context;
|
AioContext *aio_context;
|
||||||
BlockDriver *drv = NULL;
|
QDict *options = NULL;
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
int flags;
|
int flags;
|
||||||
int64_t size;
|
int64_t size;
|
||||||
|
@ -2506,13 +2497,6 @@ void qmp_drive_backup(const char *device, const char *target,
|
||||||
if (!has_format) {
|
if (!has_format) {
|
||||||
format = mode == NEW_IMAGE_MODE_EXISTING ? NULL : bs->drv->format_name;
|
format = mode == NEW_IMAGE_MODE_EXISTING ? NULL : bs->drv->format_name;
|
||||||
}
|
}
|
||||||
if (format) {
|
|
||||||
drv = bdrv_find_format(format);
|
|
||||||
if (!drv) {
|
|
||||||
error_setg(errp, QERR_INVALID_BLOCK_FORMAT, format);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Early check to avoid creating target */
|
/* Early check to avoid creating target */
|
||||||
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_BACKUP_SOURCE, errp)) {
|
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_BACKUP_SOURCE, errp)) {
|
||||||
|
@ -2540,7 +2524,7 @@ void qmp_drive_backup(const char *device, const char *target,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mode != NEW_IMAGE_MODE_EXISTING) {
|
if (mode != NEW_IMAGE_MODE_EXISTING) {
|
||||||
assert(format && drv);
|
assert(format);
|
||||||
if (source) {
|
if (source) {
|
||||||
bdrv_img_create(target, format, source->filename,
|
bdrv_img_create(target, format, source->filename,
|
||||||
source->drv->format_name, NULL,
|
source->drv->format_name, NULL,
|
||||||
|
@ -2556,8 +2540,13 @@ void qmp_drive_backup(const char *device, const char *target,
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (format) {
|
||||||
|
options = qdict_new();
|
||||||
|
qdict_put(options, "driver", qstring_from_str(format));
|
||||||
|
}
|
||||||
|
|
||||||
target_bs = NULL;
|
target_bs = NULL;
|
||||||
ret = bdrv_open(&target_bs, target, NULL, NULL, flags, drv, &local_err);
|
ret = bdrv_open(&target_bs, target, NULL, options, flags, &local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -2663,9 +2652,8 @@ void qmp_drive_mirror(const char *device, const char *target,
|
||||||
BlockDriverState *bs;
|
BlockDriverState *bs;
|
||||||
BlockDriverState *source, *target_bs;
|
BlockDriverState *source, *target_bs;
|
||||||
AioContext *aio_context;
|
AioContext *aio_context;
|
||||||
BlockDriver *drv = NULL;
|
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
QDict *options = NULL;
|
QDict *options;
|
||||||
int flags;
|
int flags;
|
||||||
int64_t size;
|
int64_t size;
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -2722,13 +2710,6 @@ void qmp_drive_mirror(const char *device, const char *target,
|
||||||
if (!has_format) {
|
if (!has_format) {
|
||||||
format = mode == NEW_IMAGE_MODE_EXISTING ? NULL : bs->drv->format_name;
|
format = mode == NEW_IMAGE_MODE_EXISTING ? NULL : bs->drv->format_name;
|
||||||
}
|
}
|
||||||
if (format) {
|
|
||||||
drv = bdrv_find_format(format);
|
|
||||||
if (!drv) {
|
|
||||||
error_setg(errp, QERR_INVALID_BLOCK_FORMAT, format);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_MIRROR, errp)) {
|
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_MIRROR, errp)) {
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -2783,7 +2764,7 @@ void qmp_drive_mirror(const char *device, const char *target,
|
||||||
&& mode != NEW_IMAGE_MODE_EXISTING)
|
&& mode != NEW_IMAGE_MODE_EXISTING)
|
||||||
{
|
{
|
||||||
/* create new image w/o backing file */
|
/* create new image w/o backing file */
|
||||||
assert(format && drv);
|
assert(format);
|
||||||
bdrv_img_create(target, format,
|
bdrv_img_create(target, format,
|
||||||
NULL, NULL, NULL, size, flags, &local_err, false);
|
NULL, NULL, NULL, size, flags, &local_err, false);
|
||||||
} else {
|
} else {
|
||||||
|
@ -2807,17 +2788,20 @@ void qmp_drive_mirror(const char *device, const char *target,
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
options = qdict_new();
|
||||||
if (has_node_name) {
|
if (has_node_name) {
|
||||||
options = qdict_new();
|
|
||||||
qdict_put(options, "node-name", qstring_from_str(node_name));
|
qdict_put(options, "node-name", qstring_from_str(node_name));
|
||||||
}
|
}
|
||||||
|
if (format) {
|
||||||
|
qdict_put(options, "driver", qstring_from_str(format));
|
||||||
|
}
|
||||||
|
|
||||||
/* Mirroring takes care of copy-on-write using the source's backing
|
/* Mirroring takes care of copy-on-write using the source's backing
|
||||||
* file.
|
* file.
|
||||||
*/
|
*/
|
||||||
target_bs = NULL;
|
target_bs = NULL;
|
||||||
ret = bdrv_open(&target_bs, target, NULL, options,
|
ret = bdrv_open(&target_bs, target, NULL, options,
|
||||||
flags | BDRV_O_NO_BACKING, drv, &local_err);
|
flags | BDRV_O_NO_BACKING, &local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
goto out;
|
goto out;
|
||||||
|
|
|
@ -147,6 +147,7 @@ typedef QSIMPLEQ_HEAD(BlockReopenQueue, BlockReopenQueueEntry) BlockReopenQueue;
|
||||||
typedef struct BDRVReopenState {
|
typedef struct BDRVReopenState {
|
||||||
BlockDriverState *bs;
|
BlockDriverState *bs;
|
||||||
int flags;
|
int flags;
|
||||||
|
QDict *options;
|
||||||
void *opaque;
|
void *opaque;
|
||||||
} BDRVReopenState;
|
} BDRVReopenState;
|
||||||
|
|
||||||
|
@ -193,8 +194,6 @@ BlockDriver *bdrv_find_protocol(const char *filename,
|
||||||
bool allow_protocol_prefix,
|
bool allow_protocol_prefix,
|
||||||
Error **errp);
|
Error **errp);
|
||||||
BlockDriver *bdrv_find_format(const char *format_name);
|
BlockDriver *bdrv_find_format(const char *format_name);
|
||||||
BlockDriver *bdrv_find_whitelisted_format(const char *format_name,
|
|
||||||
bool readonly);
|
|
||||||
int bdrv_create(BlockDriver *drv, const char* filename,
|
int bdrv_create(BlockDriver *drv, const char* filename,
|
||||||
QemuOpts *opts, Error **errp);
|
QemuOpts *opts, Error **errp);
|
||||||
int bdrv_create_file(const char *filename, QemuOpts *opts, Error **errp);
|
int bdrv_create_file(const char *filename, QemuOpts *opts, Error **errp);
|
||||||
|
@ -218,10 +217,10 @@ void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd);
|
||||||
int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp);
|
int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp);
|
||||||
int bdrv_append_temp_snapshot(BlockDriverState *bs, int flags, Error **errp);
|
int bdrv_append_temp_snapshot(BlockDriverState *bs, int flags, Error **errp);
|
||||||
int bdrv_open(BlockDriverState **pbs, const char *filename,
|
int bdrv_open(BlockDriverState **pbs, const char *filename,
|
||||||
const char *reference, QDict *options, int flags,
|
const char *reference, QDict *options, int flags, Error **errp);
|
||||||
BlockDriver *drv, Error **errp);
|
|
||||||
BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
|
BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
|
||||||
BlockDriverState *bs, int flags);
|
BlockDriverState *bs,
|
||||||
|
QDict *options, int flags);
|
||||||
int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp);
|
int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp);
|
||||||
int bdrv_reopen(BlockDriverState *bs, int bdrv_flags, Error **errp);
|
int bdrv_reopen(BlockDriverState *bs, int bdrv_flags, Error **errp);
|
||||||
int bdrv_reopen_prepare(BDRVReopenState *reopen_state,
|
int bdrv_reopen_prepare(BDRVReopenState *reopen_state,
|
||||||
|
|
|
@ -1979,6 +1979,95 @@ static const cmdinfo_t map_cmd = {
|
||||||
.oneline = "prints the allocated areas of a file",
|
.oneline = "prints the allocated areas of a file",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void reopen_help(void)
|
||||||
|
{
|
||||||
|
printf(
|
||||||
|
"\n"
|
||||||
|
" Changes the open options of an already opened image\n"
|
||||||
|
"\n"
|
||||||
|
" Example:\n"
|
||||||
|
" 'reopen -o lazy-refcounts=on' - activates lazy refcount writeback on a qcow2 image\n"
|
||||||
|
"\n"
|
||||||
|
" -r, -- Reopen the image read-only\n"
|
||||||
|
" -c, -- Change the cache mode to the given value\n"
|
||||||
|
" -o, -- Changes block driver options (cf. 'open' command)\n"
|
||||||
|
"\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int reopen_f(BlockBackend *blk, int argc, char **argv);
|
||||||
|
|
||||||
|
static QemuOptsList reopen_opts = {
|
||||||
|
.name = "reopen",
|
||||||
|
.merge_lists = true,
|
||||||
|
.head = QTAILQ_HEAD_INITIALIZER(reopen_opts.head),
|
||||||
|
.desc = {
|
||||||
|
/* no elements => accept any params */
|
||||||
|
{ /* end of list */ }
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const cmdinfo_t reopen_cmd = {
|
||||||
|
.name = "reopen",
|
||||||
|
.argmin = 0,
|
||||||
|
.argmax = -1,
|
||||||
|
.cfunc = reopen_f,
|
||||||
|
.args = "[-r] [-c cache] [-o options]",
|
||||||
|
.oneline = "reopens an image with new options",
|
||||||
|
.help = reopen_help,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int reopen_f(BlockBackend *blk, int argc, char **argv)
|
||||||
|
{
|
||||||
|
BlockDriverState *bs = blk_bs(blk);
|
||||||
|
QemuOpts *qopts;
|
||||||
|
QDict *opts;
|
||||||
|
int c;
|
||||||
|
int flags = bs->open_flags;
|
||||||
|
|
||||||
|
BlockReopenQueue *brq;
|
||||||
|
Error *local_err = NULL;
|
||||||
|
|
||||||
|
while ((c = getopt(argc, argv, "c:o:r")) != -1) {
|
||||||
|
switch (c) {
|
||||||
|
case 'c':
|
||||||
|
if (bdrv_parse_cache_flags(optarg, &flags) < 0) {
|
||||||
|
error_report("Invalid cache option: %s", optarg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'o':
|
||||||
|
if (!qemu_opts_parse_noisily(&reopen_opts, optarg, 0)) {
|
||||||
|
qemu_opts_reset(&reopen_opts);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
flags &= ~BDRV_O_RDWR;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
qemu_opts_reset(&reopen_opts);
|
||||||
|
return qemuio_command_usage(&reopen_cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (optind != argc) {
|
||||||
|
qemu_opts_reset(&reopen_opts);
|
||||||
|
return qemuio_command_usage(&reopen_cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
qopts = qemu_opts_find(&reopen_opts, NULL);
|
||||||
|
opts = qopts ? qemu_opts_to_qdict(qopts, NULL) : NULL;
|
||||||
|
qemu_opts_reset(&reopen_opts);
|
||||||
|
|
||||||
|
brq = bdrv_reopen_queue(NULL, bs, opts, flags);
|
||||||
|
bdrv_reopen_multiple(brq, &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
error_report_err(local_err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int break_f(BlockBackend *blk, int argc, char **argv)
|
static int break_f(BlockBackend *blk, int argc, char **argv)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -2266,6 +2355,7 @@ static void __attribute((constructor)) init_qemuio_commands(void)
|
||||||
qemuio_add_command(&discard_cmd);
|
qemuio_add_command(&discard_cmd);
|
||||||
qemuio_add_command(&alloc_cmd);
|
qemuio_add_command(&alloc_cmd);
|
||||||
qemuio_add_command(&map_cmd);
|
qemuio_add_command(&map_cmd);
|
||||||
|
qemuio_add_command(&reopen_cmd);
|
||||||
qemuio_add_command(&break_cmd);
|
qemuio_add_command(&break_cmd);
|
||||||
qemuio_add_command(&remove_break_cmd);
|
qemuio_add_command(&remove_break_cmd);
|
||||||
qemuio_add_command(&resume_cmd);
|
qemuio_add_command(&resume_cmd);
|
||||||
|
|
|
@ -156,7 +156,6 @@ static int open_f(BlockBackend *blk, int argc, char **argv)
|
||||||
break;
|
break;
|
||||||
case 'o':
|
case 'o':
|
||||||
if (!qemu_opts_parse_noisily(&empty_opts, optarg, false)) {
|
if (!qemu_opts_parse_noisily(&empty_opts, optarg, false)) {
|
||||||
printf("could not parse option list -- %s\n", optarg);
|
|
||||||
qemu_opts_reset(&empty_opts);
|
qemu_opts_reset(&empty_opts);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -147,6 +147,33 @@ $PYTHON qcow2.py "$TEST_IMG".base dump-header | grep incompatible_features
|
||||||
_check_test_img
|
_check_test_img
|
||||||
TEST_IMG="$TEST_IMG".base _check_test_img
|
TEST_IMG="$TEST_IMG".base _check_test_img
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "== Changing lazy_refcounts setting at runtime =="
|
||||||
|
|
||||||
|
IMGOPTS="compat=1.1,lazy_refcounts=off"
|
||||||
|
_make_test_img $size
|
||||||
|
|
||||||
|
$QEMU_IO -c "reopen -o lazy-refcounts=on" \
|
||||||
|
-c "write -P 0x5a 0 512" \
|
||||||
|
-c "sigraise $(kill -l KILL)" "$TEST_IMG" 2>&1 \
|
||||||
|
| _filter_qemu_io
|
||||||
|
|
||||||
|
# The dirty bit must be set
|
||||||
|
$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||||
|
_check_test_img
|
||||||
|
|
||||||
|
IMGOPTS="compat=1.1,lazy_refcounts=on"
|
||||||
|
_make_test_img $size
|
||||||
|
|
||||||
|
$QEMU_IO -c "reopen -o lazy-refcounts=off" \
|
||||||
|
-c "write -P 0x5a 0 512" \
|
||||||
|
-c "sigraise $(kill -l KILL)" "$TEST_IMG" 2>&1 \
|
||||||
|
| _filter_qemu_io
|
||||||
|
|
||||||
|
# The dirty bit must not be set
|
||||||
|
$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||||
|
_check_test_img
|
||||||
|
|
||||||
|
|
||||||
# success, all done
|
# success, all done
|
||||||
echo "*** done"
|
echo "*** done"
|
||||||
|
|
|
@ -74,4 +74,22 @@ incompatible_features 0x0
|
||||||
incompatible_features 0x0
|
incompatible_features 0x0
|
||||||
No errors were found on the image.
|
No errors were found on the image.
|
||||||
No errors were found on the image.
|
No errors were found on the image.
|
||||||
|
|
||||||
|
== Changing lazy_refcounts setting at runtime ==
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
|
||||||
|
wrote 512/512 bytes at offset 0
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
./common.config: Killed ( exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@" )
|
||||||
|
incompatible_features 0x1
|
||||||
|
ERROR cluster 5 refcount=0 reference=1
|
||||||
|
ERROR OFLAG_COPIED data cluster: l2_entry=8000000000050000 refcount=0
|
||||||
|
|
||||||
|
2 errors were found on the image.
|
||||||
|
Data may be corrupted, or further writes to the image may corrupt it.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
|
||||||
|
wrote 512/512 bytes at offset 0
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
./common.config: Killed ( exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@" )
|
||||||
|
incompatible_features 0x0
|
||||||
|
No errors were found on the image.
|
||||||
*** done
|
*** done
|
||||||
|
|
|
@ -0,0 +1,145 @@
|
||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Test qcow2 reopen
|
||||||
|
#
|
||||||
|
# Copyright (C) 2015 Red Hat, Inc.
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
|
||||||
|
# creator
|
||||||
|
owner=kwolf@redhat.com
|
||||||
|
|
||||||
|
seq="$(basename $0)"
|
||||||
|
echo "QA output created by $seq"
|
||||||
|
|
||||||
|
here="$PWD"
|
||||||
|
tmp=/tmp/$$
|
||||||
|
status=1 # failure is the default!
|
||||||
|
|
||||||
|
_cleanup()
|
||||||
|
{
|
||||||
|
_cleanup_test_img
|
||||||
|
}
|
||||||
|
trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||||
|
|
||||||
|
# get standard environment, filters and checks
|
||||||
|
. ./common.rc
|
||||||
|
. ./common.filter
|
||||||
|
. ./common.qemu
|
||||||
|
|
||||||
|
_supported_fmt qcow2
|
||||||
|
_supported_proto generic
|
||||||
|
_supported_os Linux
|
||||||
|
|
||||||
|
|
||||||
|
_make_test_img 64M
|
||||||
|
|
||||||
|
echo === Try setting valid values for all options ===
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Try all options and then check that all of the basic I/O operations still
|
||||||
|
# work on this image.
|
||||||
|
$QEMU_IO \
|
||||||
|
-c "reopen -o lazy-refcounts=on,pass-discard-request=on" \
|
||||||
|
-c "reopen -o lazy-refcounts=off,pass-discard-request=off" \
|
||||||
|
-c "reopen -o pass-discard-snapshot=on,pass-discard-other=on" \
|
||||||
|
-c "reopen -o pass-discard-snapshot=off,pass-discard-other=off" \
|
||||||
|
-c "reopen -o overlap-check=all" \
|
||||||
|
-c "reopen -o overlap-check=none" \
|
||||||
|
-c "reopen -o overlap-check=cached" \
|
||||||
|
-c "reopen -o overlap-check=constant" \
|
||||||
|
-c "reopen -o overlap-check.template=all" \
|
||||||
|
-c "reopen -o overlap-check.template=none" \
|
||||||
|
-c "reopen -o overlap-check.template=cached" \
|
||||||
|
-c "reopen -o overlap-check.template=constant" \
|
||||||
|
-c "reopen -o overlap-check.main-header=on" \
|
||||||
|
-c "reopen -o overlap-check.main-header=off" \
|
||||||
|
-c "reopen -o overlap-check.active-l1=on" \
|
||||||
|
-c "reopen -o overlap-check.active-l1=off" \
|
||||||
|
-c "reopen -o overlap-check.active-l2=on" \
|
||||||
|
-c "reopen -o overlap-check.active-l2=off" \
|
||||||
|
-c "reopen -o overlap-check.refcount-table=on" \
|
||||||
|
-c "reopen -o overlap-check.refcount-table=off" \
|
||||||
|
-c "reopen -o overlap-check.refcount-block=on" \
|
||||||
|
-c "reopen -o overlap-check.refcount-block=off" \
|
||||||
|
-c "reopen -o overlap-check.snapshot-table=on" \
|
||||||
|
-c "reopen -o overlap-check.snapshot-table=off" \
|
||||||
|
-c "reopen -o overlap-check.inactive-l1=on" \
|
||||||
|
-c "reopen -o overlap-check.inactive-l1=off" \
|
||||||
|
-c "reopen -o overlap-check.inactive-l2=on" \
|
||||||
|
-c "reopen -o overlap-check.inactive-l2=off" \
|
||||||
|
-c "reopen -o cache-size=1M" \
|
||||||
|
-c "reopen -o l2-cache-size=512k" \
|
||||||
|
-c "reopen -o refcount-cache-size=128k" \
|
||||||
|
-c "reopen -o cache-clean-interval=5" \
|
||||||
|
-c "reopen -o cache-clean-interval=0" \
|
||||||
|
-c "reopen -o cache-clean-interval=10" \
|
||||||
|
\
|
||||||
|
-c "write -P 55 0 32M" \
|
||||||
|
-c "read -P 55 0 32M" \
|
||||||
|
-c "discard 0 32M" \
|
||||||
|
-c "write -z 0 32M" \
|
||||||
|
-c "read -P 0 0 32M" \
|
||||||
|
\
|
||||||
|
"$TEST_IMG" | _filter_qemu_io
|
||||||
|
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo === Try setting some invalid values ===
|
||||||
|
echo
|
||||||
|
|
||||||
|
$QEMU_IO \
|
||||||
|
-c "reopen -o lazy-refcounts=42" \
|
||||||
|
-c "reopen -o cache-size=1M,l2-cache-size=64k,refcount-cache-size=64k" \
|
||||||
|
-c "reopen -o cache-size=1M,l2-cache-size=2M" \
|
||||||
|
-c "reopen -o cache-size=1M,refcount-cache-size=2M" \
|
||||||
|
-c "reopen -o l2-cache-size=256T" \
|
||||||
|
-c "reopen -o refcount-cache-size=256T" \
|
||||||
|
-c "reopen -o overlap-check=constant,overlap-check.template=all" \
|
||||||
|
-c "reopen -o overlap-check=blubb" \
|
||||||
|
-c "reopen -o overlap-check.template=blubb" \
|
||||||
|
-c "reopen -o cache-clean-interval=-1" \
|
||||||
|
"$TEST_IMG" | _filter_qemu_io
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo === Test transaction semantics ===
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Whether lazy-refcounts was actually enabled can easily be tested: Check if
|
||||||
|
# the dirty bit is set after a crash
|
||||||
|
$QEMU_IO \
|
||||||
|
-c "reopen -o lazy-refcounts=on,overlap-check=blubb" \
|
||||||
|
-c "write -P 0x5a 0 512" \
|
||||||
|
-c "sigraise $(kill -l KILL)" \
|
||||||
|
"$TEST_IMG" 2>&1 | _filter_qemu_io
|
||||||
|
|
||||||
|
# The dirty bit must not be set
|
||||||
|
$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
|
||||||
|
|
||||||
|
# Similarly we can test whether corruption detection has been enabled:
|
||||||
|
# Create L1/L2, overwrite first entry in refcount block, allocate something.
|
||||||
|
# Disabling the checks should fail, so the corruption must be detected.
|
||||||
|
_make_test_img 64M
|
||||||
|
$QEMU_IO -c "write 0 64k" "$TEST_IMG" | _filter_qemu_io
|
||||||
|
poke_file "$TEST_IMG" "$((0x20000))" "\x00\x00"
|
||||||
|
$QEMU_IO \
|
||||||
|
-c "reopen -o overlap-check=none,lazy-refcounts=42" \
|
||||||
|
-c "write 64k 64k" \
|
||||||
|
"$TEST_IMG" 2>&1 | _filter_qemu_io
|
||||||
|
|
||||||
|
# success, all done
|
||||||
|
echo '*** done'
|
||||||
|
rm -f $seq.full
|
||||||
|
status=0
|
|
@ -0,0 +1,42 @@
|
||||||
|
QA output created by 137
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
||||||
|
=== Try setting valid values for all options ===
|
||||||
|
|
||||||
|
wrote 33554432/33554432 bytes at offset 0
|
||||||
|
32 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 33554432/33554432 bytes at offset 0
|
||||||
|
32 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
discard 33554432/33554432 bytes at offset 0
|
||||||
|
32 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
wrote 33554432/33554432 bytes at offset 0
|
||||||
|
32 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
read 33554432/33554432 bytes at offset 0
|
||||||
|
32 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
|
||||||
|
=== Try setting some invalid values ===
|
||||||
|
|
||||||
|
Parameter 'lazy-refcounts' expects 'on' or 'off'
|
||||||
|
cache-size, l2-cache-size and refcount-cache-size may not be set the same time
|
||||||
|
l2-cache-size may not exceed cache-size
|
||||||
|
refcount-cache-size may not exceed cache-size
|
||||||
|
L2 cache size too big
|
||||||
|
L2 cache size too big
|
||||||
|
Conflicting values for qcow2 options 'overlap-check' ('constant') and 'overlap-check.template' ('all')
|
||||||
|
Unsupported value 'blubb' for qcow2 option 'overlap-check'. Allowed are any of the following: none, constant, cached, all
|
||||||
|
Unsupported value 'blubb' for qcow2 option 'overlap-check'. Allowed are any of the following: none, constant, cached, all
|
||||||
|
Cache clean interval too big
|
||||||
|
|
||||||
|
=== Test transaction semantics ===
|
||||||
|
|
||||||
|
Unsupported value 'blubb' for qcow2 option 'overlap-check'. Allowed are any of the following: none, constant, cached, all
|
||||||
|
wrote 512/512 bytes at offset 0
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
./common.config: Killed ( exec "$QEMU_IO_PROG" $QEMU_IO_OPTIONS "$@" )
|
||||||
|
incompatible_features 0x0
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
||||||
|
wrote 65536/65536 bytes at offset 0
|
||||||
|
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
Parameter 'lazy-refcounts' expects 'on' or 'off'
|
||||||
|
qcow2: Marking image as corrupt: Preventing invalid write on metadata (overlaps with qcow2_header); further corruption events will be suppressed
|
||||||
|
write failed: Input/output error
|
||||||
|
*** done
|
|
@ -0,0 +1,73 @@
|
||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# General test case for qcow2's image check
|
||||||
|
#
|
||||||
|
# Copyright (C) 2015 Red Hat, Inc.
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
|
||||||
|
# creator
|
||||||
|
owner=mreitz@redhat.com
|
||||||
|
|
||||||
|
seq="$(basename $0)"
|
||||||
|
echo "QA output created by $seq"
|
||||||
|
|
||||||
|
here="$PWD"
|
||||||
|
tmp=/tmp/$$
|
||||||
|
status=1 # failure is the default!
|
||||||
|
|
||||||
|
_cleanup()
|
||||||
|
{
|
||||||
|
_cleanup_test_img
|
||||||
|
}
|
||||||
|
trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||||
|
|
||||||
|
# get standard environment, filters and checks
|
||||||
|
. ./common.rc
|
||||||
|
. ./common.filter
|
||||||
|
|
||||||
|
# This tests qocw2-specific low-level functionality
|
||||||
|
_supported_fmt qcow2
|
||||||
|
_supported_proto file
|
||||||
|
_supported_os Linux
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo '=== Check on an image with a multiple of 2^32 clusters ==='
|
||||||
|
echo
|
||||||
|
|
||||||
|
IMGOPTS=$(_optstr_add "$IMGOPTS" "cluster_size=512") \
|
||||||
|
_make_test_img 512
|
||||||
|
|
||||||
|
# Allocate L2 table
|
||||||
|
$QEMU_IO -c 'write 0 512' "$TEST_IMG" | _filter_qemu_io
|
||||||
|
|
||||||
|
# Put the data cluster at a multiple of 2 TB, resulting in the image apparently
|
||||||
|
# having a multiple of 2^32 clusters
|
||||||
|
# (To be more specific: It is at 32 PB)
|
||||||
|
poke_file "$TEST_IMG" 2048 "\x80\x80\x00\x00\x00\x00\x00\x00"
|
||||||
|
|
||||||
|
# An offset of 32 PB results in qemu-img check having to allocate an in-memory
|
||||||
|
# refcount table of 128 TB (16 bit refcounts, 512 byte clusters).
|
||||||
|
# This should be generally too much for any system and thus fail.
|
||||||
|
# What this test is checking is that the qcow2 driver actually tries to allocate
|
||||||
|
# such a large amount of memory (and is consequently aborting) instead of having
|
||||||
|
# truncated the cluster count somewhere (which would result in much less memory
|
||||||
|
# being allocated and then a segfault occurring).
|
||||||
|
_check_test_img
|
||||||
|
|
||||||
|
# success, all done
|
||||||
|
echo "*** done"
|
||||||
|
rm -f $seq.full
|
||||||
|
status=0
|
|
@ -0,0 +1,9 @@
|
||||||
|
QA output created by 138
|
||||||
|
|
||||||
|
=== Check on an image with a multiple of 2^32 clusters ===
|
||||||
|
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=512
|
||||||
|
wrote 512/512 bytes at offset 0
|
||||||
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
qemu-img: Check failed: Cannot allocate memory
|
||||||
|
*** done
|
|
@ -134,3 +134,5 @@
|
||||||
132 rw auto quick
|
132 rw auto quick
|
||||||
134 rw auto quick
|
134 rw auto quick
|
||||||
135 rw auto
|
135 rw auto
|
||||||
|
137 rw auto
|
||||||
|
138 rw auto quick
|
||||||
|
|
Loading…
Reference in New Issue