mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge remote-tracking branch 'kwolf/for-anthony' into staging
# By Max Reitz (16) and others # Via Kevin Wolf * kwolf/for-anthony: (33 commits) qemu-iotests: Fix test 038 block: Assert validity of BdrvActionOps qemu-iotests: Cleanup test image in test number 007 qemu-img: fix invalid JSON coroutine: add ./configure --disable-coroutine-pool qemu-iotests: Adjustments due to error propagation qcow2: Use Error parameter qemu-img create: Emit filename on error block: Error parameter for create functions block: Error parameter for open functions bdrv: Use "Error" for creating images bdrv: Use "Error" for opening images qemu-iotests: add 057 internal snapshot for block device test case hmp: add interface hmp_snapshot_delete_blkdev_internal hmp: add interface hmp_snapshot_blkdev_internal qmp: add interface blockdev-snapshot-delete-internal-sync qmp: add interface blockdev-snapshot-internal-sync qmp: add internal snapshot support in qmp_transaction snapshot: distinguish id and name in snapshot delete snapshot: new function bdrv_snapshot_find_by_id_and_name() ... Message-id: 1379073063-14963-1-git-send-email-kwolf@redhat.com
This commit is contained in:
@@ -188,3 +188,9 @@ class QEMUMonitorProtocol:
|
||||
|
||||
def settimeout(self, timeout):
|
||||
self.__sock.settimeout(timeout)
|
||||
|
||||
def get_sock_fd(self):
|
||||
return self.__sock.fileno()
|
||||
|
||||
def is_scm_available(self):
|
||||
return self.__sock.family == socket.AF_UNIX
|
||||
|
||||
@@ -394,18 +394,26 @@ typedef struct CreateCo {
|
||||
char *filename;
|
||||
QEMUOptionParameter *options;
|
||||
int ret;
|
||||
Error *err;
|
||||
} CreateCo;
|
||||
|
||||
static void coroutine_fn bdrv_create_co_entry(void *opaque)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
CreateCo *cco = opaque;
|
||||
assert(cco->drv);
|
||||
|
||||
cco->ret = cco->drv->bdrv_create(cco->filename, cco->options);
|
||||
ret = cco->drv->bdrv_create(cco->filename, cco->options, &local_err);
|
||||
if (error_is_set(&local_err)) {
|
||||
error_propagate(&cco->err, local_err);
|
||||
}
|
||||
cco->ret = ret;
|
||||
}
|
||||
|
||||
int bdrv_create(BlockDriver *drv, const char* filename,
|
||||
QEMUOptionParameter *options)
|
||||
QEMUOptionParameter *options, Error **errp)
|
||||
{
|
||||
int ret;
|
||||
|
||||
@@ -415,9 +423,11 @@ int bdrv_create(BlockDriver *drv, const char* filename,
|
||||
.filename = g_strdup(filename),
|
||||
.options = options,
|
||||
.ret = NOT_DONE,
|
||||
.err = NULL,
|
||||
};
|
||||
|
||||
if (!drv->bdrv_create) {
|
||||
error_setg(errp, "Driver '%s' does not support image creation", drv->format_name);
|
||||
ret = -ENOTSUP;
|
||||
goto out;
|
||||
}
|
||||
@@ -434,22 +444,37 @@ int bdrv_create(BlockDriver *drv, const char* filename,
|
||||
}
|
||||
|
||||
ret = cco.ret;
|
||||
if (ret < 0) {
|
||||
if (error_is_set(&cco.err)) {
|
||||
error_propagate(errp, cco.err);
|
||||
} else {
|
||||
error_setg_errno(errp, -ret, "Could not create image");
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
g_free(cco.filename);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int bdrv_create_file(const char* filename, QEMUOptionParameter *options)
|
||||
int bdrv_create_file(const char* filename, QEMUOptionParameter *options,
|
||||
Error **errp)
|
||||
{
|
||||
BlockDriver *drv;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
drv = bdrv_find_protocol(filename, true);
|
||||
if (drv == NULL) {
|
||||
error_setg(errp, "Could not find protocol for file '%s'", filename);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
return bdrv_create(drv, filename, options);
|
||||
ret = bdrv_create(drv, filename, options, &local_err);
|
||||
if (error_is_set(&local_err)) {
|
||||
error_propagate(errp, local_err);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -552,7 +577,7 @@ BlockDriver *bdrv_find_protocol(const char *filename,
|
||||
}
|
||||
|
||||
static int find_image_format(BlockDriverState *bs, const char *filename,
|
||||
BlockDriver **pdrv)
|
||||
BlockDriver **pdrv, Error **errp)
|
||||
{
|
||||
int score, score_max;
|
||||
BlockDriver *drv1, *drv;
|
||||
@@ -563,6 +588,7 @@ static int find_image_format(BlockDriverState *bs, const char *filename,
|
||||
if (bs->sg || !bdrv_is_inserted(bs) || bdrv_getlength(bs) == 0) {
|
||||
drv = bdrv_find_format("raw");
|
||||
if (!drv) {
|
||||
error_setg(errp, "Could not find raw image format");
|
||||
ret = -ENOENT;
|
||||
}
|
||||
*pdrv = drv;
|
||||
@@ -571,6 +597,8 @@ static int find_image_format(BlockDriverState *bs, const char *filename,
|
||||
|
||||
ret = bdrv_pread(bs, 0, buf, sizeof(buf));
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not read image for determining its "
|
||||
"format");
|
||||
*pdrv = NULL;
|
||||
return ret;
|
||||
}
|
||||
@@ -587,6 +615,8 @@ static int find_image_format(BlockDriverState *bs, const char *filename,
|
||||
}
|
||||
}
|
||||
if (!drv) {
|
||||
error_setg(errp, "Could not determine image format: No compatible "
|
||||
"driver found");
|
||||
ret = -ENOENT;
|
||||
}
|
||||
*pdrv = drv;
|
||||
@@ -706,10 +736,11 @@ static int bdrv_open_flags(BlockDriverState *bs, int flags)
|
||||
* Removes all processed options from *options.
|
||||
*/
|
||||
static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
|
||||
QDict *options, int flags, BlockDriver *drv)
|
||||
QDict *options, int flags, BlockDriver *drv, Error **errp)
|
||||
{
|
||||
int ret, open_flags;
|
||||
const char *filename;
|
||||
Error *local_err = NULL;
|
||||
|
||||
assert(drv != NULL);
|
||||
assert(bs->file == NULL);
|
||||
@@ -738,6 +769,7 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
|
||||
bs->read_only = !(open_flags & BDRV_O_RDWR);
|
||||
|
||||
if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, bs->read_only)) {
|
||||
error_setg(errp, "Driver '%s' is not whitelisted", drv->format_name);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
@@ -761,25 +793,32 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
|
||||
if (drv->bdrv_file_open) {
|
||||
assert(file == NULL);
|
||||
assert(drv->bdrv_parse_filename || filename != NULL);
|
||||
ret = drv->bdrv_file_open(bs, options, open_flags);
|
||||
ret = drv->bdrv_file_open(bs, options, open_flags, &local_err);
|
||||
} else {
|
||||
if (file == NULL) {
|
||||
qerror_report(ERROR_CLASS_GENERIC_ERROR, "Can't use '%s' as a "
|
||||
"block driver for the protocol level",
|
||||
drv->format_name);
|
||||
error_setg(errp, "Can't use '%s' as a block driver for the "
|
||||
"protocol level", drv->format_name);
|
||||
ret = -EINVAL;
|
||||
goto free_and_fail;
|
||||
}
|
||||
bs->file = file;
|
||||
ret = drv->bdrv_open(bs, options, open_flags);
|
||||
ret = drv->bdrv_open(bs, options, open_flags, &local_err);
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
if (error_is_set(&local_err)) {
|
||||
error_propagate(errp, local_err);
|
||||
} else if (filename) {
|
||||
error_setg_errno(errp, -ret, "Could not open '%s'", filename);
|
||||
} else {
|
||||
error_setg_errno(errp, -ret, "Could not open image");
|
||||
}
|
||||
goto free_and_fail;
|
||||
}
|
||||
|
||||
ret = refresh_total_sectors(bs, bs->total_sectors);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not refresh total sector count");
|
||||
goto free_and_fail;
|
||||
}
|
||||
|
||||
@@ -808,12 +847,13 @@ free_and_fail:
|
||||
* dictionary, it needs to use QINCREF() before calling bdrv_file_open.
|
||||
*/
|
||||
int bdrv_file_open(BlockDriverState **pbs, const char *filename,
|
||||
QDict *options, int flags)
|
||||
QDict *options, int flags, Error **errp)
|
||||
{
|
||||
BlockDriverState *bs;
|
||||
BlockDriver *drv;
|
||||
const char *drvname;
|
||||
bool allow_protocol_prefix = false;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
/* NULL means an empty set of options */
|
||||
@@ -832,8 +872,8 @@ int bdrv_file_open(BlockDriverState **pbs, const char *filename,
|
||||
qdict_put(options, "filename", qstring_from_str(filename));
|
||||
allow_protocol_prefix = true;
|
||||
} else {
|
||||
qerror_report(ERROR_CLASS_GENERIC_ERROR, "Can't specify 'file' and "
|
||||
"'filename' options at the same time");
|
||||
error_setg(errp, "Can't specify 'file' and 'filename' options at the "
|
||||
"same time");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
@@ -842,53 +882,53 @@ int bdrv_file_open(BlockDriverState **pbs, const char *filename,
|
||||
drvname = qdict_get_try_str(options, "driver");
|
||||
if (drvname) {
|
||||
drv = bdrv_find_whitelisted_format(drvname, !(flags & BDRV_O_RDWR));
|
||||
if (!drv) {
|
||||
error_setg(errp, "Unknown driver '%s'", drvname);
|
||||
}
|
||||
qdict_del(options, "driver");
|
||||
} else if (filename) {
|
||||
drv = bdrv_find_protocol(filename, allow_protocol_prefix);
|
||||
if (!drv) {
|
||||
qerror_report(ERROR_CLASS_GENERIC_ERROR, "Unknown protocol");
|
||||
error_setg(errp, "Unknown protocol");
|
||||
}
|
||||
} else {
|
||||
qerror_report(ERROR_CLASS_GENERIC_ERROR,
|
||||
"Must specify either driver or file");
|
||||
error_setg(errp, "Must specify either driver or file");
|
||||
drv = NULL;
|
||||
}
|
||||
|
||||
if (!drv) {
|
||||
/* errp has been set already */
|
||||
ret = -ENOENT;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Parse the filename and open it */
|
||||
if (drv->bdrv_parse_filename && filename) {
|
||||
Error *local_err = NULL;
|
||||
drv->bdrv_parse_filename(filename, options, &local_err);
|
||||
if (error_is_set(&local_err)) {
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
qdict_del(options, "filename");
|
||||
} else if (!drv->bdrv_parse_filename && !filename) {
|
||||
qerror_report(ERROR_CLASS_GENERIC_ERROR,
|
||||
"The '%s' block driver requires a file name",
|
||||
drv->format_name);
|
||||
error_setg(errp, "The '%s' block driver requires a file name",
|
||||
drv->format_name);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = bdrv_open_common(bs, NULL, options, flags, drv);
|
||||
ret = bdrv_open_common(bs, NULL, options, flags, drv, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Check if any unknown options were used */
|
||||
if (qdict_size(options) != 0) {
|
||||
const QDictEntry *entry = qdict_first(options);
|
||||
qerror_report(ERROR_CLASS_GENERIC_ERROR, "Block protocol '%s' doesn't "
|
||||
"support the option '%s'",
|
||||
drv->format_name, entry->key);
|
||||
error_setg(errp, "Block protocol '%s' doesn't support the option '%s'",
|
||||
drv->format_name, entry->key);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
@@ -915,11 +955,12 @@ fail:
|
||||
* function (even on failure), so if the caller intends to reuse the dictionary,
|
||||
* it needs to use QINCREF() before calling bdrv_file_open.
|
||||
*/
|
||||
int bdrv_open_backing_file(BlockDriverState *bs, QDict *options)
|
||||
int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp)
|
||||
{
|
||||
char backing_filename[PATH_MAX];
|
||||
int back_flags, ret;
|
||||
BlockDriver *back_drv = NULL;
|
||||
Error *local_err = NULL;
|
||||
|
||||
if (bs->backing_hd != NULL) {
|
||||
QDECREF(options);
|
||||
@@ -952,11 +993,12 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options)
|
||||
|
||||
ret = bdrv_open(bs->backing_hd,
|
||||
*backing_filename ? backing_filename : NULL, options,
|
||||
back_flags, back_drv);
|
||||
back_flags, back_drv, &local_err);
|
||||
if (ret < 0) {
|
||||
bdrv_unref(bs->backing_hd);
|
||||
bs->backing_hd = NULL;
|
||||
bs->open_flags |= BDRV_O_NO_BACKING;
|
||||
error_propagate(errp, local_err);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
@@ -990,7 +1032,7 @@ static void extract_subqdict(QDict *src, QDict **dst, const char *start)
|
||||
* dictionary, it needs to use QINCREF() before calling bdrv_open.
|
||||
*/
|
||||
int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
|
||||
int flags, BlockDriver *drv)
|
||||
int flags, BlockDriver *drv, Error **errp)
|
||||
{
|
||||
int ret;
|
||||
/* TODO: extra byte is a hack to ensure MAX_PATH space on Windows. */
|
||||
@@ -998,6 +1040,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
|
||||
BlockDriverState *file = NULL;
|
||||
QDict *file_options = NULL;
|
||||
const char *drvname;
|
||||
Error *local_err = NULL;
|
||||
|
||||
/* NULL means an empty set of options */
|
||||
if (options == NULL) {
|
||||
@@ -1016,7 +1059,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
|
||||
char backing_filename[PATH_MAX];
|
||||
|
||||
if (qdict_size(options) != 0) {
|
||||
error_report("Can't use snapshot=on with driver-specific options");
|
||||
error_setg(errp, "Can't use snapshot=on with driver-specific options");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
@@ -1027,7 +1070,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
|
||||
|
||||
/* if there is a backing file, use it */
|
||||
bs1 = bdrv_new("");
|
||||
ret = bdrv_open(bs1, filename, NULL, 0, drv);
|
||||
ret = bdrv_open(bs1, filename, NULL, 0, drv, &local_err);
|
||||
if (ret < 0) {
|
||||
bdrv_unref(bs1);
|
||||
goto fail;
|
||||
@@ -1038,6 +1081,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
|
||||
|
||||
ret = get_tmp_filename(tmp_filename, sizeof(tmp_filename));
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not get temporary filename");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@@ -1046,6 +1090,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
|
||||
snprintf(backing_filename, sizeof(backing_filename),
|
||||
"%s", filename);
|
||||
} else if (!realpath(filename, backing_filename)) {
|
||||
error_setg_errno(errp, errno, "Could not resolve path '%s'", filename);
|
||||
ret = -errno;
|
||||
goto fail;
|
||||
}
|
||||
@@ -1062,9 +1107,14 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
|
||||
drv->format_name);
|
||||
}
|
||||
|
||||
ret = bdrv_create(bdrv_qcow2, tmp_filename, create_options);
|
||||
ret = bdrv_create(bdrv_qcow2, tmp_filename, create_options, &local_err);
|
||||
free_option_parameters(create_options);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not create temporary overlay "
|
||||
"'%s': %s", tmp_filename,
|
||||
error_get_pretty(local_err));
|
||||
error_free(local_err);
|
||||
local_err = NULL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@@ -1081,7 +1131,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
|
||||
extract_subqdict(options, &file_options, "file.");
|
||||
|
||||
ret = bdrv_file_open(&file, filename, file_options,
|
||||
bdrv_open_flags(bs, flags | BDRV_O_UNMAP));
|
||||
bdrv_open_flags(bs, flags | BDRV_O_UNMAP), &local_err);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -1094,7 +1144,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
|
||||
}
|
||||
|
||||
if (!drv) {
|
||||
ret = find_image_format(file, filename, &drv);
|
||||
ret = find_image_format(file, filename, &drv, &local_err);
|
||||
}
|
||||
|
||||
if (!drv) {
|
||||
@@ -1102,7 +1152,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
|
||||
}
|
||||
|
||||
/* Open the image */
|
||||
ret = bdrv_open_common(bs, file, options, flags, drv);
|
||||
ret = bdrv_open_common(bs, file, options, flags, drv, &local_err);
|
||||
if (ret < 0) {
|
||||
goto unlink_and_fail;
|
||||
}
|
||||
@@ -1117,7 +1167,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
|
||||
QDict *backing_options;
|
||||
|
||||
extract_subqdict(options, &backing_options, "backing.");
|
||||
ret = bdrv_open_backing_file(bs, backing_options);
|
||||
ret = bdrv_open_backing_file(bs, backing_options, &local_err);
|
||||
if (ret < 0) {
|
||||
goto close_and_fail;
|
||||
}
|
||||
@@ -1126,9 +1176,9 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
|
||||
/* Check if any unknown options were used */
|
||||
if (qdict_size(options) != 0) {
|
||||
const QDictEntry *entry = qdict_first(options);
|
||||
qerror_report(ERROR_CLASS_GENERIC_ERROR, "Block format '%s' used by "
|
||||
"device '%s' doesn't support the option '%s'",
|
||||
drv->format_name, bs->device_name, entry->key);
|
||||
error_setg(errp, "Block format '%s' used by device '%s' doesn't "
|
||||
"support the option '%s'", drv->format_name, bs->device_name,
|
||||
entry->key);
|
||||
|
||||
ret = -EINVAL;
|
||||
goto close_and_fail;
|
||||
@@ -1152,11 +1202,17 @@ fail:
|
||||
QDECREF(bs->options);
|
||||
QDECREF(options);
|
||||
bs->options = NULL;
|
||||
if (error_is_set(&local_err)) {
|
||||
error_propagate(errp, local_err);
|
||||
}
|
||||
return ret;
|
||||
|
||||
close_and_fail:
|
||||
bdrv_close(bs);
|
||||
QDECREF(options);
|
||||
if (error_is_set(&local_err)) {
|
||||
error_propagate(errp, local_err);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -4433,6 +4489,7 @@ void bdrv_img_create(const char *filename, const char *fmt,
|
||||
BlockDriverState *bs = NULL;
|
||||
BlockDriver *drv, *proto_drv;
|
||||
BlockDriver *backing_drv = NULL;
|
||||
Error *local_err = NULL;
|
||||
int ret = 0;
|
||||
|
||||
/* Find driver and parse its options */
|
||||
@@ -4519,10 +4576,13 @@ void bdrv_img_create(const char *filename, const char *fmt,
|
||||
bs = bdrv_new("");
|
||||
|
||||
ret = bdrv_open(bs, backing_file->value.s, NULL, back_flags,
|
||||
backing_drv);
|
||||
backing_drv, &local_err);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not open '%s'",
|
||||
backing_file->value.s);
|
||||
error_setg_errno(errp, -ret, "Could not open '%s': %s",
|
||||
backing_file->value.s,
|
||||
error_get_pretty(local_err));
|
||||
error_free(local_err);
|
||||
local_err = NULL;
|
||||
goto out;
|
||||
}
|
||||
bdrv_get_geometry(bs, &size);
|
||||
@@ -4541,22 +4601,19 @@ void bdrv_img_create(const char *filename, const char *fmt,
|
||||
print_option_parameters(param);
|
||||
puts("");
|
||||
}
|
||||
ret = bdrv_create(drv, filename, param);
|
||||
if (ret < 0) {
|
||||
if (ret == -ENOTSUP) {
|
||||
error_setg(errp,"Formatting or formatting option not supported for "
|
||||
"file format '%s'", fmt);
|
||||
} else if (ret == -EFBIG) {
|
||||
const char *cluster_size_hint = "";
|
||||
if (get_option_parameter(create_options, BLOCK_OPT_CLUSTER_SIZE)) {
|
||||
cluster_size_hint = " (try using a larger cluster size)";
|
||||
}
|
||||
error_setg(errp, "The image size is too large for file format '%s'%s",
|
||||
fmt, cluster_size_hint);
|
||||
} else {
|
||||
error_setg(errp, "%s: error while creating %s: %s", filename, fmt,
|
||||
strerror(-ret));
|
||||
ret = bdrv_create(drv, filename, param, &local_err);
|
||||
if (ret == -EFBIG) {
|
||||
/* This is generally a better message than whatever the driver would
|
||||
* deliver (especially because of the cluster_size_hint), since that
|
||||
* is most probably not much different from "image too large". */
|
||||
const char *cluster_size_hint = "";
|
||||
if (get_option_parameter(create_options, BLOCK_OPT_CLUSTER_SIZE)) {
|
||||
cluster_size_hint = " (try using a larger cluster size)";
|
||||
}
|
||||
error_setg(errp, "The image size is too large for file format '%s'"
|
||||
"%s", fmt, cluster_size_hint);
|
||||
error_free(local_err);
|
||||
local_err = NULL;
|
||||
}
|
||||
|
||||
out:
|
||||
@@ -4566,6 +4623,9 @@ out:
|
||||
if (bs) {
|
||||
bdrv_unref(bs);
|
||||
}
|
||||
if (error_is_set(&local_err)) {
|
||||
error_propagate(errp, local_err);
|
||||
}
|
||||
}
|
||||
|
||||
AioContext *bdrv_get_aio_context(BlockDriverState *bs)
|
||||
@@ -4579,3 +4639,11 @@ void bdrv_add_before_write_notifier(BlockDriverState *bs,
|
||||
{
|
||||
notifier_with_return_list_add(&bs->before_write_notifiers, notifier);
|
||||
}
|
||||
|
||||
int bdrv_amend_options(BlockDriverState *bs, QEMUOptionParameter *options)
|
||||
{
|
||||
if (bs->drv->bdrv_amend_options == NULL) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
return bs->drv->bdrv_amend_options(bs, options);
|
||||
}
|
||||
|
||||
+5
-2
@@ -350,7 +350,8 @@ static QemuOptsList runtime_opts = {
|
||||
},
|
||||
};
|
||||
|
||||
static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags)
|
||||
static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
QemuOpts *opts;
|
||||
@@ -386,8 +387,10 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = bdrv_file_open(&bs->file, filename, NULL, flags);
|
||||
ret = bdrv_file_open(&bs->file, filename, NULL, flags, &local_err);
|
||||
if (ret < 0) {
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
+8
-3
@@ -116,7 +116,8 @@ static QemuOptsList runtime_opts = {
|
||||
},
|
||||
};
|
||||
|
||||
static int blkverify_open(BlockDriverState *bs, QDict *options, int flags)
|
||||
static int blkverify_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVBlkverifyState *s = bs->opaque;
|
||||
QemuOpts *opts;
|
||||
@@ -140,8 +141,10 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = bdrv_file_open(&bs->file, raw, NULL, flags);
|
||||
ret = bdrv_file_open(&bs->file, raw, NULL, flags, &local_err);
|
||||
if (ret < 0) {
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@@ -153,8 +156,10 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags)
|
||||
}
|
||||
|
||||
s->test_file = bdrv_new("");
|
||||
ret = bdrv_open(s->test_file, filename, NULL, flags, NULL);
|
||||
ret = bdrv_open(s->test_file, filename, NULL, flags, NULL, &local_err);
|
||||
if (ret < 0) {
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
bdrv_unref(s->test_file);
|
||||
s->test_file = NULL;
|
||||
goto fail;
|
||||
|
||||
+2
-1
@@ -108,7 +108,8 @@ static int bochs_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bochs_open(BlockDriverState *bs, QDict *options, int flags)
|
||||
static int bochs_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVBochsState *s = bs->opaque;
|
||||
int i;
|
||||
|
||||
+2
-1
@@ -53,7 +53,8 @@ static int cloop_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cloop_open(BlockDriverState *bs, QDict *options, int flags)
|
||||
static int cloop_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVCloopState *s = bs->opaque;
|
||||
uint32_t offsets_size, max_compressed_block_size = 1, i;
|
||||
|
||||
+11
-4
@@ -58,7 +58,8 @@ static int cow_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cow_open(BlockDriverState *bs, QDict *options, int flags)
|
||||
static int cow_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVCowState *s = bs->opaque;
|
||||
struct cow_header_v2 cow_header;
|
||||
@@ -294,12 +295,14 @@ static void cow_close(BlockDriverState *bs)
|
||||
{
|
||||
}
|
||||
|
||||
static int cow_create(const char *filename, QEMUOptionParameter *options)
|
||||
static int cow_create(const char *filename, QEMUOptionParameter *options,
|
||||
Error **errp)
|
||||
{
|
||||
struct cow_header_v2 cow_header;
|
||||
struct stat st;
|
||||
int64_t image_sectors = 0;
|
||||
const char *image_filename = NULL;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
BlockDriverState *cow_bs;
|
||||
|
||||
@@ -313,13 +316,17 @@ static int cow_create(const char *filename, QEMUOptionParameter *options)
|
||||
options++;
|
||||
}
|
||||
|
||||
ret = bdrv_create_file(filename, options);
|
||||
ret = bdrv_create_file(filename, options, &local_err);
|
||||
if (ret < 0) {
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = bdrv_file_open(&cow_bs, filename, NULL, BDRV_O_RDWR);
|
||||
ret = bdrv_file_open(&cow_bs, filename, NULL, BDRV_O_RDWR, &local_err);
|
||||
if (ret < 0) {
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -395,7 +395,8 @@ static QemuOptsList runtime_opts = {
|
||||
},
|
||||
};
|
||||
|
||||
static int curl_open(BlockDriverState *bs, QDict *options, int flags)
|
||||
static int curl_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVCURLState *s = bs->opaque;
|
||||
CURLState *state = NULL;
|
||||
|
||||
+2
-1
@@ -92,7 +92,8 @@ static int read_uint32(BlockDriverState *bs, int64_t offset, uint32_t *result)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dmg_open(BlockDriverState *bs, QDict *options, int flags)
|
||||
static int dmg_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVDMGState *s = bs->opaque;
|
||||
uint64_t info_begin,info_end,last_in_offset,last_out_offset;
|
||||
|
||||
+2
-2
@@ -288,7 +288,7 @@ static QemuOptsList runtime_opts = {
|
||||
};
|
||||
|
||||
static int qemu_gluster_open(BlockDriverState *bs, QDict *options,
|
||||
int bdrv_flags)
|
||||
int bdrv_flags, Error **errp)
|
||||
{
|
||||
BDRVGlusterState *s = bs->opaque;
|
||||
int open_flags = O_BINARY;
|
||||
@@ -357,7 +357,7 @@ out:
|
||||
}
|
||||
|
||||
static int qemu_gluster_create(const char *filename,
|
||||
QEMUOptionParameter *options)
|
||||
QEMUOptionParameter *options, Error **errp)
|
||||
{
|
||||
struct glfs *glfs;
|
||||
struct glfs_fd *fd;
|
||||
|
||||
+5
-3
@@ -1216,7 +1216,8 @@ fail:
|
||||
* We support iscsi url's on the form
|
||||
* iscsi://[<username>%<password>@]<host>[:<port>]/<targetname>/<lun>
|
||||
*/
|
||||
static int iscsi_open(BlockDriverState *bs, QDict *options, int flags)
|
||||
static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
{
|
||||
IscsiLun *iscsilun = bs->opaque;
|
||||
struct iscsi_context *iscsi = NULL;
|
||||
@@ -1447,7 +1448,8 @@ static int iscsi_has_zero_init(BlockDriverState *bs)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iscsi_create(const char *filename, QEMUOptionParameter *options)
|
||||
static int iscsi_create(const char *filename, QEMUOptionParameter *options,
|
||||
Error **errp)
|
||||
{
|
||||
int ret = 0;
|
||||
int64_t total_size = 0;
|
||||
@@ -1470,7 +1472,7 @@ static int iscsi_create(const char *filename, QEMUOptionParameter *options)
|
||||
|
||||
bs_options = qdict_new();
|
||||
qdict_put(bs_options, "filename", qstring_from_str(filename));
|
||||
ret = iscsi_open(bs, bs_options, 0);
|
||||
ret = iscsi_open(bs, bs_options, 0, NULL);
|
||||
QDECREF(bs_options);
|
||||
|
||||
if (ret != 0) {
|
||||
|
||||
+3
-2
@@ -505,14 +505,15 @@ static void mirror_iostatus_reset(BlockJob *job)
|
||||
static void mirror_complete(BlockJob *job, Error **errp)
|
||||
{
|
||||
MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
ret = bdrv_open_backing_file(s->target, NULL);
|
||||
ret = bdrv_open_backing_file(s->target, NULL, &local_err);
|
||||
if (ret < 0) {
|
||||
char backing_filename[PATH_MAX];
|
||||
bdrv_get_full_backing_filename(s->target, backing_filename,
|
||||
sizeof(backing_filename));
|
||||
error_setg_file_open(errp, -ret, backing_filename);
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
if (!s->synced) {
|
||||
|
||||
+2
-1
@@ -453,7 +453,8 @@ static void nbd_teardown_connection(BlockDriverState *bs)
|
||||
closesocket(s->sock);
|
||||
}
|
||||
|
||||
static int nbd_open(BlockDriverState *bs, QDict *options, int flags)
|
||||
static int nbd_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVNBDState *s = bs->opaque;
|
||||
int result;
|
||||
|
||||
+2
-1
@@ -68,7 +68,8 @@ static int parallels_probe(const uint8_t *buf, int buf_size, const char *filenam
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parallels_open(BlockDriverState *bs, QDict *options, int flags)
|
||||
static int parallels_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVParallelsState *s = bs->opaque;
|
||||
int i;
|
||||
|
||||
+11
-4
@@ -92,7 +92,8 @@ static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcow_open(BlockDriverState *bs, QDict *options, int flags)
|
||||
static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int len, i, shift, ret;
|
||||
@@ -658,7 +659,8 @@ static void qcow_close(BlockDriverState *bs)
|
||||
error_free(s->migration_blocker);
|
||||
}
|
||||
|
||||
static int qcow_create(const char *filename, QEMUOptionParameter *options)
|
||||
static int qcow_create(const char *filename, QEMUOptionParameter *options,
|
||||
Error **errp)
|
||||
{
|
||||
int header_size, backing_filename_len, l1_size, shift, i;
|
||||
QCowHeader header;
|
||||
@@ -666,6 +668,7 @@ static int qcow_create(const char *filename, QEMUOptionParameter *options)
|
||||
int64_t total_size = 0;
|
||||
const char *backing_file = NULL;
|
||||
int flags = 0;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
BlockDriverState *qcow_bs;
|
||||
|
||||
@@ -681,13 +684,17 @@ static int qcow_create(const char *filename, QEMUOptionParameter *options)
|
||||
options++;
|
||||
}
|
||||
|
||||
ret = bdrv_create_file(filename, options);
|
||||
ret = bdrv_create_file(filename, options, &local_err);
|
||||
if (ret < 0) {
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = bdrv_file_open(&qcow_bs, filename, NULL, BDRV_O_RDWR);
|
||||
ret = bdrv_file_open(&qcow_bs, filename, NULL, BDRV_O_RDWR, &local_err);
|
||||
if (ret < 0) {
|
||||
qerror_report_err(local_err);
|
||||
error_free(local_err);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -202,6 +202,24 @@ void qcow2_cache_depends_on_flush(Qcow2Cache *c)
|
||||
c->depends_on_flush = true;
|
||||
}
|
||||
|
||||
int qcow2_cache_empty(BlockDriverState *bs, Qcow2Cache *c)
|
||||
{
|
||||
int ret, i;
|
||||
|
||||
ret = qcow2_cache_flush(bs, c);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < c->size; i++) {
|
||||
assert(c->entries[i].ref == 0);
|
||||
c->entries[i].offset = 0;
|
||||
c->entries[i].cache_hits = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcow2_cache_find_entry_to_replace(Qcow2Cache *c)
|
||||
{
|
||||
int i;
|
||||
|
||||
+237
-4
@@ -1338,7 +1338,7 @@ int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset)
|
||||
* clusters.
|
||||
*/
|
||||
static int discard_single_l2(BlockDriverState *bs, uint64_t offset,
|
||||
unsigned int nb_clusters)
|
||||
unsigned int nb_clusters, enum qcow2_discard_type type)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
uint64_t *l2_table;
|
||||
@@ -1367,7 +1367,7 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset,
|
||||
l2_table[l2_index + i] = cpu_to_be64(0);
|
||||
|
||||
/* Then decrease the refcount */
|
||||
qcow2_free_any_clusters(bs, old_offset, 1, QCOW2_DISCARD_REQUEST);
|
||||
qcow2_free_any_clusters(bs, old_offset, 1, type);
|
||||
}
|
||||
|
||||
ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
|
||||
@@ -1379,7 +1379,7 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset,
|
||||
}
|
||||
|
||||
int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset,
|
||||
int nb_sectors)
|
||||
int nb_sectors, enum qcow2_discard_type type)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
uint64_t end_offset;
|
||||
@@ -1402,7 +1402,7 @@ int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset,
|
||||
|
||||
/* Each L2 table is handled by its own loop iteration */
|
||||
while (nb_clusters > 0) {
|
||||
ret = discard_single_l2(bs, offset, nb_clusters);
|
||||
ret = discard_single_l2(bs, offset, nb_clusters, type);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -1497,3 +1497,236 @@ fail:
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Expands all zero clusters in a specific L1 table (or deallocates them, for
|
||||
* non-backed non-pre-allocated zero clusters).
|
||||
*
|
||||
* expanded_clusters is a bitmap where every bit corresponds to one cluster in
|
||||
* the image file; a bit gets set if the corresponding cluster has been used for
|
||||
* zero expansion (i.e., has been filled with zeroes and is referenced from an
|
||||
* L2 table). nb_clusters contains the total cluster count of the image file,
|
||||
* i.e., the number of bits in expanded_clusters.
|
||||
*/
|
||||
static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
|
||||
int l1_size, uint8_t *expanded_clusters,
|
||||
uint64_t nb_clusters)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
bool is_active_l1 = (l1_table == s->l1_table);
|
||||
uint64_t *l2_table = NULL;
|
||||
int ret;
|
||||
int i, j;
|
||||
|
||||
if (!is_active_l1) {
|
||||
/* inactive L2 tables require a buffer to be stored in when loading
|
||||
* them from disk */
|
||||
l2_table = qemu_blockalign(bs, s->cluster_size);
|
||||
}
|
||||
|
||||
for (i = 0; i < l1_size; i++) {
|
||||
uint64_t l2_offset = l1_table[i] & L1E_OFFSET_MASK;
|
||||
bool l2_dirty = false;
|
||||
|
||||
if (!l2_offset) {
|
||||
/* unallocated */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_active_l1) {
|
||||
/* get active L2 tables from cache */
|
||||
ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset,
|
||||
(void **)&l2_table);
|
||||
} else {
|
||||
/* load inactive L2 tables from disk */
|
||||
ret = bdrv_read(bs->file, l2_offset / BDRV_SECTOR_SIZE,
|
||||
(void *)l2_table, s->cluster_sectors);
|
||||
}
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (j = 0; j < s->l2_size; j++) {
|
||||
uint64_t l2_entry = be64_to_cpu(l2_table[j]);
|
||||
int64_t offset = l2_entry & L2E_OFFSET_MASK, cluster_index;
|
||||
int cluster_type = qcow2_get_cluster_type(l2_entry);
|
||||
|
||||
if (cluster_type == QCOW2_CLUSTER_NORMAL) {
|
||||
cluster_index = offset >> s->cluster_bits;
|
||||
assert((cluster_index >= 0) && (cluster_index < nb_clusters));
|
||||
if (expanded_clusters[cluster_index / 8] &
|
||||
(1 << (cluster_index % 8))) {
|
||||
/* Probably a shared L2 table; this cluster was a zero
|
||||
* cluster which has been expanded, its refcount
|
||||
* therefore most likely requires an update. */
|
||||
ret = qcow2_update_cluster_refcount(bs, cluster_index, 1,
|
||||
QCOW2_DISCARD_NEVER);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
/* Since we just increased the refcount, the COPIED flag may
|
||||
* no longer be set. */
|
||||
l2_table[j] = cpu_to_be64(l2_entry & ~QCOW_OFLAG_COPIED);
|
||||
l2_dirty = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else if (qcow2_get_cluster_type(l2_entry) != QCOW2_CLUSTER_ZERO) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!offset) {
|
||||
/* not preallocated */
|
||||
if (!bs->backing_hd) {
|
||||
/* not backed; therefore we can simply deallocate the
|
||||
* cluster */
|
||||
l2_table[j] = 0;
|
||||
l2_dirty = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
offset = qcow2_alloc_clusters(bs, s->cluster_size);
|
||||
if (offset < 0) {
|
||||
ret = offset;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT,
|
||||
offset, s->cluster_size);
|
||||
if (ret < 0) {
|
||||
qcow2_free_clusters(bs, offset, s->cluster_size,
|
||||
QCOW2_DISCARD_ALWAYS);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = bdrv_write_zeroes(bs->file, offset / BDRV_SECTOR_SIZE,
|
||||
s->cluster_sectors);
|
||||
if (ret < 0) {
|
||||
qcow2_free_clusters(bs, offset, s->cluster_size,
|
||||
QCOW2_DISCARD_ALWAYS);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
l2_table[j] = cpu_to_be64(offset | QCOW_OFLAG_COPIED);
|
||||
l2_dirty = true;
|
||||
|
||||
cluster_index = offset >> s->cluster_bits;
|
||||
assert((cluster_index >= 0) && (cluster_index < nb_clusters));
|
||||
expanded_clusters[cluster_index / 8] |= 1 << (cluster_index % 8);
|
||||
}
|
||||
|
||||
if (is_active_l1) {
|
||||
if (l2_dirty) {
|
||||
qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table);
|
||||
qcow2_cache_depends_on_flush(s->l2_table_cache);
|
||||
}
|
||||
ret = qcow2_cache_put(bs, s->l2_table_cache, (void **)&l2_table);
|
||||
if (ret < 0) {
|
||||
l2_table = NULL;
|
||||
goto fail;
|
||||
}
|
||||
} else {
|
||||
if (l2_dirty) {
|
||||
ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT &
|
||||
~(QCOW2_OL_INACTIVE_L2 | QCOW2_OL_ACTIVE_L2), l2_offset,
|
||||
s->cluster_size);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = bdrv_write(bs->file, l2_offset / BDRV_SECTOR_SIZE,
|
||||
(void *)l2_table, s->cluster_sectors);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
fail:
|
||||
if (l2_table) {
|
||||
if (!is_active_l1) {
|
||||
qemu_vfree(l2_table);
|
||||
} else {
|
||||
if (ret < 0) {
|
||||
qcow2_cache_put(bs, s->l2_table_cache, (void **)&l2_table);
|
||||
} else {
|
||||
ret = qcow2_cache_put(bs, s->l2_table_cache,
|
||||
(void **)&l2_table);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* For backed images, expands all zero clusters on the image. For non-backed
|
||||
* images, deallocates all non-pre-allocated zero clusters (and claims the
|
||||
* allocation for pre-allocated ones). This is important for downgrading to a
|
||||
* qcow2 version which doesn't yet support metadata zero clusters.
|
||||
*/
|
||||
int qcow2_expand_zero_clusters(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
uint64_t *l1_table = NULL;
|
||||
int cluster_to_sector_bits = s->cluster_bits - BDRV_SECTOR_BITS;
|
||||
uint64_t nb_clusters;
|
||||
uint8_t *expanded_clusters;
|
||||
int ret;
|
||||
int i, j;
|
||||
|
||||
nb_clusters = (bs->total_sectors + (1 << cluster_to_sector_bits) - 1)
|
||||
>> cluster_to_sector_bits;
|
||||
expanded_clusters = g_malloc0((nb_clusters + 7) / 8);
|
||||
|
||||
ret = expand_zero_clusters_in_l1(bs, s->l1_table, s->l1_size,
|
||||
expanded_clusters, nb_clusters);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Inactive L1 tables may point to active L2 tables - therefore it is
|
||||
* necessary to flush the L2 table cache before trying to access the L2
|
||||
* tables pointed to by inactive L1 entries (else we might try to expand
|
||||
* zero clusters that have already been expanded); furthermore, it is also
|
||||
* necessary to empty the L2 table cache, since it may contain tables which
|
||||
* are now going to be modified directly on disk, bypassing the cache.
|
||||
* qcow2_cache_empty() does both for us. */
|
||||
ret = qcow2_cache_empty(bs, s->l2_table_cache);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (i = 0; i < s->nb_snapshots; i++) {
|
||||
int l1_sectors = (s->snapshots[i].l1_size * sizeof(uint64_t) +
|
||||
BDRV_SECTOR_SIZE - 1) / BDRV_SECTOR_SIZE;
|
||||
|
||||
l1_table = g_realloc(l1_table, l1_sectors * BDRV_SECTOR_SIZE);
|
||||
|
||||
ret = bdrv_read(bs->file, s->snapshots[i].l1_table_offset /
|
||||
BDRV_SECTOR_SIZE, (void *)l1_table, l1_sectors);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (j = 0; j < s->snapshots[i].l1_size; j++) {
|
||||
be64_to_cpus(&l1_table[j]);
|
||||
}
|
||||
|
||||
ret = expand_zero_clusters_in_l1(bs, l1_table, s->snapshots[i].l1_size,
|
||||
expanded_clusters, nb_clusters);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
fail:
|
||||
g_free(expanded_clusters);
|
||||
g_free(l1_table);
|
||||
return ret;
|
||||
}
|
||||
|
||||
+15
-14
@@ -601,10 +601,10 @@ fail:
|
||||
* If the return value is non-negative, it is the new refcount of the cluster.
|
||||
* If it is negative, it is -errno and indicates an error.
|
||||
*/
|
||||
static int update_cluster_refcount(BlockDriverState *bs,
|
||||
int64_t cluster_index,
|
||||
int addend,
|
||||
enum qcow2_discard_type type)
|
||||
int qcow2_update_cluster_refcount(BlockDriverState *bs,
|
||||
int64_t cluster_index,
|
||||
int addend,
|
||||
enum qcow2_discard_type type)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int ret;
|
||||
@@ -733,8 +733,8 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size)
|
||||
if (free_in_cluster == 0)
|
||||
s->free_byte_offset = 0;
|
||||
if ((offset & (s->cluster_size - 1)) != 0)
|
||||
update_cluster_refcount(bs, offset >> s->cluster_bits, 1,
|
||||
QCOW2_DISCARD_NEVER);
|
||||
qcow2_update_cluster_refcount(bs, offset >> s->cluster_bits, 1,
|
||||
QCOW2_DISCARD_NEVER);
|
||||
} else {
|
||||
offset = qcow2_alloc_clusters(bs, s->cluster_size);
|
||||
if (offset < 0) {
|
||||
@@ -744,8 +744,8 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size)
|
||||
if ((cluster_offset + s->cluster_size) == offset) {
|
||||
/* we are lucky: contiguous data */
|
||||
offset = s->free_byte_offset;
|
||||
update_cluster_refcount(bs, offset >> s->cluster_bits, 1,
|
||||
QCOW2_DISCARD_NEVER);
|
||||
qcow2_update_cluster_refcount(bs, offset >> s->cluster_bits, 1,
|
||||
QCOW2_DISCARD_NEVER);
|
||||
s->free_byte_offset += size;
|
||||
} else {
|
||||
s->free_byte_offset = offset;
|
||||
@@ -754,8 +754,8 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size)
|
||||
}
|
||||
|
||||
/* The cluster refcount was incremented, either by qcow2_alloc_clusters()
|
||||
* or explicitly by update_cluster_refcount(). Refcount blocks must be
|
||||
* flushed before the caller's L2 table updates.
|
||||
* or explicitly by qcow2_update_cluster_refcount(). Refcount blocks must
|
||||
* be flushed before the caller's L2 table updates.
|
||||
*/
|
||||
qcow2_cache_set_dependency(bs, s->l2_table_cache, s->refcount_block_cache);
|
||||
return offset;
|
||||
@@ -896,8 +896,9 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
|
||||
break;
|
||||
}
|
||||
if (addend != 0) {
|
||||
refcount = update_cluster_refcount(bs, cluster_index, addend,
|
||||
QCOW2_DISCARD_SNAPSHOT);
|
||||
refcount = qcow2_update_cluster_refcount(bs,
|
||||
cluster_index, addend,
|
||||
QCOW2_DISCARD_SNAPSHOT);
|
||||
} else {
|
||||
refcount = get_refcount(bs, cluster_index);
|
||||
}
|
||||
@@ -936,8 +937,8 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
|
||||
|
||||
|
||||
if (addend != 0) {
|
||||
refcount = update_cluster_refcount(bs, l2_offset >> s->cluster_bits, addend,
|
||||
QCOW2_DISCARD_SNAPSHOT);
|
||||
refcount = qcow2_update_cluster_refcount(bs, l2_offset >>
|
||||
s->cluster_bits, addend, QCOW2_DISCARD_SNAPSHOT);
|
||||
} else {
|
||||
refcount = get_refcount(bs, l2_offset >> s->cluster_bits);
|
||||
}
|
||||
|
||||
+46
-16
@@ -297,31 +297,47 @@ static void find_new_snapshot_id(BlockDriverState *bs,
|
||||
snprintf(id_str, id_str_size, "%d", id_max + 1);
|
||||
}
|
||||
|
||||
static int find_snapshot_by_id(BlockDriverState *bs, const char *id_str)
|
||||
static int find_snapshot_by_id_and_name(BlockDriverState *bs,
|
||||
const char *id,
|
||||
const char *name)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int i;
|
||||
|
||||
for(i = 0; i < s->nb_snapshots; i++) {
|
||||
if (!strcmp(s->snapshots[i].id_str, id_str))
|
||||
return i;
|
||||
if (id && name) {
|
||||
for (i = 0; i < s->nb_snapshots; i++) {
|
||||
if (!strcmp(s->snapshots[i].id_str, id) &&
|
||||
!strcmp(s->snapshots[i].name, name)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
} else if (id) {
|
||||
for (i = 0; i < s->nb_snapshots; i++) {
|
||||
if (!strcmp(s->snapshots[i].id_str, id)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
} else if (name) {
|
||||
for (i = 0; i < s->nb_snapshots; i++) {
|
||||
if (!strcmp(s->snapshots[i].name, name)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int find_snapshot_by_id_or_name(BlockDriverState *bs, const char *name)
|
||||
static int find_snapshot_by_id_or_name(BlockDriverState *bs,
|
||||
const char *id_or_name)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int i, ret;
|
||||
int ret;
|
||||
|
||||
ret = find_snapshot_by_id(bs, name);
|
||||
if (ret >= 0)
|
||||
ret = find_snapshot_by_id_and_name(bs, id_or_name, NULL);
|
||||
if (ret >= 0) {
|
||||
return ret;
|
||||
for(i = 0; i < s->nb_snapshots; i++) {
|
||||
if (!strcmp(s->snapshots[i].name, name))
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
return find_snapshot_by_id_and_name(bs, NULL, id_or_name);
|
||||
}
|
||||
|
||||
/* if no id is provided, a new one is constructed */
|
||||
@@ -343,7 +359,7 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
|
||||
}
|
||||
|
||||
/* Check that the ID is unique */
|
||||
if (find_snapshot_by_id(bs, sn_info->id_str) >= 0) {
|
||||
if (find_snapshot_by_id_and_name(bs, sn_info->id_str, NULL) >= 0) {
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
@@ -416,6 +432,13 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
|
||||
|
||||
g_free(old_snapshot_list);
|
||||
|
||||
/* The VM state isn't needed any more in the active L1 table; in fact, it
|
||||
* hurts by causing expensive COW for the next snapshot. */
|
||||
qcow2_discard_clusters(bs, qcow2_vm_state_offset(s),
|
||||
align_offset(sn->vm_state_size, s->cluster_size)
|
||||
>> BDRV_SECTOR_BITS,
|
||||
QCOW2_DISCARD_NEVER);
|
||||
|
||||
#ifdef DEBUG_ALLOC
|
||||
{
|
||||
BdrvCheckResult result = {0};
|
||||
@@ -553,15 +576,19 @@ fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id)
|
||||
int qcow2_snapshot_delete(BlockDriverState *bs,
|
||||
const char *snapshot_id,
|
||||
const char *name,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
QCowSnapshot sn;
|
||||
int snapshot_index, ret;
|
||||
|
||||
/* Search the snapshot */
|
||||
snapshot_index = find_snapshot_by_id_or_name(bs, snapshot_id);
|
||||
snapshot_index = find_snapshot_by_id_and_name(bs, snapshot_id, name);
|
||||
if (snapshot_index < 0) {
|
||||
error_setg(errp, "Can't find the snapshot");
|
||||
return -ENOENT;
|
||||
}
|
||||
sn = s->snapshots[snapshot_index];
|
||||
@@ -573,6 +600,7 @@ int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id)
|
||||
s->nb_snapshots--;
|
||||
ret = qcow2_write_snapshots(bs);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Failed to remove snapshot from snapshot list");
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -590,6 +618,7 @@ int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id)
|
||||
ret = qcow2_update_snapshot_refcount(bs, sn.l1_table_offset,
|
||||
sn.l1_size, -1);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Failed to free the cluster and L1 table");
|
||||
return ret;
|
||||
}
|
||||
qcow2_free_clusters(bs, sn.l1_table_offset, sn.l1_size * sizeof(uint64_t),
|
||||
@@ -598,6 +627,7 @@ int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id)
|
||||
/* must update the copied flag on the current cluster offsets */
|
||||
ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 0);
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Failed to update snapshot status in disk");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
+290
-56
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user