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:
Anthony Liguori 2013-09-17 09:51:40 -05:00
commit 5dc11192b2
73 changed files with 2878 additions and 425 deletions

View file

@ -188,3 +188,9 @@ class QEMUMonitorProtocol:
def settimeout(self, timeout): def settimeout(self, timeout):
self.__sock.settimeout(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

184
block.c
View file

@ -394,18 +394,26 @@ typedef struct CreateCo {
char *filename; char *filename;
QEMUOptionParameter *options; QEMUOptionParameter *options;
int ret; int ret;
Error *err;
} CreateCo; } CreateCo;
static void coroutine_fn bdrv_create_co_entry(void *opaque) static void coroutine_fn bdrv_create_co_entry(void *opaque)
{ {
Error *local_err = NULL;
int ret;
CreateCo *cco = opaque; CreateCo *cco = opaque;
assert(cco->drv); 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, int bdrv_create(BlockDriver *drv, const char* filename,
QEMUOptionParameter *options) QEMUOptionParameter *options, Error **errp)
{ {
int ret; int ret;
@ -415,9 +423,11 @@ int bdrv_create(BlockDriver *drv, const char* filename,
.filename = g_strdup(filename), .filename = g_strdup(filename),
.options = options, .options = options,
.ret = NOT_DONE, .ret = NOT_DONE,
.err = NULL,
}; };
if (!drv->bdrv_create) { if (!drv->bdrv_create) {
error_setg(errp, "Driver '%s' does not support image creation", drv->format_name);
ret = -ENOTSUP; ret = -ENOTSUP;
goto out; goto out;
} }
@ -434,22 +444,37 @@ int bdrv_create(BlockDriver *drv, const char* filename,
} }
ret = cco.ret; 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: out:
g_free(cco.filename); g_free(cco.filename);
return ret; return ret;
} }
int bdrv_create_file(const char* filename, QEMUOptionParameter *options) int bdrv_create_file(const char* filename, QEMUOptionParameter *options,
Error **errp)
{ {
BlockDriver *drv; BlockDriver *drv;
Error *local_err = NULL;
int ret;
drv = bdrv_find_protocol(filename, true); drv = bdrv_find_protocol(filename, true);
if (drv == NULL) { if (drv == NULL) {
error_setg(errp, "Could not find protocol for file '%s'", filename);
return -ENOENT; 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, static int find_image_format(BlockDriverState *bs, const char *filename,
BlockDriver **pdrv) BlockDriver **pdrv, Error **errp)
{ {
int score, score_max; int score, score_max;
BlockDriver *drv1, *drv; 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) { if (bs->sg || !bdrv_is_inserted(bs) || bdrv_getlength(bs) == 0) {
drv = bdrv_find_format("raw"); drv = bdrv_find_format("raw");
if (!drv) { if (!drv) {
error_setg(errp, "Could not find raw image format");
ret = -ENOENT; ret = -ENOENT;
} }
*pdrv = drv; *pdrv = drv;
@ -571,6 +597,8 @@ static int find_image_format(BlockDriverState *bs, const char *filename,
ret = bdrv_pread(bs, 0, buf, sizeof(buf)); ret = bdrv_pread(bs, 0, buf, sizeof(buf));
if (ret < 0) { if (ret < 0) {
error_setg_errno(errp, -ret, "Could not read image for determining its "
"format");
*pdrv = NULL; *pdrv = NULL;
return ret; return ret;
} }
@ -587,6 +615,8 @@ static int find_image_format(BlockDriverState *bs, const char *filename,
} }
} }
if (!drv) { if (!drv) {
error_setg(errp, "Could not determine image format: No compatible "
"driver found");
ret = -ENOENT; ret = -ENOENT;
} }
*pdrv = drv; *pdrv = drv;
@ -706,10 +736,11 @@ static int bdrv_open_flags(BlockDriverState *bs, int flags)
* Removes all processed options from *options. * Removes all processed options from *options.
*/ */
static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file, 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; int ret, open_flags;
const char *filename; const char *filename;
Error *local_err = NULL;
assert(drv != NULL); assert(drv != NULL);
assert(bs->file == 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); bs->read_only = !(open_flags & BDRV_O_RDWR);
if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, bs->read_only)) { if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, bs->read_only)) {
error_setg(errp, "Driver '%s' is not whitelisted", drv->format_name);
return -ENOTSUP; return -ENOTSUP;
} }
@ -761,25 +793,32 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
if (drv->bdrv_file_open) { if (drv->bdrv_file_open) {
assert(file == NULL); assert(file == NULL);
assert(drv->bdrv_parse_filename || filename != 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 { } else {
if (file == NULL) { if (file == NULL) {
qerror_report(ERROR_CLASS_GENERIC_ERROR, "Can't use '%s' as a " error_setg(errp, "Can't use '%s' as a block driver for the "
"block driver for the protocol level", "protocol level", drv->format_name);
drv->format_name);
ret = -EINVAL; ret = -EINVAL;
goto free_and_fail; goto free_and_fail;
} }
bs->file = file; 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 (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; goto free_and_fail;
} }
ret = refresh_total_sectors(bs, bs->total_sectors); ret = refresh_total_sectors(bs, bs->total_sectors);
if (ret < 0) { if (ret < 0) {
error_setg_errno(errp, -ret, "Could not refresh total sector count");
goto free_and_fail; goto free_and_fail;
} }
@ -808,12 +847,13 @@ free_and_fail:
* dictionary, it needs to use QINCREF() before calling bdrv_file_open. * dictionary, it needs to use QINCREF() before calling bdrv_file_open.
*/ */
int bdrv_file_open(BlockDriverState **pbs, const char *filename, int bdrv_file_open(BlockDriverState **pbs, const char *filename,
QDict *options, int flags) QDict *options, int flags, Error **errp)
{ {
BlockDriverState *bs; BlockDriverState *bs;
BlockDriver *drv; BlockDriver *drv;
const char *drvname; const char *drvname;
bool allow_protocol_prefix = false; bool allow_protocol_prefix = false;
Error *local_err = NULL;
int ret; int ret;
/* NULL means an empty set of options */ /* 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)); qdict_put(options, "filename", qstring_from_str(filename));
allow_protocol_prefix = true; allow_protocol_prefix = true;
} else { } else {
qerror_report(ERROR_CLASS_GENERIC_ERROR, "Can't specify 'file' and " error_setg(errp, "Can't specify 'file' and 'filename' options at the "
"'filename' options at the same time"); "same time");
ret = -EINVAL; ret = -EINVAL;
goto fail; goto fail;
} }
@ -842,53 +882,53 @@ int bdrv_file_open(BlockDriverState **pbs, const char *filename,
drvname = qdict_get_try_str(options, "driver"); drvname = qdict_get_try_str(options, "driver");
if (drvname) { if (drvname) {
drv = bdrv_find_whitelisted_format(drvname, !(flags & BDRV_O_RDWR)); drv = bdrv_find_whitelisted_format(drvname, !(flags & BDRV_O_RDWR));
if (!drv) {
error_setg(errp, "Unknown driver '%s'", drvname);
}
qdict_del(options, "driver"); qdict_del(options, "driver");
} else if (filename) { } else if (filename) {
drv = bdrv_find_protocol(filename, allow_protocol_prefix); drv = bdrv_find_protocol(filename, allow_protocol_prefix);
if (!drv) { if (!drv) {
qerror_report(ERROR_CLASS_GENERIC_ERROR, "Unknown protocol"); error_setg(errp, "Unknown protocol");
} }
} else { } else {
qerror_report(ERROR_CLASS_GENERIC_ERROR, error_setg(errp, "Must specify either driver or file");
"Must specify either driver or file");
drv = NULL; drv = NULL;
} }
if (!drv) { if (!drv) {
/* errp has been set already */
ret = -ENOENT; ret = -ENOENT;
goto fail; goto fail;
} }
/* Parse the filename and open it */ /* Parse the filename and open it */
if (drv->bdrv_parse_filename && filename) { if (drv->bdrv_parse_filename && filename) {
Error *local_err = NULL;
drv->bdrv_parse_filename(filename, options, &local_err); drv->bdrv_parse_filename(filename, options, &local_err);
if (error_is_set(&local_err)) { if (error_is_set(&local_err)) {
qerror_report_err(local_err); error_propagate(errp, local_err);
error_free(local_err);
ret = -EINVAL; ret = -EINVAL;
goto fail; goto fail;
} }
qdict_del(options, "filename"); qdict_del(options, "filename");
} else if (!drv->bdrv_parse_filename && !filename) { } else if (!drv->bdrv_parse_filename && !filename) {
qerror_report(ERROR_CLASS_GENERIC_ERROR, error_setg(errp, "The '%s' block driver requires a file name",
"The '%s' block driver requires a file name", drv->format_name);
drv->format_name);
ret = -EINVAL; ret = -EINVAL;
goto fail; 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) { if (ret < 0) {
error_propagate(errp, local_err);
goto fail; goto fail;
} }
/* Check if any unknown options were used */ /* Check if any unknown options were used */
if (qdict_size(options) != 0) { if (qdict_size(options) != 0) {
const QDictEntry *entry = qdict_first(options); const QDictEntry *entry = qdict_first(options);
qerror_report(ERROR_CLASS_GENERIC_ERROR, "Block protocol '%s' doesn't " error_setg(errp, "Block protocol '%s' doesn't support the option '%s'",
"support the option '%s'", drv->format_name, entry->key);
drv->format_name, entry->key);
ret = -EINVAL; ret = -EINVAL;
goto fail; goto fail;
} }
@ -915,11 +955,12 @@ fail:
* function (even on failure), so if the caller intends to reuse the dictionary, * function (even on failure), so if the caller intends to reuse the dictionary,
* it needs to use QINCREF() before calling bdrv_file_open. * 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]; char backing_filename[PATH_MAX];
int back_flags, ret; int back_flags, ret;
BlockDriver *back_drv = NULL; BlockDriver *back_drv = NULL;
Error *local_err = NULL;
if (bs->backing_hd != NULL) { if (bs->backing_hd != NULL) {
QDECREF(options); QDECREF(options);
@ -952,11 +993,12 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options)
ret = bdrv_open(bs->backing_hd, ret = bdrv_open(bs->backing_hd,
*backing_filename ? backing_filename : NULL, options, *backing_filename ? backing_filename : NULL, options,
back_flags, back_drv); back_flags, back_drv, &local_err);
if (ret < 0) { if (ret < 0) {
bdrv_unref(bs->backing_hd); bdrv_unref(bs->backing_hd);
bs->backing_hd = NULL; bs->backing_hd = NULL;
bs->open_flags |= BDRV_O_NO_BACKING; bs->open_flags |= BDRV_O_NO_BACKING;
error_propagate(errp, local_err);
return ret; return ret;
} }
return 0; 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. * dictionary, it needs to use QINCREF() before calling bdrv_open.
*/ */
int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
int flags, BlockDriver *drv) int flags, BlockDriver *drv, Error **errp)
{ {
int ret; int ret;
/* TODO: extra byte is a hack to ensure MAX_PATH space on Windows. */ /* 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; BlockDriverState *file = NULL;
QDict *file_options = NULL; QDict *file_options = NULL;
const char *drvname; const char *drvname;
Error *local_err = NULL;
/* NULL means an empty set of options */ /* NULL means an empty set of options */
if (options == NULL) { if (options == NULL) {
@ -1016,7 +1059,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
char backing_filename[PATH_MAX]; char backing_filename[PATH_MAX];
if (qdict_size(options) != 0) { 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; ret = -EINVAL;
goto fail; goto fail;
} }
@ -1027,7 +1070,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
/* if there is a backing file, use it */ /* if there is a backing file, use it */
bs1 = bdrv_new(""); bs1 = bdrv_new("");
ret = bdrv_open(bs1, filename, NULL, 0, drv); ret = bdrv_open(bs1, filename, NULL, 0, drv, &local_err);
if (ret < 0) { if (ret < 0) {
bdrv_unref(bs1); bdrv_unref(bs1);
goto fail; 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)); ret = get_tmp_filename(tmp_filename, sizeof(tmp_filename));
if (ret < 0) { if (ret < 0) {
error_setg_errno(errp, -ret, "Could not get temporary filename");
goto fail; goto fail;
} }
@ -1046,6 +1090,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
snprintf(backing_filename, sizeof(backing_filename), snprintf(backing_filename, sizeof(backing_filename),
"%s", filename); "%s", filename);
} else if (!realpath(filename, backing_filename)) { } else if (!realpath(filename, backing_filename)) {
error_setg_errno(errp, errno, "Could not resolve path '%s'", filename);
ret = -errno; ret = -errno;
goto fail; goto fail;
} }
@ -1062,9 +1107,14 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
drv->format_name); 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); free_option_parameters(create_options);
if (ret < 0) { 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; goto fail;
} }
@ -1081,7 +1131,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
extract_subqdict(options, &file_options, "file."); extract_subqdict(options, &file_options, "file.");
ret = bdrv_file_open(&file, filename, file_options, 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) { if (ret < 0) {
goto fail; goto fail;
} }
@ -1094,7 +1144,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
} }
if (!drv) { if (!drv) {
ret = find_image_format(file, filename, &drv); ret = find_image_format(file, filename, &drv, &local_err);
} }
if (!drv) { if (!drv) {
@ -1102,7 +1152,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
} }
/* Open the image */ /* 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) { if (ret < 0) {
goto unlink_and_fail; goto unlink_and_fail;
} }
@ -1117,7 +1167,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
QDict *backing_options; QDict *backing_options;
extract_subqdict(options, &backing_options, "backing."); 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) { if (ret < 0) {
goto close_and_fail; 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 */ /* Check if any unknown options were used */
if (qdict_size(options) != 0) { if (qdict_size(options) != 0) {
const QDictEntry *entry = qdict_first(options); const QDictEntry *entry = qdict_first(options);
qerror_report(ERROR_CLASS_GENERIC_ERROR, "Block format '%s' used by " error_setg(errp, "Block format '%s' used by device '%s' doesn't "
"device '%s' doesn't support the option '%s'", "support the option '%s'", drv->format_name, bs->device_name,
drv->format_name, bs->device_name, entry->key); entry->key);
ret = -EINVAL; ret = -EINVAL;
goto close_and_fail; goto close_and_fail;
@ -1152,11 +1202,17 @@ fail:
QDECREF(bs->options); QDECREF(bs->options);
QDECREF(options); QDECREF(options);
bs->options = NULL; bs->options = NULL;
if (error_is_set(&local_err)) {
error_propagate(errp, local_err);
}
return ret; return ret;
close_and_fail: close_and_fail:
bdrv_close(bs); bdrv_close(bs);
QDECREF(options); QDECREF(options);
if (error_is_set(&local_err)) {
error_propagate(errp, local_err);
}
return ret; return ret;
} }
@ -4433,6 +4489,7 @@ void bdrv_img_create(const char *filename, const char *fmt,
BlockDriverState *bs = NULL; BlockDriverState *bs = NULL;
BlockDriver *drv, *proto_drv; BlockDriver *drv, *proto_drv;
BlockDriver *backing_drv = NULL; BlockDriver *backing_drv = NULL;
Error *local_err = NULL;
int ret = 0; int ret = 0;
/* Find driver and parse its options */ /* Find driver and parse its options */
@ -4519,10 +4576,13 @@ void bdrv_img_create(const char *filename, const char *fmt,
bs = bdrv_new(""); bs = bdrv_new("");
ret = bdrv_open(bs, backing_file->value.s, NULL, back_flags, ret = bdrv_open(bs, backing_file->value.s, NULL, back_flags,
backing_drv); backing_drv, &local_err);
if (ret < 0) { if (ret < 0) {
error_setg_errno(errp, -ret, "Could not open '%s'", error_setg_errno(errp, -ret, "Could not open '%s': %s",
backing_file->value.s); backing_file->value.s,
error_get_pretty(local_err));
error_free(local_err);
local_err = NULL;
goto out; goto out;
} }
bdrv_get_geometry(bs, &size); bdrv_get_geometry(bs, &size);
@ -4541,22 +4601,19 @@ void bdrv_img_create(const char *filename, const char *fmt,
print_option_parameters(param); print_option_parameters(param);
puts(""); puts("");
} }
ret = bdrv_create(drv, filename, param); ret = bdrv_create(drv, filename, param, &local_err);
if (ret < 0) { if (ret == -EFBIG) {
if (ret == -ENOTSUP) { /* This is generally a better message than whatever the driver would
error_setg(errp,"Formatting or formatting option not supported for " * deliver (especially because of the cluster_size_hint), since that
"file format '%s'", fmt); * is most probably not much different from "image too large". */
} else if (ret == -EFBIG) { const char *cluster_size_hint = "";
const char *cluster_size_hint = ""; if (get_option_parameter(create_options, BLOCK_OPT_CLUSTER_SIZE)) {
if (get_option_parameter(create_options, BLOCK_OPT_CLUSTER_SIZE)) { cluster_size_hint = " (try using a larger 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));
} }
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: out:
@ -4566,6 +4623,9 @@ out:
if (bs) { if (bs) {
bdrv_unref(bs); bdrv_unref(bs);
} }
if (error_is_set(&local_err)) {
error_propagate(errp, local_err);
}
} }
AioContext *bdrv_get_aio_context(BlockDriverState *bs) 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); 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);
}

View file

@ -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; BDRVBlkdebugState *s = bs->opaque;
QemuOpts *opts; QemuOpts *opts;
@ -386,8 +387,10 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags)
goto fail; 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) { if (ret < 0) {
qerror_report_err(local_err);
error_free(local_err);
goto fail; goto fail;
} }

View file

@ -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; BDRVBlkverifyState *s = bs->opaque;
QemuOpts *opts; QemuOpts *opts;
@ -140,8 +141,10 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags)
goto fail; 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) { if (ret < 0) {
qerror_report_err(local_err);
error_free(local_err);
goto fail; goto fail;
} }
@ -153,8 +156,10 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags)
} }
s->test_file = bdrv_new(""); 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) { if (ret < 0) {
qerror_report_err(local_err);
error_free(local_err);
bdrv_unref(s->test_file); bdrv_unref(s->test_file);
s->test_file = NULL; s->test_file = NULL;
goto fail; goto fail;

View file

@ -108,7 +108,8 @@ static int bochs_probe(const uint8_t *buf, int buf_size, const char *filename)
return 0; 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; BDRVBochsState *s = bs->opaque;
int i; int i;

View file

@ -53,7 +53,8 @@ static int cloop_probe(const uint8_t *buf, int buf_size, const char *filename)
return 0; 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; BDRVCloopState *s = bs->opaque;
uint32_t offsets_size, max_compressed_block_size = 1, i; uint32_t offsets_size, max_compressed_block_size = 1, i;

View file

@ -58,7 +58,8 @@ static int cow_probe(const uint8_t *buf, int buf_size, const char *filename)
return 0; 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; BDRVCowState *s = bs->opaque;
struct cow_header_v2 cow_header; 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 cow_header_v2 cow_header;
struct stat st; struct stat st;
int64_t image_sectors = 0; int64_t image_sectors = 0;
const char *image_filename = NULL; const char *image_filename = NULL;
Error *local_err = NULL;
int ret; int ret;
BlockDriverState *cow_bs; BlockDriverState *cow_bs;
@ -313,13 +316,17 @@ static int cow_create(const char *filename, QEMUOptionParameter *options)
options++; options++;
} }
ret = bdrv_create_file(filename, options); ret = bdrv_create_file(filename, options, &local_err);
if (ret < 0) { if (ret < 0) {
qerror_report_err(local_err);
error_free(local_err);
return ret; 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) { if (ret < 0) {
qerror_report_err(local_err);
error_free(local_err);
return ret; return ret;
} }

View file

@ -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; BDRVCURLState *s = bs->opaque;
CURLState *state = NULL; CURLState *state = NULL;

View file

@ -92,7 +92,8 @@ static int read_uint32(BlockDriverState *bs, int64_t offset, uint32_t *result)
return 0; 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; BDRVDMGState *s = bs->opaque;
uint64_t info_begin,info_end,last_in_offset,last_out_offset; uint64_t info_begin,info_end,last_in_offset,last_out_offset;

View file

@ -288,7 +288,7 @@ static QemuOptsList runtime_opts = {
}; };
static int qemu_gluster_open(BlockDriverState *bs, QDict *options, static int qemu_gluster_open(BlockDriverState *bs, QDict *options,
int bdrv_flags) int bdrv_flags, Error **errp)
{ {
BDRVGlusterState *s = bs->opaque; BDRVGlusterState *s = bs->opaque;
int open_flags = O_BINARY; int open_flags = O_BINARY;
@ -357,7 +357,7 @@ out:
} }
static int qemu_gluster_create(const char *filename, static int qemu_gluster_create(const char *filename,
QEMUOptionParameter *options) QEMUOptionParameter *options, Error **errp)
{ {
struct glfs *glfs; struct glfs *glfs;
struct glfs_fd *fd; struct glfs_fd *fd;

View file

@ -1216,7 +1216,8 @@ fail:
* We support iscsi url's on the form * We support iscsi url's on the form
* iscsi://[<username>%<password>@]<host>[:<port>]/<targetname>/<lun> * 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; IscsiLun *iscsilun = bs->opaque;
struct iscsi_context *iscsi = NULL; struct iscsi_context *iscsi = NULL;
@ -1447,7 +1448,8 @@ static int iscsi_has_zero_init(BlockDriverState *bs)
return 0; 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; int ret = 0;
int64_t total_size = 0; int64_t total_size = 0;
@ -1470,7 +1472,7 @@ static int iscsi_create(const char *filename, QEMUOptionParameter *options)
bs_options = qdict_new(); bs_options = qdict_new();
qdict_put(bs_options, "filename", qstring_from_str(filename)); 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); QDECREF(bs_options);
if (ret != 0) { if (ret != 0) {

View file

@ -505,14 +505,15 @@ static void mirror_iostatus_reset(BlockJob *job)
static void mirror_complete(BlockJob *job, Error **errp) static void mirror_complete(BlockJob *job, Error **errp)
{ {
MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
Error *local_err = NULL;
int ret; int ret;
ret = bdrv_open_backing_file(s->target, NULL); ret = bdrv_open_backing_file(s->target, NULL, &local_err);
if (ret < 0) { if (ret < 0) {
char backing_filename[PATH_MAX]; char backing_filename[PATH_MAX];
bdrv_get_full_backing_filename(s->target, backing_filename, bdrv_get_full_backing_filename(s->target, backing_filename,
sizeof(backing_filename)); sizeof(backing_filename));
error_setg_file_open(errp, -ret, backing_filename); error_propagate(errp, local_err);
return; return;
} }
if (!s->synced) { if (!s->synced) {

View file

@ -453,7 +453,8 @@ static void nbd_teardown_connection(BlockDriverState *bs)
closesocket(s->sock); 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; BDRVNBDState *s = bs->opaque;
int result; int result;

View file

@ -68,7 +68,8 @@ static int parallels_probe(const uint8_t *buf, int buf_size, const char *filenam
return 0; 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; BDRVParallelsState *s = bs->opaque;
int i; int i;

View file

@ -92,7 +92,8 @@ static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename)
return 0; 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; BDRVQcowState *s = bs->opaque;
int len, i, shift, ret; int len, i, shift, ret;
@ -658,7 +659,8 @@ static void qcow_close(BlockDriverState *bs)
error_free(s->migration_blocker); 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; int header_size, backing_filename_len, l1_size, shift, i;
QCowHeader header; QCowHeader header;
@ -666,6 +668,7 @@ static int qcow_create(const char *filename, QEMUOptionParameter *options)
int64_t total_size = 0; int64_t total_size = 0;
const char *backing_file = NULL; const char *backing_file = NULL;
int flags = 0; int flags = 0;
Error *local_err = NULL;
int ret; int ret;
BlockDriverState *qcow_bs; BlockDriverState *qcow_bs;
@ -681,13 +684,17 @@ static int qcow_create(const char *filename, QEMUOptionParameter *options)
options++; options++;
} }
ret = bdrv_create_file(filename, options); ret = bdrv_create_file(filename, options, &local_err);
if (ret < 0) { if (ret < 0) {
qerror_report_err(local_err);
error_free(local_err);
return ret; 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) { if (ret < 0) {
qerror_report_err(local_err);
error_free(local_err);
return ret; return ret;
} }

View file

@ -202,6 +202,24 @@ void qcow2_cache_depends_on_flush(Qcow2Cache *c)
c->depends_on_flush = true; 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) static int qcow2_cache_find_entry_to_replace(Qcow2Cache *c)
{ {
int i; int i;

View file

@ -1338,7 +1338,7 @@ 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) unsigned int nb_clusters, enum qcow2_discard_type type)
{ {
BDRVQcowState *s = bs->opaque; BDRVQcowState *s = bs->opaque;
uint64_t *l2_table; 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); l2_table[l2_index + i] = cpu_to_be64(0);
/* Then decrease the refcount */ /* 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); 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 qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset,
int nb_sectors) int nb_sectors, enum qcow2_discard_type type)
{ {
BDRVQcowState *s = bs->opaque; BDRVQcowState *s = bs->opaque;
uint64_t end_offset; 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 */ /* Each L2 table is handled by its own loop iteration */
while (nb_clusters > 0) { while (nb_clusters > 0) {
ret = discard_single_l2(bs, offset, nb_clusters); ret = discard_single_l2(bs, offset, nb_clusters, type);
if (ret < 0) { if (ret < 0) {
goto fail; goto fail;
} }
@ -1497,3 +1497,236 @@ fail:
return ret; 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;
}

View file

@ -601,10 +601,10 @@ fail:
* If the return value is non-negative, it is the new refcount of the cluster. * 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. * If it is negative, it is -errno and indicates an error.
*/ */
static int update_cluster_refcount(BlockDriverState *bs, int qcow2_update_cluster_refcount(BlockDriverState *bs,
int64_t cluster_index, int64_t cluster_index,
int addend, int addend,
enum qcow2_discard_type type) enum qcow2_discard_type type)
{ {
BDRVQcowState *s = bs->opaque; BDRVQcowState *s = bs->opaque;
int ret; int ret;
@ -733,8 +733,8 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size)
if (free_in_cluster == 0) if (free_in_cluster == 0)
s->free_byte_offset = 0; s->free_byte_offset = 0;
if ((offset & (s->cluster_size - 1)) != 0) if ((offset & (s->cluster_size - 1)) != 0)
update_cluster_refcount(bs, offset >> s->cluster_bits, 1, qcow2_update_cluster_refcount(bs, offset >> s->cluster_bits, 1,
QCOW2_DISCARD_NEVER); QCOW2_DISCARD_NEVER);
} else { } else {
offset = qcow2_alloc_clusters(bs, s->cluster_size); offset = qcow2_alloc_clusters(bs, s->cluster_size);
if (offset < 0) { if (offset < 0) {
@ -744,8 +744,8 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size)
if ((cluster_offset + s->cluster_size) == offset) { if ((cluster_offset + s->cluster_size) == offset) {
/* we are lucky: contiguous data */ /* we are lucky: contiguous data */
offset = s->free_byte_offset; offset = s->free_byte_offset;
update_cluster_refcount(bs, offset >> s->cluster_bits, 1, qcow2_update_cluster_refcount(bs, offset >> s->cluster_bits, 1,
QCOW2_DISCARD_NEVER); QCOW2_DISCARD_NEVER);
s->free_byte_offset += size; s->free_byte_offset += size;
} else { } else {
s->free_byte_offset = offset; 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() /* The cluster refcount was incremented, either by qcow2_alloc_clusters()
* or explicitly by update_cluster_refcount(). Refcount blocks must be * or explicitly by qcow2_update_cluster_refcount(). Refcount blocks must
* flushed before the caller's L2 table updates. * be flushed before the caller's L2 table updates.
*/ */
qcow2_cache_set_dependency(bs, s->l2_table_cache, s->refcount_block_cache); qcow2_cache_set_dependency(bs, s->l2_table_cache, s->refcount_block_cache);
return offset; return offset;
@ -896,8 +896,9 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
break; break;
} }
if (addend != 0) { if (addend != 0) {
refcount = update_cluster_refcount(bs, cluster_index, addend, refcount = qcow2_update_cluster_refcount(bs,
QCOW2_DISCARD_SNAPSHOT); cluster_index, addend,
QCOW2_DISCARD_SNAPSHOT);
} else { } else {
refcount = get_refcount(bs, cluster_index); refcount = get_refcount(bs, cluster_index);
} }
@ -936,8 +937,8 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
if (addend != 0) { if (addend != 0) {
refcount = update_cluster_refcount(bs, l2_offset >> s->cluster_bits, addend, refcount = qcow2_update_cluster_refcount(bs, l2_offset >>
QCOW2_DISCARD_SNAPSHOT); s->cluster_bits, addend, QCOW2_DISCARD_SNAPSHOT);
} else { } else {
refcount = get_refcount(bs, l2_offset >> s->cluster_bits); refcount = get_refcount(bs, l2_offset >> s->cluster_bits);
} }

View file

@ -297,31 +297,47 @@ static void find_new_snapshot_id(BlockDriverState *bs,
snprintf(id_str, id_str_size, "%d", id_max + 1); 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; BDRVQcowState *s = bs->opaque;
int i; int i;
for(i = 0; i < s->nb_snapshots; i++) { if (id && name) {
if (!strcmp(s->snapshots[i].id_str, id_str)) for (i = 0; i < s->nb_snapshots; i++) {
return 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; 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 ret;
int i, ret;
ret = find_snapshot_by_id(bs, name); ret = find_snapshot_by_id_and_name(bs, id_or_name, NULL);
if (ret >= 0) if (ret >= 0) {
return ret; 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 */ /* 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 */ /* 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; return -EEXIST;
} }
@ -416,6 +432,13 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
g_free(old_snapshot_list); 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 #ifdef DEBUG_ALLOC
{ {
BdrvCheckResult result = {0}; BdrvCheckResult result = {0};
@ -553,15 +576,19 @@ fail:
return ret; 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; BDRVQcowState *s = bs->opaque;
QCowSnapshot sn; QCowSnapshot sn;
int snapshot_index, ret; int snapshot_index, ret;
/* Search the snapshot */ /* 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) { if (snapshot_index < 0) {
error_setg(errp, "Can't find the snapshot");
return -ENOENT; return -ENOENT;
} }
sn = s->snapshots[snapshot_index]; sn = s->snapshots[snapshot_index];
@ -573,6 +600,7 @@ int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id)
s->nb_snapshots--; s->nb_snapshots--;
ret = qcow2_write_snapshots(bs); ret = qcow2_write_snapshots(bs);
if (ret < 0) { if (ret < 0) {
error_setg(errp, "Failed to remove snapshot from snapshot list");
return ret; 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, ret = qcow2_update_snapshot_refcount(bs, sn.l1_table_offset,
sn.l1_size, -1); sn.l1_size, -1);
if (ret < 0) { if (ret < 0) {
error_setg(errp, "Failed to free the cluster and L1 table");
return ret; return ret;
} }
qcow2_free_clusters(bs, sn.l1_table_offset, sn.l1_size * sizeof(uint64_t), 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 */ /* must update the copied flag on the current cluster offsets */
ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 0); ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 0);
if (ret < 0) { if (ret < 0) {
error_setg(errp, "Failed to update snapshot status in disk");
return ret; return ret;
} }

View file

@ -79,7 +79,8 @@ static int qcow2_probe(const uint8_t *buf, int buf_size, const char *filename)
* return 0 upon success, non-0 otherwise * return 0 upon success, non-0 otherwise
*/ */
static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, 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)
{ {
BDRVQcowState *s = bs->opaque; BDRVQcowState *s = bs->opaque;
QCowExtension ext; QCowExtension ext;
@ -100,10 +101,10 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset,
printf("attempting to read extended header in offset %lu\n", offset); printf("attempting to read extended header in offset %lu\n", offset);
#endif #endif
if (bdrv_pread(bs->file, offset, &ext, sizeof(ext)) != sizeof(ext)) { ret = bdrv_pread(bs->file, offset, &ext, sizeof(ext));
fprintf(stderr, "qcow2_read_extension: ERROR: " if (ret < 0) {
"pread fail from offset %" PRIu64 "\n", error_setg_errno(errp, -ret, "qcow2_read_extension: ERROR: "
offset); "pread fail from offset %" PRIu64, offset);
return 1; return 1;
} }
be32_to_cpus(&ext.magic); be32_to_cpus(&ext.magic);
@ -113,7 +114,7 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset,
printf("ext.magic = 0x%x\n", ext.magic); printf("ext.magic = 0x%x\n", ext.magic);
#endif #endif
if (ext.len > end_offset - offset) { if (ext.len > end_offset - offset) {
error_report("Header extension too large"); error_setg(errp, "Header extension too large");
return -EINVAL; return -EINVAL;
} }
@ -123,14 +124,16 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset,
case QCOW2_EXT_MAGIC_BACKING_FORMAT: case QCOW2_EXT_MAGIC_BACKING_FORMAT:
if (ext.len >= sizeof(bs->backing_format)) { if (ext.len >= sizeof(bs->backing_format)) {
fprintf(stderr, "ERROR: ext_backing_format: len=%u too large" error_setg(errp, "ERROR: ext_backing_format: len=%u too large"
" (>=%zu)\n", " (>=%zu)", ext.len, sizeof(bs->backing_format));
ext.len, sizeof(bs->backing_format));
return 2; return 2;
} }
if (bdrv_pread(bs->file, offset , bs->backing_format, ret = bdrv_pread(bs->file, offset, bs->backing_format, ext.len);
ext.len) != ext.len) if (ret < 0) {
error_setg_errno(errp, -ret, "ERROR: ext_backing_format: "
"Could not read format name");
return 3; return 3;
}
bs->backing_format[ext.len] = '\0'; bs->backing_format[ext.len] = '\0';
#ifdef DEBUG_EXT #ifdef DEBUG_EXT
printf("Qcow2: Got format extension %s\n", bs->backing_format); printf("Qcow2: Got format extension %s\n", bs->backing_format);
@ -142,6 +145,8 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset,
void* feature_table = g_malloc0(ext.len + 2 * sizeof(Qcow2Feature)); void* feature_table = g_malloc0(ext.len + 2 * sizeof(Qcow2Feature));
ret = bdrv_pread(bs->file, offset , feature_table, ext.len); ret = bdrv_pread(bs->file, offset , feature_table, ext.len);
if (ret < 0) { if (ret < 0) {
error_setg_errno(errp, -ret, "ERROR: ext_feature_table: "
"Could not read table");
return ret; return ret;
} }
@ -161,6 +166,8 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset,
ret = bdrv_pread(bs->file, offset , uext->data, uext->len); ret = bdrv_pread(bs->file, offset , uext->data, uext->len);
if (ret < 0) { if (ret < 0) {
error_setg_errno(errp, -ret, "ERROR: unknown extension: "
"Could not read data");
return ret; return ret;
} }
} }
@ -184,8 +191,8 @@ static void cleanup_unknown_header_ext(BlockDriverState *bs)
} }
} }
static void GCC_FMT_ATTR(2, 3) report_unsupported(BlockDriverState *bs, static void GCC_FMT_ATTR(3, 4) report_unsupported(BlockDriverState *bs,
const char *fmt, ...) Error **errp, const char *fmt, ...)
{ {
char msg[64]; char msg[64];
va_list ap; va_list ap;
@ -194,17 +201,17 @@ static void GCC_FMT_ATTR(2, 3) report_unsupported(BlockDriverState *bs,
vsnprintf(msg, sizeof(msg), fmt, ap); vsnprintf(msg, sizeof(msg), fmt, ap);
va_end(ap); va_end(ap);
qerror_report(QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, error_set(errp, QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, bs->device_name, "qcow2",
bs->device_name, "qcow2", msg); msg);
} }
static void report_unsupported_feature(BlockDriverState *bs, static void report_unsupported_feature(BlockDriverState *bs,
Qcow2Feature *table, uint64_t mask) Error **errp, Qcow2Feature *table, uint64_t mask)
{ {
while (table && table->name[0] != '\0') { while (table && table->name[0] != '\0') {
if (table->type == QCOW2_FEAT_TYPE_INCOMPATIBLE) { if (table->type == QCOW2_FEAT_TYPE_INCOMPATIBLE) {
if (mask & (1 << table->bit)) { if (mask & (1 << table->bit)) {
report_unsupported(bs, "%.46s",table->name); report_unsupported(bs, errp, "%.46s", table->name);
mask &= ~(1 << table->bit); mask &= ~(1 << table->bit);
} }
} }
@ -212,7 +219,8 @@ static void report_unsupported_feature(BlockDriverState *bs,
} }
if (mask) { if (mask) {
report_unsupported(bs, "Unknown incompatible feature: %" PRIx64, mask); report_unsupported(bs, errp, "Unknown incompatible feature: %" PRIx64,
mask);
} }
} }
@ -350,7 +358,8 @@ static QemuOptsList qcow2_runtime_opts = {
}, },
}; };
static int qcow2_open(BlockDriverState *bs, QDict *options, int flags) static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp)
{ {
BDRVQcowState *s = bs->opaque; BDRVQcowState *s = bs->opaque;
int len, i, ret = 0; int len, i, ret = 0;
@ -362,6 +371,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
ret = bdrv_pread(bs->file, 0, &header, sizeof(header)); ret = bdrv_pread(bs->file, 0, &header, sizeof(header));
if (ret < 0) { if (ret < 0) {
error_setg_errno(errp, -ret, "Could not read qcow2 header");
goto fail; goto fail;
} }
be32_to_cpus(&header.magic); be32_to_cpus(&header.magic);
@ -379,11 +389,12 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
be32_to_cpus(&header.nb_snapshots); be32_to_cpus(&header.nb_snapshots);
if (header.magic != QCOW_MAGIC) { if (header.magic != QCOW_MAGIC) {
error_setg(errp, "Image is not in qcow2 format");
ret = -EMEDIUMTYPE; ret = -EMEDIUMTYPE;
goto fail; goto fail;
} }
if (header.version < 2 || header.version > 3) { if (header.version < 2 || header.version > 3) {
report_unsupported(bs, "QCOW version %d", header.version); report_unsupported(bs, errp, "QCOW version %d", header.version);
ret = -ENOTSUP; ret = -ENOTSUP;
goto fail; goto fail;
} }
@ -411,6 +422,8 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
ret = bdrv_pread(bs->file, sizeof(header), s->unknown_header_fields, ret = bdrv_pread(bs->file, sizeof(header), s->unknown_header_fields,
s->unknown_header_fields_size); s->unknown_header_fields_size);
if (ret < 0) { if (ret < 0) {
error_setg_errno(errp, -ret, "Could not read unknown qcow2 header "
"fields");
goto fail; goto fail;
} }
} }
@ -429,8 +442,8 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
if (s->incompatible_features & ~QCOW2_INCOMPAT_MASK) { if (s->incompatible_features & ~QCOW2_INCOMPAT_MASK) {
void *feature_table = NULL; void *feature_table = NULL;
qcow2_read_extensions(bs, header.header_length, ext_end, qcow2_read_extensions(bs, header.header_length, ext_end,
&feature_table); &feature_table, NULL);
report_unsupported_feature(bs, feature_table, report_unsupported_feature(bs, errp, feature_table,
s->incompatible_features & s->incompatible_features &
~QCOW2_INCOMPAT_MASK); ~QCOW2_INCOMPAT_MASK);
ret = -ENOTSUP; ret = -ENOTSUP;
@ -441,8 +454,8 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
/* Corrupt images may not be written to unless they are being repaired /* Corrupt images may not be written to unless they are being repaired
*/ */
if ((flags & BDRV_O_RDWR) && !(flags & BDRV_O_CHECK)) { if ((flags & BDRV_O_RDWR) && !(flags & BDRV_O_CHECK)) {
error_report("qcow2: Image is corrupt; cannot be opened " error_setg(errp, "qcow2: Image is corrupt; cannot be opened "
"read/write."); "read/write");
ret = -EACCES; ret = -EACCES;
goto fail; goto fail;
} }
@ -450,18 +463,22 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
/* Check support for various header values */ /* Check support for various header values */
if (header.refcount_order != 4) { if (header.refcount_order != 4) {
report_unsupported(bs, "%d bit reference counts", report_unsupported(bs, errp, "%d bit reference counts",
1 << header.refcount_order); 1 << header.refcount_order);
ret = -ENOTSUP; ret = -ENOTSUP;
goto fail; goto fail;
} }
s->refcount_order = header.refcount_order;
if (header.cluster_bits < MIN_CLUSTER_BITS || if (header.cluster_bits < MIN_CLUSTER_BITS ||
header.cluster_bits > MAX_CLUSTER_BITS) { header.cluster_bits > MAX_CLUSTER_BITS) {
error_setg(errp, "Unsupported cluster size: 2^%i", header.cluster_bits);
ret = -EINVAL; ret = -EINVAL;
goto fail; goto fail;
} }
if (header.crypt_method > QCOW_CRYPT_AES) { if (header.crypt_method > QCOW_CRYPT_AES) {
error_setg(errp, "Unsupported encryption method: %i",
header.crypt_method);
ret = -EINVAL; ret = -EINVAL;
goto fail; goto fail;
} }
@ -490,6 +507,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
l1_vm_state_index = size_to_l1(s, header.size); l1_vm_state_index = size_to_l1(s, header.size);
if (l1_vm_state_index > INT_MAX) { if (l1_vm_state_index > INT_MAX) {
error_setg(errp, "Image is too big");
ret = -EFBIG; ret = -EFBIG;
goto fail; goto fail;
} }
@ -498,6 +516,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
/* the L1 table must contain at least enough entries to put /* the L1 table must contain at least enough entries to put
header.size bytes */ header.size bytes */
if (s->l1_size < s->l1_vm_state_index) { if (s->l1_size < s->l1_vm_state_index) {
error_setg(errp, "L1 table is too small");
ret = -EINVAL; ret = -EINVAL;
goto fail; goto fail;
} }
@ -508,6 +527,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
ret = bdrv_pread(bs->file, s->l1_table_offset, s->l1_table, ret = bdrv_pread(bs->file, s->l1_table_offset, s->l1_table,
s->l1_size * sizeof(uint64_t)); s->l1_size * sizeof(uint64_t));
if (ret < 0) { if (ret < 0) {
error_setg_errno(errp, -ret, "Could not read L1 table");
goto fail; goto fail;
} }
for(i = 0;i < s->l1_size; i++) { for(i = 0;i < s->l1_size; i++) {
@ -528,6 +548,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
ret = qcow2_refcount_init(bs); ret = qcow2_refcount_init(bs);
if (ret != 0) { if (ret != 0) {
error_setg_errno(errp, -ret, "Could not initialize refcount handling");
goto fail; goto fail;
} }
@ -535,7 +556,9 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
QTAILQ_INIT(&s->discards); QTAILQ_INIT(&s->discards);
/* read qcow2 extensions */ /* read qcow2 extensions */
if (qcow2_read_extensions(bs, header.header_length, ext_end, NULL)) { if (qcow2_read_extensions(bs, header.header_length, ext_end, NULL,
&local_err)) {
error_propagate(errp, local_err);
ret = -EINVAL; ret = -EINVAL;
goto fail; goto fail;
} }
@ -549,6 +572,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
ret = bdrv_pread(bs->file, header.backing_file_offset, ret = bdrv_pread(bs->file, header.backing_file_offset,
bs->backing_file, len); bs->backing_file, len);
if (ret < 0) { if (ret < 0) {
error_setg_errno(errp, -ret, "Could not read backing file name");
goto fail; goto fail;
} }
bs->backing_file[len] = '\0'; bs->backing_file[len] = '\0';
@ -556,6 +580,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
ret = qcow2_read_snapshots(bs); ret = qcow2_read_snapshots(bs);
if (ret < 0) { if (ret < 0) {
error_setg_errno(errp, -ret, "Could not read snapshots");
goto fail; goto fail;
} }
@ -564,6 +589,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
s->autoclear_features = 0; s->autoclear_features = 0;
ret = qcow2_update_header(bs); ret = qcow2_update_header(bs);
if (ret < 0) { if (ret < 0) {
error_setg_errno(errp, -ret, "Could not update qcow2 header");
goto fail; goto fail;
} }
} }
@ -578,6 +604,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
ret = qcow2_check(bs, &result, BDRV_FIX_ERRORS); ret = qcow2_check(bs, &result, BDRV_FIX_ERRORS);
if (ret < 0) { if (ret < 0) {
error_setg_errno(errp, -ret, "Could not repair dirty image");
goto fail; goto fail;
} }
} }
@ -586,8 +613,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
opts = qemu_opts_create_nofail(&qcow2_runtime_opts); opts = qemu_opts_create_nofail(&qcow2_runtime_opts);
qemu_opts_absorb_qdict(opts, options, &local_err); qemu_opts_absorb_qdict(opts, options, &local_err);
if (error_is_set(&local_err)) { if (error_is_set(&local_err)) {
qerror_report_err(local_err); error_propagate(errp, local_err);
error_free(local_err);
ret = -EINVAL; ret = -EINVAL;
goto fail; goto fail;
} }
@ -608,8 +634,8 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
qemu_opts_del(opts); qemu_opts_del(opts);
if (s->use_lazy_refcounts && s->qcow_version < 3) { if (s->use_lazy_refcounts && s->qcow_version < 3) {
qerror_report(ERROR_CLASS_GENERIC_ERROR, "Lazy refcounts require " error_setg(errp, "Lazy refcounts require a qcow2 image with at least "
"a qcow2 image with at least qemu 1.1 compatibility level"); "qemu 1.1 compatibility level");
ret = -EINVAL; ret = -EINVAL;
goto fail; goto fail;
} }
@ -1059,7 +1085,7 @@ static void qcow2_invalidate_cache(BlockDriverState *bs)
qbool_from_int(s->use_lazy_refcounts)); qbool_from_int(s->use_lazy_refcounts));
memset(s, 0, sizeof(BDRVQcowState)); memset(s, 0, sizeof(BDRVQcowState));
qcow2_open(bs, options, flags); qcow2_open(bs, options, flags, NULL);
QDECREF(options); QDECREF(options);
@ -1143,7 +1169,7 @@ int qcow2_update_header(BlockDriverState *bs)
.incompatible_features = cpu_to_be64(s->incompatible_features), .incompatible_features = cpu_to_be64(s->incompatible_features),
.compatible_features = cpu_to_be64(s->compatible_features), .compatible_features = cpu_to_be64(s->compatible_features),
.autoclear_features = cpu_to_be64(s->autoclear_features), .autoclear_features = cpu_to_be64(s->autoclear_features),
.refcount_order = cpu_to_be32(3 + REFCOUNT_SHIFT), .refcount_order = cpu_to_be32(s->refcount_order),
.header_length = cpu_to_be32(header_length), .header_length = cpu_to_be32(header_length),
}; };
@ -1332,7 +1358,8 @@ static int preallocate(BlockDriverState *bs)
static int qcow2_create2(const char *filename, int64_t total_size, static int qcow2_create2(const char *filename, int64_t total_size,
const char *backing_file, const char *backing_format, const char *backing_file, const char *backing_format,
int flags, size_t cluster_size, int prealloc, int flags, size_t cluster_size, int prealloc,
QEMUOptionParameter *options, int version) QEMUOptionParameter *options, int version,
Error **errp)
{ {
/* Calculate cluster_bits */ /* Calculate cluster_bits */
int cluster_bits; int cluster_bits;
@ -1340,9 +1367,8 @@ static int qcow2_create2(const char *filename, int64_t total_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)
{ {
error_report( error_setg(errp, "Cluster size must be a power of two between %d and "
"Cluster size must be a power of two between %d and %dk", "%dk", 1 << MIN_CLUSTER_BITS, 1 << (MAX_CLUSTER_BITS - 10));
1 << MIN_CLUSTER_BITS, 1 << (MAX_CLUSTER_BITS - 10));
return -EINVAL; return -EINVAL;
} }
@ -1361,15 +1387,18 @@ static int qcow2_create2(const char *filename, int64_t total_size,
BlockDriverState* bs; BlockDriverState* bs;
QCowHeader header; QCowHeader header;
uint8_t* refcount_table; uint8_t* refcount_table;
Error *local_err = NULL;
int ret; int ret;
ret = bdrv_create_file(filename, options); ret = bdrv_create_file(filename, options, &local_err);
if (ret < 0) { if (ret < 0) {
error_propagate(errp, local_err);
return ret; return ret;
} }
ret = bdrv_file_open(&bs, filename, NULL, BDRV_O_RDWR); ret = bdrv_file_open(&bs, filename, NULL, BDRV_O_RDWR, &local_err);
if (ret < 0) { if (ret < 0) {
error_propagate(errp, local_err);
return ret; return ret;
} }
@ -1399,6 +1428,7 @@ static int qcow2_create2(const char *filename, int64_t total_size,
ret = bdrv_pwrite(bs, 0, &header, sizeof(header)); ret = bdrv_pwrite(bs, 0, &header, sizeof(header));
if (ret < 0) { if (ret < 0) {
error_setg_errno(errp, -ret, "Could not write qcow2 header");
goto out; goto out;
} }
@ -1408,6 +1438,7 @@ static int qcow2_create2(const char *filename, int64_t total_size,
g_free(refcount_table); g_free(refcount_table);
if (ret < 0) { if (ret < 0) {
error_setg_errno(errp, -ret, "Could not write refcount table");
goto out; goto out;
} }
@ -1421,13 +1452,16 @@ static int qcow2_create2(const char *filename, int64_t total_size,
BlockDriver* drv = bdrv_find_format("qcow2"); BlockDriver* drv = bdrv_find_format("qcow2");
assert(drv != NULL); assert(drv != NULL);
ret = bdrv_open(bs, filename, NULL, ret = bdrv_open(bs, filename, NULL,
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, drv); BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, drv, &local_err);
if (ret < 0) { if (ret < 0) {
error_propagate(errp, local_err);
goto out; goto out;
} }
ret = qcow2_alloc_clusters(bs, 2 * cluster_size); ret = qcow2_alloc_clusters(bs, 2 * cluster_size);
if (ret < 0) { if (ret < 0) {
error_setg_errno(errp, -ret, "Could not allocate clusters for qcow2 "
"header and refcount table");
goto out; goto out;
} else if (ret != 0) { } else if (ret != 0) {
@ -1438,6 +1472,7 @@ static int qcow2_create2(const char *filename, int64_t total_size,
/* Okay, now that we have a valid image, let's give it the right size */ /* Okay, now that we have a valid image, let's give it the right size */
ret = bdrv_truncate(bs, total_size * BDRV_SECTOR_SIZE); ret = bdrv_truncate(bs, total_size * BDRV_SECTOR_SIZE);
if (ret < 0) { if (ret < 0) {
error_setg_errno(errp, -ret, "Could not resize image");
goto out; goto out;
} }
@ -1445,6 +1480,8 @@ static int qcow2_create2(const char *filename, int64_t total_size,
if (backing_file) { if (backing_file) {
ret = bdrv_change_backing_file(bs, backing_file, backing_format); ret = bdrv_change_backing_file(bs, backing_file, backing_format);
if (ret < 0) { if (ret < 0) {
error_setg_errno(errp, -ret, "Could not assign backing file '%s' "
"with format '%s'", backing_file, backing_format);
goto out; goto out;
} }
} }
@ -1456,6 +1493,7 @@ static int qcow2_create2(const char *filename, int64_t total_size,
ret = preallocate(bs); ret = preallocate(bs);
qemu_co_mutex_unlock(&s->lock); qemu_co_mutex_unlock(&s->lock);
if (ret < 0) { if (ret < 0) {
error_setg_errno(errp, -ret, "Could not preallocate metadata");
goto out; goto out;
} }
} }
@ -1466,7 +1504,8 @@ out:
return ret; return ret;
} }
static int qcow2_create(const char *filename, QEMUOptionParameter *options) static int qcow2_create(const char *filename, QEMUOptionParameter *options,
Error **errp)
{ {
const char *backing_file = NULL; const char *backing_file = NULL;
const char *backing_fmt = NULL; const char *backing_fmt = NULL;
@ -1475,6 +1514,8 @@ static int qcow2_create(const char *filename, QEMUOptionParameter *options)
size_t cluster_size = DEFAULT_CLUSTER_SIZE; size_t cluster_size = DEFAULT_CLUSTER_SIZE;
int prealloc = 0; int prealloc = 0;
int version = 3; int version = 3;
Error *local_err = NULL;
int ret;
/* Read out options */ /* Read out options */
while (options && options->name) { while (options && options->name) {
@ -1496,8 +1537,8 @@ static int qcow2_create(const char *filename, QEMUOptionParameter *options)
} else if (!strcmp(options->value.s, "metadata")) { } else if (!strcmp(options->value.s, "metadata")) {
prealloc = 1; prealloc = 1;
} else { } else {
fprintf(stderr, "Invalid preallocation mode: '%s'\n", error_setg(errp, "Invalid preallocation mode: '%s'",
options->value.s); options->value.s);
return -EINVAL; return -EINVAL;
} }
} else if (!strcmp(options->name, BLOCK_OPT_COMPAT_LEVEL)) { } else if (!strcmp(options->name, BLOCK_OPT_COMPAT_LEVEL)) {
@ -1508,8 +1549,8 @@ static int qcow2_create(const char *filename, QEMUOptionParameter *options)
} else if (!strcmp(options->value.s, "1.1")) { } else if (!strcmp(options->value.s, "1.1")) {
version = 3; version = 3;
} else { } else {
fprintf(stderr, "Invalid compatibility level: '%s'\n", error_setg(errp, "Invalid compatibility level: '%s'",
options->value.s); options->value.s);
return -EINVAL; return -EINVAL;
} }
} else if (!strcmp(options->name, BLOCK_OPT_LAZY_REFCOUNTS)) { } else if (!strcmp(options->name, BLOCK_OPT_LAZY_REFCOUNTS)) {
@ -1519,19 +1560,23 @@ static int qcow2_create(const char *filename, QEMUOptionParameter *options)
} }
if (backing_file && prealloc) { if (backing_file && prealloc) {
fprintf(stderr, "Backing file and preallocation cannot be used at " error_setg(errp, "Backing file and preallocation cannot be used at "
"the same time\n"); "the same time");
return -EINVAL; return -EINVAL;
} }
if (version < 3 && (flags & BLOCK_FLAG_LAZY_REFCOUNTS)) { if (version < 3 && (flags & BLOCK_FLAG_LAZY_REFCOUNTS)) {
fprintf(stderr, "Lazy refcounts only supported with compatibility " error_setg(errp, "Lazy refcounts only supported with compatibility "
"level 1.1 and above (use compat=1.1 or greater)\n"); "level 1.1 and above (use compat=1.1 or greater)");
return -EINVAL; return -EINVAL;
} }
return qcow2_create2(filename, sectors, backing_file, backing_fmt, flags, ret = qcow2_create2(filename, sectors, backing_file, backing_fmt, flags,
cluster_size, prealloc, options, version); cluster_size, prealloc, options, version, &local_err);
if (error_is_set(&local_err)) {
error_propagate(errp, local_err);
}
return ret;
} }
static int qcow2_make_empty(BlockDriverState *bs) static int qcow2_make_empty(BlockDriverState *bs)
@ -1582,7 +1627,7 @@ static coroutine_fn int qcow2_co_discard(BlockDriverState *bs,
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,
nb_sectors); nb_sectors, QCOW2_DISCARD_REQUEST);
qemu_co_mutex_unlock(&s->lock); qemu_co_mutex_unlock(&s->lock);
return ret; return ret;
} }
@ -1757,11 +1802,6 @@ static coroutine_fn int qcow2_co_flush_to_os(BlockDriverState *bs)
return 0; return 0;
} }
static int64_t qcow2_vm_state_offset(BDRVQcowState *s)
{
return (int64_t)s->l1_vm_state_index << (s->cluster_bits + s->l2_bits);
}
static int qcow2_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) static int qcow2_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
{ {
BDRVQcowState *s = bs->opaque; BDRVQcowState *s = bs->opaque;
@ -1824,6 +1864,199 @@ static int qcow2_load_vmstate(BlockDriverState *bs, uint8_t *buf,
return ret; return ret;
} }
/*
* Downgrades an image's version. To achieve this, any incompatible features
* have to be removed.
*/
static int qcow2_downgrade(BlockDriverState *bs, int target_version)
{
BDRVQcowState *s = bs->opaque;
int current_version = s->qcow_version;
int ret;
if (target_version == current_version) {
return 0;
} else if (target_version > current_version) {
return -EINVAL;
} else if (target_version != 2) {
return -EINVAL;
}
if (s->refcount_order != 4) {
/* we would have to convert the image to a refcount_order == 4 image
* here; however, since qemu (at the time of writing this) does not
* support anything different than 4 anyway, there is no point in doing
* so right now; however, we should error out (if qemu supports this in
* the future and this code has not been adapted) */
error_report("qcow2_downgrade: Image refcount orders other than 4 are"
"currently not supported.");
return -ENOTSUP;
}
/* clear incompatible features */
if (s->incompatible_features & QCOW2_INCOMPAT_DIRTY) {
ret = qcow2_mark_clean(bs);
if (ret < 0) {
return ret;
}
}
/* with QCOW2_INCOMPAT_CORRUPT, it is pretty much impossible to get here in
* the first place; if that happens nonetheless, returning -ENOTSUP is the
* best thing to do anyway */
if (s->incompatible_features) {
return -ENOTSUP;
}
/* since we can ignore compatible features, we can set them to 0 as well */
s->compatible_features = 0;
/* if lazy refcounts have been used, they have already been fixed through
* clearing the dirty flag */
/* clearing autoclear features is trivial */
s->autoclear_features = 0;
ret = qcow2_expand_zero_clusters(bs);
if (ret < 0) {
return ret;
}
s->qcow_version = target_version;
ret = qcow2_update_header(bs);
if (ret < 0) {
s->qcow_version = current_version;
return ret;
}
return 0;
}
static int qcow2_amend_options(BlockDriverState *bs,
QEMUOptionParameter *options)
{
BDRVQcowState *s = bs->opaque;
int old_version = s->qcow_version, new_version = old_version;
uint64_t new_size = 0;
const char *backing_file = NULL, *backing_format = NULL;
bool lazy_refcounts = s->use_lazy_refcounts;
int ret;
int i;
for (i = 0; options[i].name; i++)
{
if (!options[i].assigned) {
/* only change explicitly defined options */
continue;
}
if (!strcmp(options[i].name, "compat")) {
if (!options[i].value.s) {
/* preserve default */
} else if (!strcmp(options[i].value.s, "0.10")) {
new_version = 2;
} else if (!strcmp(options[i].value.s, "1.1")) {
new_version = 3;
} else {
fprintf(stderr, "Unknown compatibility level %s.\n",
options[i].value.s);
return -EINVAL;
}
} else if (!strcmp(options[i].name, "preallocation")) {
fprintf(stderr, "Cannot change preallocation mode.\n");
return -ENOTSUP;
} else if (!strcmp(options[i].name, "size")) {
new_size = options[i].value.n;
} else if (!strcmp(options[i].name, "backing_file")) {
backing_file = options[i].value.s;
} else if (!strcmp(options[i].name, "backing_fmt")) {
backing_format = options[i].value.s;
} else if (!strcmp(options[i].name, "encryption")) {
if ((options[i].value.n != !!s->crypt_method)) {
fprintf(stderr, "Changing the encryption flag is not "
"supported.\n");
return -ENOTSUP;
}
} else if (!strcmp(options[i].name, "cluster_size")) {
if (options[i].value.n != s->cluster_size) {
fprintf(stderr, "Changing the cluster size is not "
"supported.\n");
return -ENOTSUP;
}
} else if (!strcmp(options[i].name, "lazy_refcounts")) {
lazy_refcounts = options[i].value.n;
} else {
/* if this assertion fails, this probably means a new option was
* added without having it covered here */
assert(false);
}
}
if (new_version != old_version) {
if (new_version > old_version) {
/* Upgrade */
s->qcow_version = new_version;
ret = qcow2_update_header(bs);
if (ret < 0) {
s->qcow_version = old_version;
return ret;
}
} else {
ret = qcow2_downgrade(bs, new_version);
if (ret < 0) {
return ret;
}
}
}
if (backing_file || backing_format) {
ret = qcow2_change_backing_file(bs, backing_file ?: bs->backing_file,
backing_format ?: bs->backing_format);
if (ret < 0) {
return ret;
}
}
if (s->use_lazy_refcounts != lazy_refcounts) {
if (lazy_refcounts) {
if (s->qcow_version < 3) {
fprintf(stderr, "Lazy refcounts only supported with compatibility "
"level 1.1 and above (use compat=1.1 or greater)\n");
return -EINVAL;
}
s->compatible_features |= QCOW2_COMPAT_LAZY_REFCOUNTS;
ret = qcow2_update_header(bs);
if (ret < 0) {
s->compatible_features &= ~QCOW2_COMPAT_LAZY_REFCOUNTS;
return ret;
}
s->use_lazy_refcounts = true;
} else {
/* make image clean first */
ret = qcow2_mark_clean(bs);
if (ret < 0) {
return ret;
}
/* now disallow lazy refcounts */
s->compatible_features &= ~QCOW2_COMPAT_LAZY_REFCOUNTS;
ret = qcow2_update_header(bs);
if (ret < 0) {
s->compatible_features |= QCOW2_COMPAT_LAZY_REFCOUNTS;
return ret;
}
s->use_lazy_refcounts = false;
}
}
if (new_size) {
ret = bdrv_truncate(bs, new_size);
if (ret < 0) {
return ret;
}
}
return 0;
}
static QEMUOptionParameter qcow2_create_options[] = { static QEMUOptionParameter qcow2_create_options[] = {
{ {
.name = BLOCK_OPT_SIZE, .name = BLOCK_OPT_SIZE,
@ -1907,6 +2140,7 @@ static BlockDriver bdrv_qcow2 = {
.create_options = qcow2_create_options, .create_options = qcow2_create_options,
.bdrv_check = qcow2_check, .bdrv_check = qcow2_check,
.bdrv_amend_options = qcow2_amend_options,
}; };
static void bdrv_qcow2_init(void) static void bdrv_qcow2_init(void)

View file

@ -199,6 +199,7 @@ typedef struct BDRVQcowState {
int flags; int flags;
int qcow_version; int qcow_version;
bool use_lazy_refcounts; bool use_lazy_refcounts;
int refcount_order;
bool discard_passthrough[QCOW2_DISCARD_MAX]; bool discard_passthrough[QCOW2_DISCARD_MAX];
@ -361,6 +362,11 @@ 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)
{
return (int64_t)s->l1_vm_state_index << (s->cluster_bits + s->l2_bits);
}
static inline int qcow2_get_cluster_type(uint64_t l2_entry) static inline int qcow2_get_cluster_type(uint64_t l2_entry)
{ {
if (l2_entry & QCOW_OFLAG_COMPRESSED) { if (l2_entry & QCOW_OFLAG_COMPRESSED) {
@ -406,6 +412,9 @@ int qcow2_update_header(BlockDriverState *bs);
int qcow2_refcount_init(BlockDriverState *bs); int qcow2_refcount_init(BlockDriverState *bs);
void qcow2_refcount_close(BlockDriverState *bs); void qcow2_refcount_close(BlockDriverState *bs);
int qcow2_update_cluster_refcount(BlockDriverState *bs, int64_t cluster_index,
int addend, enum qcow2_discard_type type);
int64_t qcow2_alloc_clusters(BlockDriverState *bs, int64_t size); int64_t qcow2_alloc_clusters(BlockDriverState *bs, int64_t size);
int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset, int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset,
int nb_clusters); int nb_clusters);
@ -450,13 +459,18 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m); int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m);
int qcow2_discard_clusters(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);
int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors); int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors);
int qcow2_expand_zero_clusters(BlockDriverState *bs);
/* qcow2-snapshot.c functions */ /* qcow2-snapshot.c functions */
int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info); int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info);
int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id); int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id);
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);
int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab); int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab);
int qcow2_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_name); int qcow2_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_name);
@ -473,6 +487,8 @@ int qcow2_cache_set_dependency(BlockDriverState *bs, Qcow2Cache *c,
Qcow2Cache *dependency); Qcow2Cache *dependency);
void qcow2_cache_depends_on_flush(Qcow2Cache *c); void qcow2_cache_depends_on_flush(Qcow2Cache *c);
int qcow2_cache_empty(BlockDriverState *bs, Qcow2Cache *c);
int qcow2_cache_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, int qcow2_cache_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset,
void **table); void **table);
int qcow2_cache_get_empty(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, int qcow2_cache_get_empty(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset,

View file

@ -373,7 +373,8 @@ static void bdrv_qed_rebind(BlockDriverState *bs)
s->bs = bs; s->bs = bs;
} }
static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags) static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp)
{ {
BDRVQEDState *s = bs->opaque; BDRVQEDState *s = bs->opaque;
QEDHeader le_header; QEDHeader le_header;
@ -550,16 +551,22 @@ static int qed_create(const char *filename, uint32_t cluster_size,
QEDHeader le_header; QEDHeader le_header;
uint8_t *l1_table = NULL; uint8_t *l1_table = NULL;
size_t l1_size = header.cluster_size * header.table_size; size_t l1_size = header.cluster_size * header.table_size;
Error *local_err = NULL;
int ret = 0; int ret = 0;
BlockDriverState *bs = NULL; BlockDriverState *bs = NULL;
ret = bdrv_create_file(filename, NULL); ret = bdrv_create_file(filename, NULL, &local_err);
if (ret < 0) { if (ret < 0) {
qerror_report_err(local_err);
error_free(local_err);
return ret; return ret;
} }
ret = bdrv_file_open(&bs, filename, NULL, BDRV_O_RDWR | BDRV_O_CACHE_WB); ret = bdrv_file_open(&bs, filename, NULL, BDRV_O_RDWR | BDRV_O_CACHE_WB,
&local_err);
if (ret < 0) { if (ret < 0) {
qerror_report_err(local_err);
error_free(local_err);
return ret; return ret;
} }
@ -603,7 +610,8 @@ out:
return ret; return ret;
} }
static int bdrv_qed_create(const char *filename, QEMUOptionParameter *options) static int bdrv_qed_create(const char *filename, QEMUOptionParameter *options,
Error **errp)
{ {
uint64_t image_size = 0; uint64_t image_size = 0;
uint32_t cluster_size = QED_DEFAULT_CLUSTER_SIZE; uint32_t cluster_size = QED_DEFAULT_CLUSTER_SIZE;
@ -1547,7 +1555,7 @@ static void bdrv_qed_invalidate_cache(BlockDriverState *bs)
bdrv_qed_close(bs); bdrv_qed_close(bs);
memset(s, 0, sizeof(BDRVQEDState)); memset(s, 0, sizeof(BDRVQEDState));
bdrv_qed_open(bs, NULL, bs->open_flags); bdrv_qed_open(bs, NULL, bs->open_flags, NULL);
} }
static int bdrv_qed_check(BlockDriverState *bs, BdrvCheckResult *result, static int bdrv_qed_check(BlockDriverState *bs, BdrvCheckResult *result,

View file

@ -335,7 +335,8 @@ fail:
return ret; return ret;
} }
static int raw_open(BlockDriverState *bs, QDict *options, int flags) static int raw_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp)
{ {
BDRVRawState *s = bs->opaque; BDRVRawState *s = bs->opaque;
@ -1040,7 +1041,8 @@ static int64_t raw_get_allocated_file_size(BlockDriverState *bs)
return (int64_t)st.st_blocks * 512; return (int64_t)st.st_blocks * 512;
} }
static int raw_create(const char *filename, QEMUOptionParameter *options) static int raw_create(const char *filename, QEMUOptionParameter *options,
Error **errp)
{ {
int fd; int fd;
int result = 0; int result = 0;
@ -1331,7 +1333,8 @@ static int check_hdev_writable(BDRVRawState *s)
return 0; return 0;
} }
static int hdev_open(BlockDriverState *bs, QDict *options, int flags) static int hdev_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp)
{ {
BDRVRawState *s = bs->opaque; BDRVRawState *s = bs->opaque;
int ret; int ret;
@ -1504,7 +1507,8 @@ static coroutine_fn BlockDriverAIOCB *hdev_aio_discard(BlockDriverState *bs,
cb, opaque, QEMU_AIO_DISCARD|QEMU_AIO_BLKDEV); cb, opaque, QEMU_AIO_DISCARD|QEMU_AIO_BLKDEV);
} }
static int hdev_create(const char *filename, QEMUOptionParameter *options) static int hdev_create(const char *filename, QEMUOptionParameter *options,
Error **errp)
{ {
int fd; int fd;
int ret = 0; int ret = 0;
@ -1565,7 +1569,8 @@ static BlockDriver bdrv_host_device = {
}; };
#ifdef __linux__ #ifdef __linux__
static int floppy_open(BlockDriverState *bs, QDict *options, int flags) static int floppy_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp)
{ {
BDRVRawState *s = bs->opaque; BDRVRawState *s = bs->opaque;
int ret; int ret;
@ -1686,7 +1691,8 @@ static BlockDriver bdrv_host_floppy = {
.bdrv_eject = floppy_eject, .bdrv_eject = floppy_eject,
}; };
static int cdrom_open(BlockDriverState *bs, QDict *options, int flags) static int cdrom_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp)
{ {
BDRVRawState *s = bs->opaque; BDRVRawState *s = bs->opaque;

View file

@ -85,6 +85,7 @@ static size_t handle_aiocb_rw(RawWin32AIOData *aiocb)
ret_count = 0; ret_count = 0;
} }
if (ret_count != len) { if (ret_count != len) {
offset += ret_count;
break; break;
} }
offset += len; offset += len;
@ -234,7 +235,8 @@ static QemuOptsList raw_runtime_opts = {
}, },
}; };
static int raw_open(BlockDriverState *bs, QDict *options, int flags) static int raw_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp)
{ {
BDRVRawState *s = bs->opaque; BDRVRawState *s = bs->opaque;
int access_flags; int access_flags;
@ -420,7 +422,8 @@ static int64_t raw_get_allocated_file_size(BlockDriverState *bs)
return st.st_size; return st.st_size;
} }
static int raw_create(const char *filename, QEMUOptionParameter *options) static int raw_create(const char *filename, QEMUOptionParameter *options,
Error **errp)
{ {
int fd; int fd;
int64_t total_size = 0; int64_t total_size = 0;
@ -531,7 +534,8 @@ static int hdev_probe_device(const char *filename)
return 0; return 0;
} }
static int hdev_open(BlockDriverState *bs, QDict *options, int flags) static int hdev_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp)
{ {
BDRVRawState *s = bs->opaque; BDRVRawState *s = bs->opaque;
int access_flags, create_flags; int access_flags, create_flags;

View file

@ -130,12 +130,22 @@ static int raw_has_zero_init(BlockDriverState *bs)
return bdrv_has_zero_init(bs->file); return bdrv_has_zero_init(bs->file);
} }
static int raw_create(const char *filename, QEMUOptionParameter *options) static int raw_create(const char *filename, QEMUOptionParameter *options,
Error **errp)
{ {
return bdrv_create_file(filename, options); Error *local_err = NULL;
int ret;
ret = bdrv_create_file(filename, options, &local_err);
if (error_is_set(&local_err)) {
qerror_report_err(local_err);
error_free(local_err);
}
return ret;
} }
static int raw_open(BlockDriverState *bs, QDict *options, int flags) static int raw_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp)
{ {
bs->sg = bs->file->sg; bs->sg = bs->file->sg;
return 0; return 0;

View file

@ -287,7 +287,8 @@ static int qemu_rbd_set_conf(rados_t cluster, const char *conf)
return ret; return ret;
} }
static int qemu_rbd_create(const char *filename, QEMUOptionParameter *options) static int qemu_rbd_create(const char *filename, QEMUOptionParameter *options,
Error **errp)
{ {
int64_t bytes = 0; int64_t bytes = 0;
int64_t objsize; int64_t objsize;
@ -446,7 +447,8 @@ static QemuOptsList runtime_opts = {
}, },
}; };
static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags) static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp)
{ {
BDRVRBDState *s = bs->opaque; BDRVRBDState *s = bs->opaque;
char pool[RBD_MAX_POOL_NAME_SIZE]; char pool[RBD_MAX_POOL_NAME_SIZE];
@ -891,12 +893,31 @@ static int qemu_rbd_snap_create(BlockDriverState *bs,
} }
static int qemu_rbd_snap_remove(BlockDriverState *bs, static int qemu_rbd_snap_remove(BlockDriverState *bs,
const char *snapshot_name) const char *snapshot_id,
const char *snapshot_name,
Error **errp)
{ {
BDRVRBDState *s = bs->opaque; BDRVRBDState *s = bs->opaque;
int r; int r;
if (!snapshot_name) {
error_setg(errp, "rbd need a valid snapshot name");
return -EINVAL;
}
/* If snapshot_id is specified, it must be equal to name, see
qemu_rbd_snap_list() */
if (snapshot_id && strcmp(snapshot_id, snapshot_name)) {
error_setg(errp,
"rbd do not support snapshot id, it should be NULL or "
"equal to snapshot name");
return -EINVAL;
}
r = rbd_snap_remove(s->image, snapshot_name); r = rbd_snap_remove(s->image, snapshot_name);
if (r < 0) {
error_setg_errno(errp, -r, "Failed to remove the snapshot");
}
return r; return r;
} }

View file

@ -1242,7 +1242,8 @@ static QemuOptsList runtime_opts = {
}, },
}; };
static int sd_open(BlockDriverState *bs, QDict *options, int flags) static int sd_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp)
{ {
int ret, fd; int ret, fd;
uint32_t vid = 0; uint32_t vid = 0;
@ -1400,10 +1401,13 @@ static int sd_prealloc(const char *filename)
uint32_t idx, max_idx; uint32_t idx, max_idx;
int64_t vdi_size; int64_t vdi_size;
void *buf = g_malloc0(SD_DATA_OBJ_SIZE); void *buf = g_malloc0(SD_DATA_OBJ_SIZE);
Error *local_err = NULL;
int ret; int ret;
ret = bdrv_file_open(&bs, filename, NULL, BDRV_O_RDWR); ret = bdrv_file_open(&bs, filename, NULL, BDRV_O_RDWR, &local_err);
if (ret < 0) { if (ret < 0) {
qerror_report_err(local_err);
error_free(local_err);
goto out; goto out;
} }
@ -1437,7 +1441,8 @@ out:
return ret; return ret;
} }
static int sd_create(const char *filename, QEMUOptionParameter *options) static int sd_create(const char *filename, QEMUOptionParameter *options,
Error **errp)
{ {
int ret = 0; int ret = 0;
uint32_t vid = 0, base_vid = 0; uint32_t vid = 0, base_vid = 0;
@ -1447,6 +1452,7 @@ static int sd_create(const char *filename, QEMUOptionParameter *options)
char vdi[SD_MAX_VDI_LEN], tag[SD_MAX_VDI_TAG_LEN]; char vdi[SD_MAX_VDI_LEN], tag[SD_MAX_VDI_TAG_LEN];
uint32_t snapid; uint32_t snapid;
bool prealloc = false; bool prealloc = false;
Error *local_err = NULL;
s = g_malloc0(sizeof(BDRVSheepdogState)); s = g_malloc0(sizeof(BDRVSheepdogState));
@ -1500,8 +1506,10 @@ static int sd_create(const char *filename, QEMUOptionParameter *options)
goto out; goto out;
} }
ret = bdrv_file_open(&bs, backing_file, NULL, 0); ret = bdrv_file_open(&bs, backing_file, NULL, 0, &local_err);
if (ret < 0) { if (ret < 0) {
qerror_report_err(local_err);
error_free(local_err);
goto out; goto out;
} }
@ -2072,7 +2080,10 @@ out:
return ret; return ret;
} }
static int sd_snapshot_delete(BlockDriverState *bs, const char *snapshot_id) static int sd_snapshot_delete(BlockDriverState *bs,
const char *snapshot_id,
const char *name,
Error **errp)
{ {
/* FIXME: Delete specified snapshot id. */ /* FIXME: Delete specified snapshot id. */
return 0; return 0;

View file

@ -48,6 +48,79 @@ int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info,
return ret; return ret;
} }
/**
* Look up an internal snapshot by @id and @name.
* @bs: block device to search
* @id: unique snapshot ID, or NULL
* @name: snapshot name, or NULL
* @sn_info: location to store information on the snapshot found
* @errp: location to store error, will be set only for exception
*
* This function will traverse snapshot list in @bs to search the matching
* one, @id and @name are the matching condition:
* If both @id and @name are specified, find the first one with id @id and
* name @name.
* If only @id is specified, find the first one with id @id.
* If only @name is specified, find the first one with name @name.
* if none is specified, abort().
*
* Returns: true when a snapshot is found and @sn_info will be filled, false
* when error or not found. If all operation succeed but no matching one is
* found, @errp will NOT be set.
*/
bool bdrv_snapshot_find_by_id_and_name(BlockDriverState *bs,
const char *id,
const char *name,
QEMUSnapshotInfo *sn_info,
Error **errp)
{
QEMUSnapshotInfo *sn_tab, *sn;
int nb_sns, i;
bool ret = false;
assert(id || name);
nb_sns = bdrv_snapshot_list(bs, &sn_tab);
if (nb_sns < 0) {
error_setg_errno(errp, -nb_sns, "Failed to get a snapshot list");
return false;
} else if (nb_sns == 0) {
return false;
}
if (id && name) {
for (i = 0; i < nb_sns; i++) {
sn = &sn_tab[i];
if (!strcmp(sn->id_str, id) && !strcmp(sn->name, name)) {
*sn_info = *sn;
ret = true;
break;
}
}
} else if (id) {
for (i = 0; i < nb_sns; i++) {
sn = &sn_tab[i];
if (!strcmp(sn->id_str, id)) {
*sn_info = *sn;
ret = true;
break;
}
}
} else if (name) {
for (i = 0; i < nb_sns; i++) {
sn = &sn_tab[i];
if (!strcmp(sn->name, name)) {
*sn_info = *sn;
ret = true;
break;
}
}
}
g_free(sn_tab);
return ret;
}
int bdrv_can_snapshot(BlockDriverState *bs) int bdrv_can_snapshot(BlockDriverState *bs)
{ {
BlockDriver *drv = bs->drv; BlockDriver *drv = bs->drv;
@ -97,7 +170,7 @@ int bdrv_snapshot_goto(BlockDriverState *bs,
if (bs->file) { if (bs->file) {
drv->bdrv_close(bs); drv->bdrv_close(bs);
ret = bdrv_snapshot_goto(bs->file, snapshot_id); ret = bdrv_snapshot_goto(bs->file, snapshot_id);
open_ret = drv->bdrv_open(bs, NULL, bs->open_flags); open_ret = drv->bdrv_open(bs, NULL, bs->open_flags, NULL);
if (open_ret < 0) { if (open_ret < 0) {
bdrv_unref(bs->file); bdrv_unref(bs->file);
bs->drv = NULL; bs->drv = NULL;
@ -109,21 +182,73 @@ int bdrv_snapshot_goto(BlockDriverState *bs,
return -ENOTSUP; return -ENOTSUP;
} }
int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id) /**
* Delete an internal snapshot by @snapshot_id and @name.
* @bs: block device used in the operation
* @snapshot_id: unique snapshot ID, or NULL
* @name: snapshot name, or NULL
* @errp: location to store error
*
* If both @snapshot_id and @name are specified, delete the first one with
* id @snapshot_id and name @name.
* If only @snapshot_id is specified, delete the first one with id
* @snapshot_id.
* If only @name is specified, delete the first one with name @name.
* if none is specified, return -ENINVAL.
*
* Returns: 0 on success, -errno on failure. If @bs is not inserted, return
* -ENOMEDIUM. If @snapshot_id and @name are both NULL, return -EINVAL. If @bs
* does not support internal snapshot deletion, return -ENOTSUP. If @bs does
* not support parameter @snapshot_id or @name, or one of them is not correctly
* specified, return -EINVAL. If @bs can't find one matching @id and @name,
* return -ENOENT. If @errp != NULL, it will always be filled with error
* message on failure.
*/
int bdrv_snapshot_delete(BlockDriverState *bs,
const char *snapshot_id,
const char *name,
Error **errp)
{ {
BlockDriver *drv = bs->drv; BlockDriver *drv = bs->drv;
if (!drv) { if (!drv) {
error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, bdrv_get_device_name(bs));
return -ENOMEDIUM; return -ENOMEDIUM;
} }
if (!snapshot_id && !name) {
error_setg(errp, "snapshot_id and name are both NULL");
return -EINVAL;
}
if (drv->bdrv_snapshot_delete) { if (drv->bdrv_snapshot_delete) {
return drv->bdrv_snapshot_delete(bs, snapshot_id); return drv->bdrv_snapshot_delete(bs, snapshot_id, name, errp);
} }
if (bs->file) { if (bs->file) {
return bdrv_snapshot_delete(bs->file, snapshot_id); return bdrv_snapshot_delete(bs->file, snapshot_id, name, errp);
} }
error_set(errp, QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED,
drv->format_name, bdrv_get_device_name(bs),
"internal snapshot deletion");
return -ENOTSUP; return -ENOTSUP;
} }
void bdrv_snapshot_delete_by_id_or_name(BlockDriverState *bs,
const char *id_or_name,
Error **errp)
{
int ret;
Error *local_err = NULL;
ret = bdrv_snapshot_delete(bs, id_or_name, NULL, &local_err);
if (ret == -ENOENT || ret == -EINVAL) {
error_free(local_err);
local_err = NULL;
ret = bdrv_snapshot_delete(bs, NULL, id_or_name, &local_err);
}
if (ret < 0) {
error_propagate(errp, local_err);
}
}
int bdrv_snapshot_list(BlockDriverState *bs, int bdrv_snapshot_list(BlockDriverState *bs,
QEMUSnapshotInfo **psn_info) QEMUSnapshotInfo **psn_info)
{ {

View file

@ -608,7 +608,8 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options,
return ret; return ret;
} }
static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags) static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags,
Error **errp)
{ {
BDRVSSHState *s = bs->opaque; BDRVSSHState *s = bs->opaque;
int ret; int ret;
@ -650,7 +651,8 @@ static QEMUOptionParameter ssh_create_options[] = {
{ NULL } { NULL }
}; };
static int ssh_create(const char *filename, QEMUOptionParameter *options) static int ssh_create(const char *filename, QEMUOptionParameter *options,
Error **errp)
{ {
int r, ret; int r, ret;
Error *local_err = NULL; Error *local_err = NULL;

View file

@ -364,7 +364,8 @@ static int vdi_probe(const uint8_t *buf, int buf_size, const char *filename)
return result; return result;
} }
static int vdi_open(BlockDriverState *bs, QDict *options, int flags) static int vdi_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp)
{ {
BDRVVdiState *s = bs->opaque; BDRVVdiState *s = bs->opaque;
VdiHeader header; VdiHeader header;
@ -644,7 +645,8 @@ static int vdi_co_write(BlockDriverState *bs,
return ret; return ret;
} }
static int vdi_create(const char *filename, QEMUOptionParameter *options) static int vdi_create(const char *filename, QEMUOptionParameter *options,
Error **errp)
{ {
int fd; int fd;
int result = 0; int result = 0;

View file

@ -715,7 +715,8 @@ exit:
} }
static int vhdx_open(BlockDriverState *bs, QDict *options, int flags) static int vhdx_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp)
{ {
BDRVVHDXState *s = bs->opaque; BDRVVHDXState *s = bs->opaque;
int ret = 0; int ret = 0;

View file

@ -697,6 +697,7 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
int64_t flat_offset; int64_t flat_offset;
char extent_path[PATH_MAX]; char extent_path[PATH_MAX];
BlockDriverState *extent_file; BlockDriverState *extent_file;
Error *local_err = NULL;
while (*p) { while (*p) {
/* parse extent line: /* parse extent line:
@ -726,8 +727,11 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
path_combine(extent_path, sizeof(extent_path), path_combine(extent_path, sizeof(extent_path),
desc_file_path, fname); desc_file_path, fname);
ret = bdrv_file_open(&extent_file, extent_path, NULL, bs->open_flags); ret = bdrv_file_open(&extent_file, extent_path, NULL, bs->open_flags,
&local_err);
if (ret) { if (ret) {
qerror_report_err(local_err);
error_free(local_err);
return ret; return ret;
} }
@ -806,7 +810,8 @@ exit:
return ret; return ret;
} }
static int vmdk_open(BlockDriverState *bs, QDict *options, int flags) static int vmdk_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp)
{ {
int ret; int ret;
BDRVVmdkState *s = bs->opaque; BDRVVmdkState *s = bs->opaque;
@ -1551,7 +1556,8 @@ static int filename_decompose(const char *filename, char *path, char *prefix,
return VMDK_OK; return VMDK_OK;
} }
static int vmdk_create(const char *filename, QEMUOptionParameter *options) static int vmdk_create(const char *filename, QEMUOptionParameter *options,
Error **errp)
{ {
int fd, idx = 0; int fd, idx = 0;
char desc[BUF_SIZE]; char desc[BUF_SIZE];
@ -1589,6 +1595,7 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options)
"ddb.geometry.heads = \"%d\"\n" "ddb.geometry.heads = \"%d\"\n"
"ddb.geometry.sectors = \"63\"\n" "ddb.geometry.sectors = \"63\"\n"
"ddb.adapterType = \"%s\"\n"; "ddb.adapterType = \"%s\"\n";
Error *local_err = NULL;
if (filename_decompose(filename, path, prefix, postfix, PATH_MAX)) { if (filename_decompose(filename, path, prefix, postfix, PATH_MAX)) {
return -EINVAL; return -EINVAL;
@ -1651,8 +1658,10 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options)
} }
if (backing_file) { if (backing_file) {
BlockDriverState *bs = bdrv_new(""); BlockDriverState *bs = bdrv_new("");
ret = bdrv_open(bs, backing_file, NULL, 0, NULL); ret = bdrv_open(bs, backing_file, NULL, 0, NULL, &local_err);
if (ret != 0) { if (ret != 0) {
qerror_report_err(local_err);
error_free(local_err);
bdrv_unref(bs); bdrv_unref(bs);
return ret; return ret;
} }

View file

@ -155,7 +155,8 @@ static int vpc_probe(const uint8_t *buf, int buf_size, const char *filename)
return 0; return 0;
} }
static int vpc_open(BlockDriverState *bs, QDict *options, int flags) static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp)
{ {
BDRVVPCState *s = bs->opaque; BDRVVPCState *s = bs->opaque;
int i; int i;
@ -683,7 +684,8 @@ static int create_fixed_disk(int fd, uint8_t *buf, int64_t total_size)
return ret; return ret;
} }
static int vpc_create(const char *filename, QEMUOptionParameter *options) static int vpc_create(const char *filename, QEMUOptionParameter *options,
Error **errp)
{ {
uint8_t buf[1024]; uint8_t buf[1024];
struct vhd_footer *footer = (struct vhd_footer *) buf; struct vhd_footer *footer = (struct vhd_footer *) buf;

View file

@ -1065,7 +1065,8 @@ static void vvfat_parse_filename(const char *filename, QDict *options,
qdict_put(options, "rw", qbool_from_int(rw)); qdict_put(options, "rw", qbool_from_int(rw));
} }
static int vvfat_open(BlockDriverState *bs, QDict *options, int flags) static int vvfat_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp)
{ {
BDRVVVFATState *s = bs->opaque; BDRVVVFATState *s = bs->opaque;
int cyls, heads, secs; int cyls, heads, secs;
@ -2909,6 +2910,7 @@ static int enable_write_target(BDRVVVFATState *s)
{ {
BlockDriver *bdrv_qcow; BlockDriver *bdrv_qcow;
QEMUOptionParameter *options; QEMUOptionParameter *options;
Error *local_err = NULL;
int ret; int ret;
int size = sector2cluster(s, s->sector_count); int size = sector2cluster(s, s->sector_count);
s->used_clusters = calloc(size, 1); s->used_clusters = calloc(size, 1);
@ -2926,16 +2928,21 @@ static int enable_write_target(BDRVVVFATState *s)
set_option_parameter_int(options, BLOCK_OPT_SIZE, s->sector_count * 512); set_option_parameter_int(options, BLOCK_OPT_SIZE, s->sector_count * 512);
set_option_parameter(options, BLOCK_OPT_BACKING_FILE, "fat:"); set_option_parameter(options, BLOCK_OPT_BACKING_FILE, "fat:");
ret = bdrv_create(bdrv_qcow, s->qcow_filename, options); ret = bdrv_create(bdrv_qcow, s->qcow_filename, options, &local_err);
if (ret < 0) { if (ret < 0) {
qerror_report_err(local_err);
error_free(local_err);
goto err; goto err;
} }
s->qcow = bdrv_new(""); s->qcow = bdrv_new("");
ret = bdrv_open(s->qcow, s->qcow_filename, NULL, ret = bdrv_open(s->qcow, s->qcow_filename, NULL,
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, bdrv_qcow); BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, bdrv_qcow,
&local_err);
if (ret < 0) { if (ret < 0) {
qerror_report_err(local_err);
error_free(local_err);
bdrv_unref(s->qcow); bdrv_unref(s->qcow);
goto err; goto err;
} }

View file

@ -710,17 +710,11 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts,
} }
QINCREF(bs_opts); QINCREF(bs_opts);
ret = bdrv_open(dinfo->bdrv, file, bs_opts, bdrv_flags, drv); ret = bdrv_open(dinfo->bdrv, file, bs_opts, bdrv_flags, drv, &error);
if (ret < 0) { if (ret < 0) {
if (ret == -EMEDIUMTYPE) { error_report("could not open disk image %s: %s",
error_report("could not open disk image %s: not in %s format", file ?: dinfo->id, error_get_pretty(error));
file ?: dinfo->id, drv ? drv->format_name :
qdict_get_str(bs_opts, "driver"));
} else {
error_report("could not open disk image %s: %s",
file ?: dinfo->id, strerror(-ret));
}
goto err; goto err;
} }
@ -858,6 +852,80 @@ void qmp_blockdev_snapshot_sync(const char *device, const char *snapshot_file,
&snapshot, errp); &snapshot, errp);
} }
void qmp_blockdev_snapshot_internal_sync(const char *device,
const char *name,
Error **errp)
{
BlockdevSnapshotInternal snapshot = {
.device = (char *) device,
.name = (char *) name
};
blockdev_do_action(TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC,
&snapshot, errp);
}
SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device,
bool has_id,
const char *id,
bool has_name,
const char *name,
Error **errp)
{
BlockDriverState *bs = bdrv_find(device);
QEMUSnapshotInfo sn;
Error *local_err = NULL;
SnapshotInfo *info = NULL;
int ret;
if (!bs) {
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
return NULL;
}
if (!has_id) {
id = NULL;
}
if (!has_name) {
name = NULL;
}
if (!id && !name) {
error_setg(errp, "Name or id must be provided");
return NULL;
}
ret = bdrv_snapshot_find_by_id_and_name(bs, id, name, &sn, &local_err);
if (error_is_set(&local_err)) {
error_propagate(errp, local_err);
return NULL;
}
if (!ret) {
error_setg(errp,
"Snapshot with id '%s' and name '%s' does not exist on "
"device '%s'",
STR_OR_NULL(id), STR_OR_NULL(name), device);
return NULL;
}
bdrv_snapshot_delete(bs, id, name, &local_err);
if (error_is_set(&local_err)) {
error_propagate(errp, local_err);
return NULL;
}
info = g_malloc0(sizeof(SnapshotInfo));
info->id = g_strdup(sn.id_str);
info->name = g_strdup(sn.name);
info->date_nsec = sn.date_nsec;
info->date_sec = sn.date_sec;
info->vm_state_size = sn.vm_state_size;
info->vm_clock_nsec = sn.vm_clock_nsec % 1000000000;
info->vm_clock_sec = sn.vm_clock_nsec / 1000000000;
return info;
}
/* New and old BlockDriverState structs for group snapshots */ /* New and old BlockDriverState structs for group snapshots */
@ -889,6 +957,117 @@ struct BlkTransactionState {
QSIMPLEQ_ENTRY(BlkTransactionState) entry; QSIMPLEQ_ENTRY(BlkTransactionState) entry;
}; };
/* internal snapshot private data */
typedef struct InternalSnapshotState {
BlkTransactionState common;
BlockDriverState *bs;
QEMUSnapshotInfo sn;
} InternalSnapshotState;
static void internal_snapshot_prepare(BlkTransactionState *common,
Error **errp)
{
const char *device;
const char *name;
BlockDriverState *bs;
QEMUSnapshotInfo old_sn, *sn;
bool ret;
qemu_timeval tv;
BlockdevSnapshotInternal *internal;
InternalSnapshotState *state;
int ret1;
g_assert(common->action->kind ==
TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC);
internal = common->action->blockdev_snapshot_internal_sync;
state = DO_UPCAST(InternalSnapshotState, common, common);
/* 1. parse input */
device = internal->device;
name = internal->name;
/* 2. check for validation */
bs = bdrv_find(device);
if (!bs) {
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
return;
}
if (!bdrv_is_inserted(bs)) {
error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
return;
}
if (bdrv_is_read_only(bs)) {
error_set(errp, QERR_DEVICE_IS_READ_ONLY, device);
return;
}
if (!bdrv_can_snapshot(bs)) {
error_set(errp, QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED,
bs->drv->format_name, device, "internal snapshot");
return;
}
if (!strlen(name)) {
error_setg(errp, "Name is empty");
return;
}
/* check whether a snapshot with name exist */
ret = bdrv_snapshot_find_by_id_and_name(bs, NULL, name, &old_sn, errp);
if (error_is_set(errp)) {
return;
} else if (ret) {
error_setg(errp,
"Snapshot with name '%s' already exists on device '%s'",
name, device);
return;
}
/* 3. take the snapshot */
sn = &state->sn;
pstrcpy(sn->name, sizeof(sn->name), name);
qemu_gettimeofday(&tv);
sn->date_sec = tv.tv_sec;
sn->date_nsec = tv.tv_usec * 1000;
sn->vm_clock_nsec = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
ret1 = bdrv_snapshot_create(bs, sn);
if (ret1 < 0) {
error_setg_errno(errp, -ret1,
"Failed to create snapshot '%s' on device '%s'",
name, device);
return;
}
/* 4. succeed, mark a snapshot is created */
state->bs = bs;
}
static void internal_snapshot_abort(BlkTransactionState *common)
{
InternalSnapshotState *state =
DO_UPCAST(InternalSnapshotState, common, common);
BlockDriverState *bs = state->bs;
QEMUSnapshotInfo *sn = &state->sn;
Error *local_error = NULL;
if (!bs) {
return;
}
if (bdrv_snapshot_delete(bs, sn->id_str, sn->name, &local_error) < 0) {
error_report("Failed to delete snapshot with id '%s' and name '%s' on "
"device '%s' in abort: %s",
sn->id_str,
sn->name,
bdrv_get_device_name(bs),
error_get_pretty(local_error));
error_free(local_error);
}
}
/* external snapshot private data */ /* external snapshot private data */
typedef struct ExternalSnapshotState { typedef struct ExternalSnapshotState {
BlkTransactionState common; BlkTransactionState common;
@ -971,9 +1150,9 @@ static void external_snapshot_prepare(BlkTransactionState *common,
/* 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? */
ret = bdrv_open(state->new_bs, new_image_file, NULL, ret = bdrv_open(state->new_bs, new_image_file, NULL,
flags | BDRV_O_NO_BACKING, drv); flags | BDRV_O_NO_BACKING, drv, &local_err);
if (ret != 0) { if (ret != 0) {
error_setg_file_open(errp, -ret, new_image_file); error_propagate(errp, local_err);
} }
} }
@ -1072,6 +1251,11 @@ static const BdrvActionOps actions[] = {
.prepare = abort_prepare, .prepare = abort_prepare,
.commit = abort_commit, .commit = abort_commit,
}, },
[TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC] = {
.instance_size = sizeof(InternalSnapshotState),
.prepare = internal_snapshot_prepare,
.abort = internal_snapshot_abort,
},
}; };
/* /*
@ -1102,6 +1286,8 @@ void qmp_transaction(TransactionActionList *dev_list, Error **errp)
assert(dev_info->kind < ARRAY_SIZE(actions)); assert(dev_info->kind < ARRAY_SIZE(actions));
ops = &actions[dev_info->kind]; ops = &actions[dev_info->kind];
assert(ops->instance_size > 0);
state = g_malloc0(ops->instance_size); state = g_malloc0(ops->instance_size);
state->ops = ops; state->ops = ops;
state->action = dev_info; state->action = dev_info;
@ -1203,11 +1389,12 @@ static void qmp_bdrv_open_encrypted(BlockDriverState *bs, const char *filename,
int bdrv_flags, BlockDriver *drv, int bdrv_flags, BlockDriver *drv,
const char *password, Error **errp) const char *password, Error **errp)
{ {
Error *local_err = NULL;
int ret; int ret;
ret = bdrv_open(bs, filename, NULL, bdrv_flags, drv); ret = bdrv_open(bs, filename, NULL, bdrv_flags, drv, &local_err);
if (ret < 0) { if (ret < 0) {
error_setg_file_open(errp, -ret, filename); error_propagate(errp, local_err);
return; return;
} }
@ -1627,10 +1814,10 @@ void qmp_drive_backup(const char *device, const char *target,
} }
target_bs = bdrv_new(""); target_bs = bdrv_new("");
ret = bdrv_open(target_bs, target, NULL, flags, drv); ret = bdrv_open(target_bs, target, NULL, flags, drv, &local_err);
if (ret < 0) { if (ret < 0) {
bdrv_unref(target_bs); bdrv_unref(target_bs);
error_setg_file_open(errp, -ret, target); error_propagate(errp, local_err);
return; return;
} }
@ -1762,10 +1949,11 @@ void qmp_drive_mirror(const char *device, const char *target,
* file. * file.
*/ */
target_bs = bdrv_new(""); target_bs = bdrv_new("");
ret = bdrv_open(target_bs, target, NULL, flags | BDRV_O_NO_BACKING, drv); ret = bdrv_open(target_bs, target, NULL, flags | BDRV_O_NO_BACKING, drv,
&local_err);
if (ret < 0) { if (ret < 0) {
bdrv_unref(target_bs); bdrv_unref(target_bs);
error_setg_file_open(errp, -ret, target); error_propagate(errp, local_err);
return; return;
} }

26
configure vendored
View file

@ -238,6 +238,7 @@ win_sdk="no"
want_tools="yes" want_tools="yes"
libiscsi="" libiscsi=""
coroutine="" coroutine=""
coroutine_pool=""
seccomp="" seccomp=""
glusterfs="" glusterfs=""
glusterfs_discard="no" glusterfs_discard="no"
@ -888,6 +889,10 @@ for opt do
;; ;;
--with-coroutine=*) coroutine="$optarg" --with-coroutine=*) coroutine="$optarg"
;; ;;
--disable-coroutine-pool) coroutine_pool="no"
;;
--enable-coroutine-pool) coroutine_pool="yes"
;;
--disable-docs) docs="no" --disable-docs) docs="no"
;; ;;
--enable-docs) docs="yes" --enable-docs) docs="yes"
@ -1189,6 +1194,8 @@ echo " --disable-seccomp disable seccomp support"
echo " --enable-seccomp enables seccomp support" echo " --enable-seccomp enables seccomp support"
echo " --with-coroutine=BACKEND coroutine backend. Supported options:" echo " --with-coroutine=BACKEND coroutine backend. Supported options:"
echo " gthread, ucontext, sigaltstack, windows" echo " gthread, ucontext, sigaltstack, windows"
echo " --disable-coroutine-pool disable coroutine freelist (worse performance)"
echo " --enable-coroutine-pool enable coroutine freelist (better performance)"
echo " --enable-glusterfs enable GlusterFS backend" echo " --enable-glusterfs enable GlusterFS backend"
echo " --disable-glusterfs disable GlusterFS backend" echo " --disable-glusterfs disable GlusterFS backend"
echo " --enable-gcov enable test coverage analysis with gcov" echo " --enable-gcov enable test coverage analysis with gcov"
@ -3362,6 +3369,17 @@ else
esac esac
fi fi
if test "$coroutine_pool" = ""; then
if test "$coroutine" = "gthread"; then
coroutine_pool=no
else
coroutine_pool=yes
fi
fi
if test "$coroutine" = "gthread" -a "$coroutine_pool" = "yes"; then
error_exit "'gthread' coroutine backend does not support pool (use --disable-coroutine-pool)"
fi
########################################## ##########################################
# check if we have open_by_handle_at # check if we have open_by_handle_at
@ -3733,6 +3751,7 @@ echo "build guest agent $guest_agent"
echo "QGA VSS support $guest_agent_with_vss" echo "QGA VSS support $guest_agent_with_vss"
echo "seccomp support $seccomp" echo "seccomp support $seccomp"
echo "coroutine backend $coroutine" echo "coroutine backend $coroutine"
echo "coroutine pool $coroutine_pool"
echo "GlusterFS support $glusterfs" echo "GlusterFS support $glusterfs"
echo "virtio-blk-data-plane $virtio_blk_data_plane" echo "virtio-blk-data-plane $virtio_blk_data_plane"
echo "gcov $gcov_tool" echo "gcov $gcov_tool"
@ -4092,6 +4111,11 @@ if test "$rbd" = "yes" ; then
fi fi
echo "CONFIG_COROUTINE_BACKEND=$coroutine" >> $config_host_mak echo "CONFIG_COROUTINE_BACKEND=$coroutine" >> $config_host_mak
if test "$coroutine_pool" = "yes" ; then
echo "CONFIG_COROUTINE_POOL=1" >> $config_host_mak
else
echo "CONFIG_COROUTINE_POOL=0" >> $config_host_mak
fi
if test "$open_by_handle_at" = "yes" ; then if test "$open_by_handle_at" = "yes" ; then
echo "CONFIG_OPEN_BY_HANDLE=y" >> $config_host_mak echo "CONFIG_OPEN_BY_HANDLE=y" >> $config_host_mak
@ -4651,7 +4675,7 @@ if [ "$dtc_internal" = "yes" ]; then
fi fi
# build tree in object directory in case the source is not in the current directory # build tree in object directory in case the source is not in the current directory
DIRS="tests tests/tcg tests/tcg/cris tests/tcg/lm32 tests/libqos tests/qapi-schema tests/tcg/xtensa" DIRS="tests tests/tcg tests/tcg/cris tests/tcg/lm32 tests/libqos tests/qapi-schema tests/tcg/xtensa tests/qemu-iotests"
DIRS="$DIRS pc-bios/optionrom pc-bios/spapr-rtas pc-bios/s390-ccw" DIRS="$DIRS pc-bios/optionrom pc-bios/spapr-rtas pc-bios/s390-ccw"
DIRS="$DIRS roms/seabios roms/vgabios" DIRS="$DIRS roms/seabios roms/vgabios"
DIRS="$DIRS qapi-generated" DIRS="$DIRS qapi-generated"

View file

@ -1023,8 +1023,7 @@ ETEXI
"of device. If a new image file is specified, the\n\t\t\t" "of device. If a new image file is specified, the\n\t\t\t"
"new image file will become the new root image.\n\t\t\t" "new image file will become the new root image.\n\t\t\t"
"If format is specified, the snapshot file will\n\t\t\t" "If format is specified, the snapshot file will\n\t\t\t"
"be created in that format. Otherwise the\n\t\t\t" "be created in that format.\n\t\t\t"
"snapshot will be internal! (currently unsupported).\n\t\t\t"
"The default format is qcow2. The -n flag requests QEMU\n\t\t\t" "The default format is qcow2. The -n flag requests QEMU\n\t\t\t"
"to reuse the image found in new-image-file, instead of\n\t\t\t" "to reuse the image found in new-image-file, instead of\n\t\t\t"
"recreating it from scratch.", "recreating it from scratch.",
@ -1035,6 +1034,40 @@ STEXI
@item snapshot_blkdev @item snapshot_blkdev
@findex snapshot_blkdev @findex snapshot_blkdev
Snapshot device, using snapshot file as target if provided Snapshot device, using snapshot file as target if provided
ETEXI
{
.name = "snapshot_blkdev_internal",
.args_type = "device:B,name:s",
.params = "device name",
.help = "take an internal snapshot of device.\n\t\t\t"
"The format of the image used by device must\n\t\t\t"
"support it, such as qcow2.\n\t\t\t",
.mhandler.cmd = hmp_snapshot_blkdev_internal,
},
STEXI
@item snapshot_blkdev_internal
@findex snapshot_blkdev_internal
Take an internal snapshot on device if it support
ETEXI
{
.name = "snapshot_delete_blkdev_internal",
.args_type = "device:B,name:s,id:s?",
.params = "device name [id]",
.help = "delete an internal snapshot of device.\n\t\t\t"
"If id is specified, qemu will try delete\n\t\t\t"
"the snapshot matching both id and name.\n\t\t\t"
"The format of the image used by device must\n\t\t\t"
"support it, such as qcow2.\n\t\t\t",
.mhandler.cmd = hmp_snapshot_delete_blkdev_internal,
},
STEXI
@item snapshot_delete_blkdev_internal
@findex snapshot_delete_blkdev_internal
Delete an internal snapshot on device if it support
ETEXI ETEXI
{ {

22
hmp.c
View file

@ -978,6 +978,28 @@ void hmp_snapshot_blkdev(Monitor *mon, const QDict *qdict)
hmp_handle_error(mon, &errp); hmp_handle_error(mon, &errp);
} }
void hmp_snapshot_blkdev_internal(Monitor *mon, const QDict *qdict)
{
const char *device = qdict_get_str(qdict, "device");
const char *name = qdict_get_str(qdict, "name");
Error *errp = NULL;
qmp_blockdev_snapshot_internal_sync(device, name, &errp);
hmp_handle_error(mon, &errp);
}
void hmp_snapshot_delete_blkdev_internal(Monitor *mon, const QDict *qdict)
{
const char *device = qdict_get_str(qdict, "device");
const char *name = qdict_get_str(qdict, "name");
const char *id = qdict_get_try_str(qdict, "id");
Error *errp = NULL;
qmp_blockdev_snapshot_delete_internal_sync(device, !!id, id,
true, name, &errp);
hmp_handle_error(mon, &errp);
}
void hmp_migrate_cancel(Monitor *mon, const QDict *qdict) void hmp_migrate_cancel(Monitor *mon, const QDict *qdict)
{ {
qmp_migrate_cancel(NULL); qmp_migrate_cancel(NULL);

2
hmp.h
View file

@ -54,6 +54,8 @@ void hmp_block_passwd(Monitor *mon, const QDict *qdict);
void hmp_balloon(Monitor *mon, const QDict *qdict); void hmp_balloon(Monitor *mon, const QDict *qdict);
void hmp_block_resize(Monitor *mon, const QDict *qdict); void hmp_block_resize(Monitor *mon, const QDict *qdict);
void hmp_snapshot_blkdev(Monitor *mon, const QDict *qdict); void hmp_snapshot_blkdev(Monitor *mon, const QDict *qdict);
void hmp_snapshot_blkdev_internal(Monitor *mon, const QDict *qdict);
void hmp_snapshot_delete_blkdev_internal(Monitor *mon, const QDict *qdict);
void hmp_drive_mirror(Monitor *mon, const QDict *qdict); void hmp_drive_mirror(Monitor *mon, const QDict *qdict);
void hmp_drive_backup(Monitor *mon, const QDict *qdict); void hmp_drive_backup(Monitor *mon, const QDict *qdict);
void hmp_migrate_cancel(Monitor *mon, const QDict *qdict); void hmp_migrate_cancel(Monitor *mon, const QDict *qdict);

View file

@ -809,10 +809,15 @@ static int blk_connect(struct XenDevice *xendev)
xen_be_printf(&blkdev->xendev, 2, "create new bdrv (xenbus setup)\n"); xen_be_printf(&blkdev->xendev, 2, "create new bdrv (xenbus setup)\n");
blkdev->bs = bdrv_new(blkdev->dev); blkdev->bs = bdrv_new(blkdev->dev);
if (blkdev->bs) { if (blkdev->bs) {
Error *local_err = NULL;
BlockDriver *drv = bdrv_find_whitelisted_format(blkdev->fileproto, BlockDriver *drv = bdrv_find_whitelisted_format(blkdev->fileproto,
readonly); readonly);
if (bdrv_open(blkdev->bs, if (bdrv_open(blkdev->bs,
blkdev->filename, NULL, qflags, drv) != 0) { blkdev->filename, NULL, qflags, drv, &local_err) != 0)
{
xen_be_printf(&blkdev->xendev, 0, "error: %s\n",
error_get_pretty(local_err));
error_free(local_err);
bdrv_unref(blkdev->bs); bdrv_unref(blkdev->bs);
blkdev->bs = NULL; blkdev->bs = NULL;
} }

View file

@ -142,8 +142,9 @@ BlockDriver *bdrv_find_format(const char *format_name);
BlockDriver *bdrv_find_whitelisted_format(const char *format_name, BlockDriver *bdrv_find_whitelisted_format(const char *format_name,
bool readonly); bool readonly);
int bdrv_create(BlockDriver *drv, const char* filename, int bdrv_create(BlockDriver *drv, const char* filename,
QEMUOptionParameter *options); QEMUOptionParameter *options, Error **errp);
int bdrv_create_file(const char* filename, QEMUOptionParameter *options); int bdrv_create_file(const char* filename, QEMUOptionParameter *options,
Error **errp);
BlockDriverState *bdrv_new(const char *device_name); BlockDriverState *bdrv_new(const char *device_name);
void bdrv_make_anon(BlockDriverState *bs); void bdrv_make_anon(BlockDriverState *bs);
void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old); void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old);
@ -151,10 +152,10 @@ void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top);
int bdrv_parse_cache_flags(const char *mode, int *flags); int bdrv_parse_cache_flags(const char *mode, int *flags);
int bdrv_parse_discard_flags(const char *mode, int *flags); int bdrv_parse_discard_flags(const char *mode, int *flags);
int bdrv_file_open(BlockDriverState **pbs, const char *filename, int bdrv_file_open(BlockDriverState **pbs, const char *filename,
QDict *options, int flags); QDict *options, int flags, Error **errp);
int bdrv_open_backing_file(BlockDriverState *bs, QDict *options); int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp);
int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
int flags, BlockDriver *drv); int flags, BlockDriver *drv, Error **errp);
BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
BlockDriverState *bs, int flags); BlockDriverState *bs, int flags);
int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp); int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp);
@ -241,6 +242,8 @@ typedef enum {
int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix); int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix);
int bdrv_amend_options(BlockDriverState *bs_new, QEMUOptionParameter *options);
/* async block I/O */ /* async block I/O */
typedef void BlockDriverDirtyHandler(BlockDriverState *bs, int64_t sector, typedef void BlockDriverDirtyHandler(BlockDriverState *bs, int64_t sector,
int sector_num); int sector_num);

View file

@ -80,15 +80,18 @@ struct BlockDriver {
void (*bdrv_reopen_commit)(BDRVReopenState *reopen_state); void (*bdrv_reopen_commit)(BDRVReopenState *reopen_state);
void (*bdrv_reopen_abort)(BDRVReopenState *reopen_state); void (*bdrv_reopen_abort)(BDRVReopenState *reopen_state);
int (*bdrv_open)(BlockDriverState *bs, QDict *options, int flags); int (*bdrv_open)(BlockDriverState *bs, QDict *options, int flags,
int (*bdrv_file_open)(BlockDriverState *bs, QDict *options, int flags); Error **errp);
int (*bdrv_file_open)(BlockDriverState *bs, QDict *options, int flags,
Error **errp);
int (*bdrv_read)(BlockDriverState *bs, int64_t sector_num, int (*bdrv_read)(BlockDriverState *bs, int64_t sector_num,
uint8_t *buf, int nb_sectors); uint8_t *buf, int nb_sectors);
int (*bdrv_write)(BlockDriverState *bs, int64_t sector_num, int (*bdrv_write)(BlockDriverState *bs, int64_t sector_num,
const uint8_t *buf, int nb_sectors); const uint8_t *buf, int nb_sectors);
void (*bdrv_close)(BlockDriverState *bs); void (*bdrv_close)(BlockDriverState *bs);
void (*bdrv_rebind)(BlockDriverState *bs); void (*bdrv_rebind)(BlockDriverState *bs);
int (*bdrv_create)(const char *filename, QEMUOptionParameter *options); int (*bdrv_create)(const char *filename, QEMUOptionParameter *options,
Error **errp);
int (*bdrv_set_key)(BlockDriverState *bs, const char *key); int (*bdrv_set_key)(BlockDriverState *bs, const char *key);
int (*bdrv_make_empty)(BlockDriverState *bs); int (*bdrv_make_empty)(BlockDriverState *bs);
/* aio */ /* aio */
@ -150,7 +153,10 @@ struct BlockDriver {
QEMUSnapshotInfo *sn_info); QEMUSnapshotInfo *sn_info);
int (*bdrv_snapshot_goto)(BlockDriverState *bs, int (*bdrv_snapshot_goto)(BlockDriverState *bs,
const char *snapshot_id); const char *snapshot_id);
int (*bdrv_snapshot_delete)(BlockDriverState *bs, const char *snapshot_id); int (*bdrv_snapshot_delete)(BlockDriverState *bs,
const char *snapshot_id,
const char *name,
Error **errp);
int (*bdrv_snapshot_list)(BlockDriverState *bs, int (*bdrv_snapshot_list)(BlockDriverState *bs,
QEMUSnapshotInfo **psn_info); QEMUSnapshotInfo **psn_info);
int (*bdrv_snapshot_load_tmp)(BlockDriverState *bs, int (*bdrv_snapshot_load_tmp)(BlockDriverState *bs,
@ -188,6 +194,9 @@ struct BlockDriver {
int (*bdrv_check)(BlockDriverState* bs, BdrvCheckResult *result, int (*bdrv_check)(BlockDriverState* bs, BdrvCheckResult *result,
BdrvCheckMode fix); BdrvCheckMode fix);
int (*bdrv_amend_options)(BlockDriverState *bs,
QEMUOptionParameter *options);
void (*bdrv_debug_event)(BlockDriverState *bs, BlkDebugEvent event); void (*bdrv_debug_event)(BlockDriverState *bs, BlkDebugEvent event);
/* TODO Better pass a option string/QDict/QemuOpts to add any rule? */ /* TODO Better pass a option string/QDict/QemuOpts to add any rule? */

View file

@ -26,6 +26,7 @@
#define SNAPSHOT_H #define SNAPSHOT_H
#include "qemu-common.h" #include "qemu-common.h"
#include "qapi/error.h"
typedef struct QEMUSnapshotInfo { typedef struct QEMUSnapshotInfo {
char id_str[128]; /* unique snapshot id */ char id_str[128]; /* unique snapshot id */
@ -40,12 +41,23 @@ typedef struct QEMUSnapshotInfo {
int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info, int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info,
const char *name); const char *name);
bool bdrv_snapshot_find_by_id_and_name(BlockDriverState *bs,
const char *id,
const char *name,
QEMUSnapshotInfo *sn_info,
Error **errp);
int bdrv_can_snapshot(BlockDriverState *bs); int bdrv_can_snapshot(BlockDriverState *bs);
int bdrv_snapshot_create(BlockDriverState *bs, int bdrv_snapshot_create(BlockDriverState *bs,
QEMUSnapshotInfo *sn_info); QEMUSnapshotInfo *sn_info);
int bdrv_snapshot_goto(BlockDriverState *bs, int bdrv_snapshot_goto(BlockDriverState *bs,
const char *snapshot_id); const char *snapshot_id);
int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id); int bdrv_snapshot_delete(BlockDriverState *bs,
const char *snapshot_id,
const char *name,
Error **errp);
void bdrv_snapshot_delete_by_id_or_name(BlockDriverState *bs,
const char *id_or_name,
Error **errp);
int bdrv_snapshot_list(BlockDriverState *bs, int bdrv_snapshot_list(BlockDriverState *bs,
QEMUSnapshotInfo **psn_info); QEMUSnapshotInfo **psn_info);
int bdrv_snapshot_load_tmp(BlockDriverState *bs, int bdrv_snapshot_load_tmp(BlockDriverState *bs,

View file

@ -191,6 +191,9 @@ int64_t strtosz_suffix(const char *nptr, char **end, const char default_suffix);
int64_t strtosz_suffix_unit(const char *nptr, char **end, int64_t strtosz_suffix_unit(const char *nptr, char **end,
const char default_suffix, int64_t unit); const char default_suffix, int64_t unit);
/* used to print char* safely */
#define STR_OR_NULL(str) ((str) ? (str) : "null")
/* path.c */ /* path.c */
void init_paths(const char *prefix); void init_paths(const char *prefix);
const char *path(const char *pathname); const char *path(const char *pathname);

View file

@ -1685,6 +1685,22 @@
'data': { 'device': 'str', 'snapshot-file': 'str', '*format': 'str', 'data': { 'device': 'str', 'snapshot-file': 'str', '*format': 'str',
'*mode': 'NewImageMode' } } '*mode': 'NewImageMode' } }
##
# @BlockdevSnapshotInternal
#
# @device: the name of the device to generate the snapshot from
#
# @name: the name of the internal snapshot to be created
#
# Notes: In transaction, if @name is empty, or any snapshot matching @name
# exists, the operation will fail. Only some image formats support it,
# for example, qcow2, rbd, and sheepdog.
#
# Since: 1.7
##
{ 'type': 'BlockdevSnapshotInternal',
'data': { 'device': 'str', 'name': 'str' } }
## ##
# @DriveBackup # @DriveBackup
# #
@ -1747,7 +1763,8 @@
'data': { 'data': {
'blockdev-snapshot-sync': 'BlockdevSnapshot', 'blockdev-snapshot-sync': 'BlockdevSnapshot',
'drive-backup': 'DriveBackup', 'drive-backup': 'DriveBackup',
'abort': 'Abort' 'abort': 'Abort',
'blockdev-snapshot-internal-sync': 'BlockdevSnapshotInternal'
} } } }
## ##
@ -1787,6 +1804,53 @@
{ 'command': 'blockdev-snapshot-sync', { 'command': 'blockdev-snapshot-sync',
'data': 'BlockdevSnapshot' } 'data': 'BlockdevSnapshot' }
##
# @blockdev-snapshot-internal-sync
#
# Synchronously take an internal snapshot of a block device, when the format
# of the image used supports it.
#
# For the arguments, see the documentation of BlockdevSnapshotInternal.
#
# Returns: nothing on success
# If @device is not a valid block device, DeviceNotFound
# If any snapshot matching @name exists, or @name is empty,
# GenericError
# If the format of the image used does not support it,
# BlockFormatFeatureNotSupported
#
# Since 1.7
##
{ 'command': 'blockdev-snapshot-internal-sync',
'data': 'BlockdevSnapshotInternal' }
##
# @blockdev-snapshot-delete-internal-sync
#
# Synchronously delete an internal snapshot of a block device, when the format
# of the image used support it. The snapshot is identified by name or id or
# both. One of the name or id is required. Return SnapshotInfo for the
# successfully deleted snapshot.
#
# @device: the name of the device to delete the snapshot from
#
# @id: optional the snapshot's ID to be deleted
#
# @name: optional the snapshot's name to be deleted
#
# Returns: SnapshotInfo on success
# If @device is not a valid block device, DeviceNotFound
# If snapshot not found, GenericError
# If the format of the image used does not support it,
# BlockFormatFeatureNotSupported
# If @id and @name are both not specified, GenericError
#
# Since 1.7
##
{ 'command': 'blockdev-snapshot-delete-internal-sync',
'data': { 'device': 'str', '*id': 'str', '*name': 'str'},
'returns': 'SnapshotInfo' }
## ##
# @human-monitor-command: # @human-monitor-command:
# #

View file

@ -30,15 +30,17 @@ static unsigned int pool_size;
Coroutine *qemu_coroutine_create(CoroutineEntry *entry) Coroutine *qemu_coroutine_create(CoroutineEntry *entry)
{ {
Coroutine *co; Coroutine *co = NULL;
qemu_mutex_lock(&pool_lock); if (CONFIG_COROUTINE_POOL) {
co = QSLIST_FIRST(&pool); qemu_mutex_lock(&pool_lock);
if (co) { co = QSLIST_FIRST(&pool);
QSLIST_REMOVE_HEAD(&pool, pool_next); if (co) {
pool_size--; QSLIST_REMOVE_HEAD(&pool, pool_next);
pool_size--;
}
qemu_mutex_unlock(&pool_lock);
} }
qemu_mutex_unlock(&pool_lock);
if (!co) { if (!co) {
co = qemu_coroutine_new(); co = qemu_coroutine_new();
@ -51,15 +53,17 @@ Coroutine *qemu_coroutine_create(CoroutineEntry *entry)
static void coroutine_delete(Coroutine *co) static void coroutine_delete(Coroutine *co)
{ {
qemu_mutex_lock(&pool_lock); if (CONFIG_COROUTINE_POOL) {
if (pool_size < POOL_MAX_SIZE) { qemu_mutex_lock(&pool_lock);
QSLIST_INSERT_HEAD(&pool, co, pool_next); if (pool_size < POOL_MAX_SIZE) {
co->caller = NULL; QSLIST_INSERT_HEAD(&pool, co, pool_next);
pool_size++; co->caller = NULL;
pool_size++;
qemu_mutex_unlock(&pool_lock);
return;
}
qemu_mutex_unlock(&pool_lock); qemu_mutex_unlock(&pool_lock);
return;
} }
qemu_mutex_unlock(&pool_lock);
qemu_coroutine_delete(co); qemu_coroutine_delete(co);
} }

View file

@ -67,5 +67,11 @@ DEF("resize", img_resize,
"resize [-q] filename [+ | -]size") "resize [-q] filename [+ | -]size")
STEXI STEXI
@item resize [-q] @var{filename} [+ | -]@var{size} @item resize [-q] @var{filename} [+ | -]@var{size}
ETEXI
DEF("amend", img_amend,
"amend [-q] [-f fmt] -o options filename")
STEXI
@item amend [-q] [-f @var{fmt}] -o @var{options} @var{filename}
@end table @end table
ETEXI ETEXI

View file

@ -266,6 +266,7 @@ static BlockDriverState *bdrv_new_open(const char *filename,
BlockDriverState *bs; BlockDriverState *bs;
BlockDriver *drv; BlockDriver *drv;
char password[256]; char password[256];
Error *local_err = NULL;
int ret; int ret;
bs = bdrv_new("image"); bs = bdrv_new("image");
@ -280,9 +281,11 @@ static BlockDriverState *bdrv_new_open(const char *filename,
drv = NULL; drv = NULL;
} }
ret = bdrv_open(bs, filename, NULL, flags, drv); ret = bdrv_open(bs, filename, NULL, flags, drv, &local_err);
if (ret < 0) { if (ret < 0) {
error_report("Could not open '%s': %s", filename, strerror(-ret)); error_report("Could not open '%s': %s", filename,
error_get_pretty(local_err));
error_free(local_err);
goto fail; goto fail;
} }
@ -409,7 +412,7 @@ static int img_create(int argc, char **argv)
bdrv_img_create(filename, fmt, base_filename, base_fmt, bdrv_img_create(filename, fmt, base_filename, base_fmt,
options, img_size, BDRV_O_FLAGS, &local_err, quiet); options, img_size, BDRV_O_FLAGS, &local_err, quiet);
if (error_is_set(&local_err)) { if (error_is_set(&local_err)) {
error_report("%s", error_get_pretty(local_err)); error_report("%s: %s", filename, error_get_pretty(local_err));
error_free(local_err); error_free(local_err);
return 1; return 1;
} }
@ -1136,6 +1139,7 @@ static int img_convert(int argc, char **argv)
float local_progress = 0; float local_progress = 0;
int min_sparse = 8; /* Need at least 4k of zeros for sparse detection */ int min_sparse = 8; /* Need at least 4k of zeros for sparse detection */
bool quiet = false; bool quiet = false;
Error *local_err = NULL;
fmt = NULL; fmt = NULL;
out_fmt = "raw"; out_fmt = "raw";
@ -1338,18 +1342,11 @@ static int img_convert(int argc, char **argv)
if (!skip_create) { if (!skip_create) {
/* Create the new image */ /* Create the new image */
ret = bdrv_create(drv, out_filename, param); ret = bdrv_create(drv, out_filename, param, &local_err);
if (ret < 0) { if (ret < 0) {
if (ret == -ENOTSUP) { error_report("%s: error while converting %s: %s",
error_report("Formatting not supported for file format '%s'", out_filename, out_fmt, error_get_pretty(local_err));
out_fmt); error_free(local_err);
} else if (ret == -EFBIG) {
error_report("The image size is too large for file format '%s'",
out_fmt);
} else {
error_report("%s: error while converting %s: %s",
out_filename, out_fmt, strerror(-ret));
}
goto out; goto out;
} }
} }
@ -1842,7 +1839,7 @@ static void dump_map_entry(OutputFormat output_format, MapEntry *e,
(e->flags & BDRV_BLOCK_ZERO) ? "true" : "false", (e->flags & BDRV_BLOCK_ZERO) ? "true" : "false",
(e->flags & BDRV_BLOCK_DATA) ? "true" : "false"); (e->flags & BDRV_BLOCK_DATA) ? "true" : "false");
if (e->flags & BDRV_BLOCK_OFFSET_VALID) { if (e->flags & BDRV_BLOCK_OFFSET_VALID) {
printf(", 'offset': %"PRId64"", e->offset); printf(", \"offset\": %"PRId64"", e->offset);
} }
putchar('}'); putchar('}');
@ -2006,6 +2003,7 @@ static int img_snapshot(int argc, char **argv)
int action = 0; int action = 0;
qemu_timeval tv; qemu_timeval tv;
bool quiet = false; bool quiet = false;
Error *err = NULL;
bdrv_oflags = BDRV_O_FLAGS | BDRV_O_RDWR; bdrv_oflags = BDRV_O_FLAGS | BDRV_O_RDWR;
/* Parse commandline parameters */ /* Parse commandline parameters */
@ -2098,10 +2096,12 @@ static int img_snapshot(int argc, char **argv)
break; break;
case SNAPSHOT_DELETE: case SNAPSHOT_DELETE:
ret = bdrv_snapshot_delete(bs, snapshot_name); bdrv_snapshot_delete_by_id_or_name(bs, snapshot_name, &err);
if (ret) { if (error_is_set(&err)) {
error_report("Could not delete snapshot '%s': %d (%s)", error_report("Could not delete snapshot '%s': (%s)",
snapshot_name, ret, strerror(-ret)); snapshot_name, error_get_pretty(err));
error_free(err);
ret = 1;
} }
break; break;
} }
@ -2124,6 +2124,7 @@ static int img_rebase(int argc, char **argv)
int unsafe = 0; int unsafe = 0;
int progress = 0; int progress = 0;
bool quiet = false; bool quiet = false;
Error *local_err = NULL;
/* Parse commandline parameters */ /* Parse commandline parameters */
fmt = NULL; fmt = NULL;
@ -2227,18 +2228,21 @@ static int img_rebase(int argc, char **argv)
bs_old_backing = bdrv_new("old_backing"); bs_old_backing = bdrv_new("old_backing");
bdrv_get_backing_filename(bs, backing_name, sizeof(backing_name)); bdrv_get_backing_filename(bs, backing_name, sizeof(backing_name));
ret = bdrv_open(bs_old_backing, backing_name, NULL, BDRV_O_FLAGS, ret = bdrv_open(bs_old_backing, backing_name, NULL, BDRV_O_FLAGS,
old_backing_drv); old_backing_drv, &local_err);
if (ret) { if (ret) {
error_report("Could not open old backing file '%s'", backing_name); error_report("Could not open old backing file '%s': %s",
backing_name, error_get_pretty(local_err));
error_free(local_err);
goto out; goto out;
} }
if (out_baseimg[0]) { if (out_baseimg[0]) {
bs_new_backing = bdrv_new("new_backing"); bs_new_backing = bdrv_new("new_backing");
ret = bdrv_open(bs_new_backing, out_baseimg, NULL, BDRV_O_FLAGS, ret = bdrv_open(bs_new_backing, out_baseimg, NULL, BDRV_O_FLAGS,
new_backing_drv); new_backing_drv, &local_err);
if (ret) { if (ret) {
error_report("Could not open new backing file '%s'", error_report("Could not open new backing file '%s': %s",
out_baseimg); out_baseimg, error_get_pretty(local_err));
error_free(local_err);
goto out; goto out;
} }
} }
@ -2525,6 +2529,90 @@ out:
return 0; return 0;
} }
static int img_amend(int argc, char **argv)
{
int c, ret = 0;
char *options = NULL;
QEMUOptionParameter *create_options = NULL, *options_param = NULL;
const char *fmt = NULL, *filename;
bool quiet = false;
BlockDriverState *bs = NULL;
for (;;) {
c = getopt(argc, argv, "hqf:o:");
if (c == -1) {
break;
}
switch (c) {
case 'h':
case '?':
help();
break;
case 'o':
options = optarg;
break;
case 'f':
fmt = optarg;
break;
case 'q':
quiet = true;
break;
}
}
if (optind != argc - 1) {
help();
}
if (!options) {
help();
}
filename = argv[argc - 1];
bs = bdrv_new_open(filename, fmt, BDRV_O_FLAGS | BDRV_O_RDWR, true, quiet);
if (!bs) {
error_report("Could not open image '%s'", filename);
ret = -1;
goto out;
}
fmt = bs->drv->format_name;
if (is_help_option(options)) {
ret = print_block_option_help(filename, fmt);
goto out;
}
create_options = append_option_parameters(create_options,
bs->drv->create_options);
options_param = parse_option_parameters(options, create_options,
options_param);
if (options_param == NULL) {
error_report("Invalid options for file format '%s'", fmt);
ret = -1;
goto out;
}
ret = bdrv_amend_options(bs, options_param);
if (ret < 0) {
error_report("Error while amending options: %s", strerror(-ret));
goto out;
}
out:
if (bs) {
bdrv_unref(bs);
}
free_option_parameters(create_options);
free_option_parameters(options_param);
if (ret) {
return 1;
}
return 0;
}
static const img_cmd_t img_cmds[] = { static const img_cmd_t img_cmds[] = {
#define DEF(option, callback, arg_string) \ #define DEF(option, callback, arg_string) \
{ option, callback }, { option, callback },

View file

@ -350,6 +350,11 @@ sizes accordingly. Failure to do so will result in data loss!
After using this command to grow a disk image, you must use file system and After using this command to grow a disk image, you must use file system and
partitioning tools inside the VM to actually begin using the new space on the partitioning tools inside the VM to actually begin using the new space on the
device. device.
@item amend [-f @var{fmt}] -o @var{options} @var{filename}
Amends the image format specific @var{options} for the image file
@var{filename}. Not all file formats support this operation.
@end table @end table
@c man end @c man end

View file

@ -46,21 +46,27 @@ static const cmdinfo_t close_cmd = {
static int openfile(char *name, int flags, int growable) static int openfile(char *name, int flags, int growable)
{ {
Error *local_err = NULL;
if (qemuio_bs) { if (qemuio_bs) {
fprintf(stderr, "file open already, try 'help close'\n"); fprintf(stderr, "file open already, try 'help close'\n");
return 1; return 1;
} }
if (growable) { if (growable) {
if (bdrv_file_open(&qemuio_bs, name, NULL, flags)) { if (bdrv_file_open(&qemuio_bs, name, NULL, flags, &local_err)) {
fprintf(stderr, "%s: can't open device %s\n", progname, name); fprintf(stderr, "%s: can't open device %s: %s\n", progname, name,
error_get_pretty(local_err));
error_free(local_err);
return 1; return 1;
} }
} else { } else {
qemuio_bs = bdrv_new("hda"); qemuio_bs = bdrv_new("hda");
if (bdrv_open(qemuio_bs, name, NULL, flags, NULL) < 0) { if (bdrv_open(qemuio_bs, name, NULL, flags, NULL, &local_err) < 0) {
fprintf(stderr, "%s: can't open device %s\n", progname, name); fprintf(stderr, "%s: can't open device %s: %s\n", progname, name,
error_get_pretty(local_err));
error_free(local_err);
bdrv_unref(qemuio_bs); bdrv_unref(qemuio_bs);
qemuio_bs = NULL; qemuio_bs = NULL;
return 1; return 1;

View file

@ -355,6 +355,7 @@ int main(int argc, char **argv)
#endif #endif
pthread_t client_thread; pthread_t client_thread;
const char *fmt = NULL; const char *fmt = NULL;
Error *local_err = NULL;
/* The client thread uses SIGTERM to interrupt the server. A signal /* The client thread uses SIGTERM to interrupt the server. A signal
* handler ensures that "qemu-nbd -v -c" exits with a nice status code. * handler ensures that "qemu-nbd -v -c" exits with a nice status code.
@ -573,10 +574,11 @@ int main(int argc, char **argv)
bs = bdrv_new("hda"); bs = bdrv_new("hda");
srcpath = argv[optind]; srcpath = argv[optind];
ret = bdrv_open(bs, srcpath, NULL, flags, drv); ret = bdrv_open(bs, srcpath, NULL, flags, drv, &local_err);
if (ret < 0) { if (ret < 0) {
errno = -ret; errno = -ret;
err(EXIT_FAILURE, "Failed to bdrv_open '%s'", argv[optind]); err(EXIT_FAILURE, "Failed to bdrv_open '%s': %s", argv[optind],
error_get_pretty(local_err));
} }
fd_size = bdrv_getlength(bs); fd_size = bdrv_getlength(bs);

View file

@ -1001,14 +1001,15 @@ SQMP
transaction transaction
----------- -----------
Atomically operate on one or more block devices. The only supported Atomically operate on one or more block devices. The only supported operations
operation for now is snapshotting. If there is any failure performing for now are drive-backup, internal and external snapshotting. A list of
any of the operations, all snapshots for the group are abandoned, and dictionaries is accepted, that contains the actions to be performed.
the original disks pre-snapshot attempt are used. If there is any failure performing any of the operations, all operations
for the group are abandoned.
A list of dictionaries is accepted, that contains the actions to be performed. For external snapshots, the dictionary contains the device, the file to use for
For snapshots this is the device, the file to use for the new snapshot, the new snapshot, and the format. The default format, if not specified, is
and the format. The default format, if not specified, is qcow2. qcow2.
Each new snapshot defaults to being created by QEMU (wiping any Each new snapshot defaults to being created by QEMU (wiping any
contents if the file already exists), but it is also possible to reuse contents if the file already exists), but it is also possible to reuse
@ -1017,6 +1018,17 @@ the new image file has the same contents as the current one; QEMU cannot
perform any meaningful check. Typically this is achieved by using the perform any meaningful check. Typically this is achieved by using the
current image file as the backing file for the new image. current image file as the backing file for the new image.
On failure, the original disks pre-snapshot attempt will be used.
For internal snapshots, the dictionary contains the device and the snapshot's
name. If an internal snapshot matching name already exists, the request will
be rejected. Only some image formats support it, for example, qcow2, rbd,
and sheepdog.
On failure, qemu will try delete the newly created internal snapshot in the
transaction. When an I/O error occurs during deletion, the user needs to fix
it later with qemu-img or other command.
Arguments: Arguments:
actions array: actions array:
@ -1029,6 +1041,9 @@ actions array:
- "format": format of new image (json-string, optional) - "format": format of new image (json-string, optional)
- "mode": whether and how QEMU should create the snapshot file - "mode": whether and how QEMU should create the snapshot file
(NewImageMode, optional, default "absolute-paths") (NewImageMode, optional, default "absolute-paths")
When "type" is "blockdev-snapshot-internal-sync":
- "device": device name to snapshot (json-string)
- "name": name of the new snapshot (json-string)
Example: Example:
@ -1040,7 +1055,10 @@ Example:
{ 'type': 'blockdev-snapshot-sync', 'data' : { "device": "ide-hd1", { 'type': 'blockdev-snapshot-sync', 'data' : { "device": "ide-hd1",
"snapshot-file": "/some/place/my-image2", "snapshot-file": "/some/place/my-image2",
"mode": "existing", "mode": "existing",
"format": "qcow2" } } ] } } "format": "qcow2" } },
{ 'type': 'blockdev-snapshot-internal-sync', 'data' : {
"device": "ide-hd2",
"name": "snapshot0" } } ] } }
<- { "return": {} } <- { "return": {} }
EQMP EQMP
@ -1077,6 +1095,76 @@ Example:
"format": "qcow2" } } "format": "qcow2" } }
<- { "return": {} } <- { "return": {} }
EQMP
{
.name = "blockdev-snapshot-internal-sync",
.args_type = "device:B,name:s",
.mhandler.cmd_new = qmp_marshal_input_blockdev_snapshot_internal_sync,
},
SQMP
blockdev-snapshot-internal-sync
-------------------------------
Synchronously take an internal snapshot of a block device when the format of
image used supports it. If the name is an empty string, or a snapshot with
name already exists, the operation will fail.
Arguments:
- "device": device name to snapshot (json-string)
- "name": name of the new snapshot (json-string)
Example:
-> { "execute": "blockdev-snapshot-internal-sync",
"arguments": { "device": "ide-hd0",
"name": "snapshot0" }
}
<- { "return": {} }
EQMP
{
.name = "blockdev-snapshot-delete-internal-sync",
.args_type = "device:B,id:s?,name:s?",
.mhandler.cmd_new =
qmp_marshal_input_blockdev_snapshot_delete_internal_sync,
},
SQMP
blockdev-snapshot-delete-internal-sync
--------------------------------------
Synchronously delete an internal snapshot of a block device when the format of
image used supports it. The snapshot is identified by name or id or both. One
of name or id is required. If the snapshot is not found, the operation will
fail.
Arguments:
- "device": device name (json-string)
- "id": ID of the snapshot (json-string, optional)
- "name": name of the snapshot (json-string, optional)
Example:
-> { "execute": "blockdev-snapshot-delete-internal-sync",
"arguments": { "device": "ide-hd0",
"name": "snapshot0" }
}
<- { "return": {
"id": "1",
"name": "snapshot0",
"vm-state-size": 0,
"date-sec": 1000012,
"date-nsec": 10,
"vm-clock-sec": 100,
"vm-clock-nsec": 20
}
}
EQMP EQMP
{ {

View file

@ -2325,18 +2325,21 @@ static int del_existing_snapshots(Monitor *mon, const char *name)
{ {
BlockDriverState *bs; BlockDriverState *bs;
QEMUSnapshotInfo sn1, *snapshot = &sn1; QEMUSnapshotInfo sn1, *snapshot = &sn1;
int ret; Error *err = NULL;
bs = NULL; bs = NULL;
while ((bs = bdrv_next(bs))) { while ((bs = bdrv_next(bs))) {
if (bdrv_can_snapshot(bs) && if (bdrv_can_snapshot(bs) &&
bdrv_snapshot_find(bs, snapshot, name) >= 0) bdrv_snapshot_find(bs, snapshot, name) >= 0)
{ {
ret = bdrv_snapshot_delete(bs, name); bdrv_snapshot_delete_by_id_or_name(bs, name, &err);
if (ret < 0) { if (error_is_set(&err)) {
monitor_printf(mon, monitor_printf(mon,
"Error while deleting snapshot on '%s'\n", "Error while deleting snapshot on device '%s':"
bdrv_get_device_name(bs)); " %s\n",
bdrv_get_device_name(bs),
error_get_pretty(err));
error_free(err);
return -1; return -1;
} }
} }
@ -2550,7 +2553,7 @@ int load_vmstate(const char *name)
void do_delvm(Monitor *mon, const QDict *qdict) void do_delvm(Monitor *mon, const QDict *qdict)
{ {
BlockDriverState *bs, *bs1; BlockDriverState *bs, *bs1;
int ret; Error *err = NULL;
const char *name = qdict_get_str(qdict, "name"); const char *name = qdict_get_str(qdict, "name");
bs = find_vmstate_bs(); bs = find_vmstate_bs();
@ -2562,15 +2565,14 @@ void do_delvm(Monitor *mon, const QDict *qdict)
bs1 = NULL; bs1 = NULL;
while ((bs1 = bdrv_next(bs1))) { while ((bs1 = bdrv_next(bs1))) {
if (bdrv_can_snapshot(bs1)) { if (bdrv_can_snapshot(bs1)) {
ret = bdrv_snapshot_delete(bs1, name); bdrv_snapshot_delete_by_id_or_name(bs, name, &err);
if (ret < 0) { if (error_is_set(&err)) {
if (ret == -ENOTSUP) monitor_printf(mon,
monitor_printf(mon, "Error while deleting snapshot on device '%s':"
"Snapshots not supported on device '%s'\n", " %s\n",
bdrv_get_device_name(bs1)); bdrv_get_device_name(bs),
else error_get_pretty(err));
monitor_printf(mon, "Error %d while deleting snapshot on " error_free(err);
"'%s'\n", ret, bdrv_get_device_name(bs1));
} }
} }
} }

View file

@ -174,6 +174,7 @@ tests/boot-order-test$(EXESUF): tests/boot-order-test.o $(libqos-obj-y)
tests/tmp105-test$(EXESUF): tests/tmp105-test.o $(libqos-omap-obj-y) tests/tmp105-test$(EXESUF): tests/tmp105-test.o $(libqos-omap-obj-y)
tests/i440fx-test$(EXESUF): tests/i440fx-test.o $(libqos-pc-obj-y) tests/i440fx-test$(EXESUF): tests/i440fx-test.o $(libqos-pc-obj-y)
tests/fw_cfg-test$(EXESUF): tests/fw_cfg-test.o $(libqos-pc-obj-y) tests/fw_cfg-test$(EXESUF): tests/fw_cfg-test.o $(libqos-pc-obj-y)
tests/qemu-iotests/socket_scm_helper$(EXESUF): tests/qemu-iotests/socket_scm_helper.o
# QTest rules # QTest rules
@ -252,7 +253,7 @@ check-report.html: check-report.xml
# Other tests # Other tests
.PHONY: check-tests/qemu-iotests-quick.sh .PHONY: check-tests/qemu-iotests-quick.sh
check-tests/qemu-iotests-quick.sh: tests/qemu-iotests-quick.sh qemu-img$(EXESUF) qemu-io$(EXESUF) check-tests/qemu-iotests-quick.sh: tests/qemu-iotests-quick.sh qemu-img$(EXESUF) qemu-io$(EXESUF) tests/qemu-iotests/socket_scm_helper$(EXESUF)
$< $<
.PHONY: check-tests/test-qapi.py .PHONY: check-tests/test-qapi.py

View file

@ -30,7 +30,7 @@ status=1 # failure is the default!
_cleanup() _cleanup()
{ {
# _cleanup_test_img _cleanup_test_img
true true
} }
trap "_cleanup; exit \$status" 0 1 2 3 15 trap "_cleanup; exit \$status" 0 1 2 3 15

View file

@ -95,7 +95,8 @@ function overlay_io()
} }
overlay_io | $QEMU_IO $TEST_IMG | _filter_qemu_io |\ overlay_io | $QEMU_IO $TEST_IMG | _filter_qemu_io |\
sed -e 's/bytes at offset [0-9]*/bytes at offset XXX/g' sed -e 's/bytes at offset [0-9]*/bytes at offset XXX/g' \
-e 's/qemu-io> //g' | paste - - | sort | tr '\t' '\n'
echo echo
echo "== Verify image content ==" echo "== Verify image content =="

View file

@ -517,7 +517,65 @@ qemu-io> wrote 65536/65536 bytes at offset 16711680
qemu-io> Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file='TEST_DIR/t.IMGFMT.base' qemu-io> Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 backing_file='TEST_DIR/t.IMGFMT.base'
== Some concurrent requests touching the same cluster == == Some concurrent requests touching the same cluster ==
qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> qemu-io> wrote 65536/65536 bytes at offset XXX wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
@ -577,8 +635,6 @@ wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 81920/81920 bytes at offset XXX
80 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX wrote 65536/65536 bytes at offset XXX
@ -647,64 +703,8 @@ wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 81920/81920 bytes at offset XXX wrote 81920/81920 bytes at offset XXX
80 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 80 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX wrote 81920/81920 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 80 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset XXX
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
== Verify image content == == Verify image content ==
qemu-io> read 4096/4096 bytes at offset 2064384 qemu-io> read 4096/4096 bytes at offset 2064384

View file

@ -1,6 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
# #
# Tests for fdsets. # Tests for fdsets and getfd.
# #
# Copyright (C) 2012 IBM Corp. # Copyright (C) 2012 IBM Corp.
# #
@ -125,5 +125,54 @@ class TestFdSets(iotests.QMPTestCase):
'No file descriptor supplied via SCM_RIGHTS') 'No file descriptor supplied via SCM_RIGHTS')
self.vm.shutdown() self.vm.shutdown()
# Add fd at runtime, there are two ways: monitor related or fdset related
class TestSCMFd(iotests.QMPTestCase):
def setUp(self):
self.vm = iotests.VM()
qemu_img('create', '-f', iotests.imgfmt, image0, '128K')
# Add an unused monitor, to verify it works fine when two monitor
# instances present
self.vm.add_monitor_telnet("0",4445)
self.vm.launch()
def tearDown(self):
self.vm.shutdown()
os.remove(image0)
def _send_fd_by_SCM(self):
ret = self.vm.send_fd_scm(image0)
self.assertEqual(ret, 0, 'Failed to send fd with UNIX SCM')
def test_add_fd(self):
self._send_fd_by_SCM()
result = self.vm.qmp('add-fd', fdset_id=2, opaque='image0:r')
self.assert_qmp(result, 'return/fdset-id', 2)
def test_getfd(self):
self._send_fd_by_SCM()
result = self.vm.qmp('getfd', fdname='image0:r')
self.assert_qmp(result, 'return', {})
def test_getfd_invalid_fdname(self):
self._send_fd_by_SCM()
result = self.vm.qmp('getfd', fdname='0image0:r')
self.assert_qmp(result, 'error/class', 'GenericError')
self.assert_qmp(result, 'error/desc',
"Parameter 'fdname' expects a name not starting with a digit")
def test_closefd(self):
self._send_fd_by_SCM()
result = self.vm.qmp('getfd', fdname='image0:r')
self.assert_qmp(result, 'return', {})
result = self.vm.qmp('closefd', fdname='image0:r')
self.assert_qmp(result, 'return', {})
def test_closefd_fd_not_found(self):
fdname = 'image0:r'
result = self.vm.qmp('closefd', fdname=fdname)
self.assert_qmp(result, 'error/class', 'GenericError')
self.assert_qmp(result, 'error/desc',
"File descriptor named '%s' not found" % fdname)
if __name__ == '__main__': if __name__ == '__main__':
iotests.main(supported_fmts=['raw']) iotests.main(supported_fmts=['raw'])

View file

@ -1,5 +1,5 @@
...... ...........
---------------------------------------------------------------------- ----------------------------------------------------------------------
Ran 6 tests Ran 11 tests
OK OK

View file

@ -96,7 +96,7 @@ qemu-img: Image size must be less than 8 EiB!
qemu-img create -f qcow2 -o size=-1024 TEST_DIR/t.qcow2 qemu-img create -f qcow2 -o size=-1024 TEST_DIR/t.qcow2
qemu-img: qcow2 doesn't support shrinking images yet qemu-img: qcow2 doesn't support shrinking images yet
qemu-img: Formatting or formatting option not supported for file format 'qcow2' qemu-img: TEST_DIR/t.qcow2: Could not resize image: Operation not supported
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=-1024 encryption=off cluster_size=65536 lazy_refcounts=off Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=-1024 encryption=off cluster_size=65536 lazy_refcounts=off
qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- -1k qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- -1k
@ -104,7 +104,7 @@ qemu-img: Image size must be less than 8 EiB!
qemu-img create -f qcow2 -o size=-1k TEST_DIR/t.qcow2 qemu-img create -f qcow2 -o size=-1k TEST_DIR/t.qcow2
qemu-img: qcow2 doesn't support shrinking images yet qemu-img: qcow2 doesn't support shrinking images yet
qemu-img: Formatting or formatting option not supported for file format 'qcow2' qemu-img: TEST_DIR/t.qcow2: Could not resize image: Operation not supported
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=-1024 encryption=off cluster_size=65536 lazy_refcounts=off Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=-1024 encryption=off cluster_size=65536 lazy_refcounts=off
qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- 1kilobyte qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- 1kilobyte
@ -120,7 +120,7 @@ qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes.
qemu-img create -f qcow2 -o size=foobar TEST_DIR/t.qcow2 qemu-img create -f qcow2 -o size=foobar TEST_DIR/t.qcow2
qemu-img: Parameter 'size' expects a size qemu-img: Parameter 'size' expects a size
qemu-img: Invalid options for file format 'qcow2'. qemu-img: TEST_DIR/t.qcow2: Invalid options for file format 'qcow2'.
== Check correct interpretation of suffixes for cluster size == == Check correct interpretation of suffixes for cluster size ==
@ -163,13 +163,11 @@ qemu-img create -f qcow2 -o compat=1.1 TEST_DIR/t.qcow2 64M
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='1.1' encryption=off cluster_size=65536 lazy_refcounts=off Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='1.1' encryption=off cluster_size=65536 lazy_refcounts=off
qemu-img create -f qcow2 -o compat=0.42 TEST_DIR/t.qcow2 64M qemu-img create -f qcow2 -o compat=0.42 TEST_DIR/t.qcow2 64M
Invalid compatibility level: '0.42' qemu-img: TEST_DIR/t.qcow2: Invalid compatibility level: '0.42'
qemu-img: TEST_DIR/t.qcow2: error while creating qcow2: Invalid argument
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.42' encryption=off cluster_size=65536 lazy_refcounts=off Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.42' encryption=off cluster_size=65536 lazy_refcounts=off
qemu-img create -f qcow2 -o compat=foobar TEST_DIR/t.qcow2 64M qemu-img create -f qcow2 -o compat=foobar TEST_DIR/t.qcow2 64M
Invalid compatibility level: 'foobar' qemu-img: TEST_DIR/t.qcow2: Invalid compatibility level: 'foobar'
qemu-img: TEST_DIR/t.qcow2: error while creating qcow2: Invalid argument
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='foobar' encryption=off cluster_size=65536 lazy_refcounts=off Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='foobar' encryption=off cluster_size=65536 lazy_refcounts=off
== Check preallocation option == == Check preallocation option ==
@ -181,8 +179,7 @@ qemu-img create -f qcow2 -o preallocation=metadata TEST_DIR/t.qcow2 64M
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 preallocation='metadata' lazy_refcounts=off Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 preallocation='metadata' lazy_refcounts=off
qemu-img create -f qcow2 -o preallocation=1234 TEST_DIR/t.qcow2 64M qemu-img create -f qcow2 -o preallocation=1234 TEST_DIR/t.qcow2 64M
Invalid preallocation mode: '1234' qemu-img: TEST_DIR/t.qcow2: Invalid preallocation mode: '1234'
qemu-img: TEST_DIR/t.qcow2: error while creating qcow2: Invalid argument
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 preallocation='1234' lazy_refcounts=off Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 preallocation='1234' lazy_refcounts=off
== Check encryption option == == Check encryption option ==
@ -205,8 +202,7 @@ qemu-img create -f qcow2 -o compat=0.10,lazy_refcounts=off TEST_DIR/t.qcow2 64M
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.10' encryption=off cluster_size=65536 lazy_refcounts=off Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.10' encryption=off cluster_size=65536 lazy_refcounts=off
qemu-img create -f qcow2 -o compat=0.10,lazy_refcounts=on TEST_DIR/t.qcow2 64M qemu-img create -f qcow2 -o compat=0.10,lazy_refcounts=on TEST_DIR/t.qcow2 64M
Lazy refcounts only supported with compatibility level 1.1 and above (use compat=1.1 or greater) qemu-img: TEST_DIR/t.qcow2: Lazy refcounts only supported with compatibility level 1.1 and above (use compat=1.1 or greater)
qemu-img: TEST_DIR/t.qcow2: error while creating qcow2: Invalid argument
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.10' encryption=off cluster_size=65536 lazy_refcounts=on Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 compat='0.10' encryption=off cluster_size=65536 lazy_refcounts=on
*** done *** done

View file

@ -4,20 +4,16 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
=== Unknown option === === Unknown option ===
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt= Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt' QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=: could not open disk image TEST_DIR/t.qcow2: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt'
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=: could not open disk image TEST_DIR/t.qcow2: Invalid argument
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=on Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=on
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=on: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt' QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=on: could not open disk image TEST_DIR/t.qcow2: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt'
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=on: could not open disk image TEST_DIR/t.qcow2: Invalid argument
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=1234 Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=1234
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=1234: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt' QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=1234: could not open disk image TEST_DIR/t.qcow2: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt'
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=1234: could not open disk image TEST_DIR/t.qcow2: Invalid argument
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=foo Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=foo
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=foo: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt' QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=foo: could not open disk image TEST_DIR/t.qcow2: Block format 'qcow2' used by device 'ide0-hd0' doesn't support the option 'unknown_opt'
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,unknown_opt=foo: could not open disk image TEST_DIR/t.qcow2: Invalid argument
=== Enable and disable lazy refcounting on the command line, plus some invalid values === === Enable and disable lazy refcounting on the command line, plus some invalid values ===
@ -31,24 +27,20 @@ QEMU X.Y.Z monitor - type 'help' for more information
(qemu) qququiquit (qemu) qququiquit
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts= Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=: Parameter 'lazy-refcounts' expects 'on' or 'off' QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=: could not open disk image TEST_DIR/t.qcow2: Parameter 'lazy-refcounts' expects 'on' or 'off'
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=: could not open disk image TEST_DIR/t.qcow2: Invalid argument
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=42 Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=42
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=42: Parameter 'lazy-refcounts' expects 'on' or 'off' QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=42: could not open disk image TEST_DIR/t.qcow2: Parameter 'lazy-refcounts' expects 'on' or 'off'
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=42: could not open disk image TEST_DIR/t.qcow2: Invalid argument
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=foo Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=foo
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=foo: Parameter 'lazy-refcounts' expects 'on' or 'off' QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=foo: could not open disk image TEST_DIR/t.qcow2: Parameter 'lazy-refcounts' expects 'on' or 'off'
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=foo: could not open disk image TEST_DIR/t.qcow2: Invalid argument
=== With version 2 images enabling lazy refcounts must fail === === With version 2 images enabling lazy refcounts must fail ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=on Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=on
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=on: Lazy refcounts require a qcow2 image with at least qemu 1.1 compatibility level QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=on: could not open disk image TEST_DIR/t.qcow2: Lazy refcounts require a qcow2 image with at least qemu 1.1 compatibility level
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=on: could not open disk image TEST_DIR/t.qcow2: Invalid argument
Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=off Testing: -drive file=TEST_DIR/t.qcow2,format=qcow2,lazy-refcounts=off
QEMU X.Y.Z monitor - type 'help' for more information QEMU X.Y.Z monitor - type 'help' for more information
@ -208,21 +200,18 @@ QEMU X.Y.Z monitor - type 'help' for more information
(qemu) qququiquit (qemu) qququiquit
Testing: -drive file=TEST_DIR/t.qcow2,file.driver=qcow2 Testing: -drive file=TEST_DIR/t.qcow2,file.driver=qcow2
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,file.driver=qcow2: Can't use 'qcow2' as a block driver for the protocol level QEMU_PROG: -drive file=TEST_DIR/t.qcow2,file.driver=qcow2: could not open disk image TEST_DIR/t.qcow2: Can't use 'qcow2' as a block driver for the protocol level
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,file.driver=qcow2: could not open disk image TEST_DIR/t.qcow2: Invalid argument
=== Parsing protocol from file name === === Parsing protocol from file name ===
Testing: -hda foo:bar Testing: -hda foo:bar
QEMU_PROG: -hda foo:bar: Unknown protocol QEMU_PROG: -hda foo:bar: could not open disk image foo:bar: Unknown protocol
QEMU_PROG: -hda foo:bar: could not open disk image foo:bar: No such file or directory
Testing: -drive file=foo:bar Testing: -drive file=foo:bar
QEMU_PROG: -drive file=foo:bar: Unknown protocol QEMU_PROG: -drive file=foo:bar: could not open disk image foo:bar: Unknown protocol
QEMU_PROG: -drive file=foo:bar: could not open disk image foo:bar: No such file or directory
Testing: -drive file.filename=foo:bar Testing: -drive file.filename=foo:bar
QEMU_PROG: -drive file.filename=foo:bar: could not open disk image ide0-hd0: No such file or directory QEMU_PROG: -drive file.filename=foo:bar: could not open disk image ide0-hd0: Could not open 'foo:bar': No such file or directory
*** done *** done

View file

@ -1,10 +1,10 @@
QA output created by 054 QA output created by 054
creating too large image (1 EB) creating too large image (1 EB)
qemu-img: The image size is too large for file format 'qcow2' (try using a larger cluster size) qemu-img: TEST_DIR/t.IMGFMT: The image size is too large for file format 'IMGFMT' (try using a larger cluster size)
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1152921504606846976 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1152921504606846976
creating too large image (1 EB) using qcow2.py creating too large image (1 EB) using qcow2.py
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296
qemu-img: Could not open 'TEST_DIR/t.qcow2': File too large qemu-img: Could not open 'TEST_DIR/t.qcow2': Image is too big
*** done *** done

259
tests/qemu-iotests/057 Executable file
View file

@ -0,0 +1,259 @@
#!/usr/bin/env python
#
# Tests for internal snapshot.
#
# Copyright (C) 2013 IBM, Inc.
#
# Based on 055.
#
# 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/>.
#
import time
import os
import iotests
from iotests import qemu_img, qemu_io
test_drv_base_name = 'drive'
class ImageSnapshotTestCase(iotests.QMPTestCase):
image_len = 120 * 1024 * 1024 # MB
def __init__(self, *args):
self.expect = []
super(ImageSnapshotTestCase, self).__init__(*args)
def _setUp(self, test_img_base_name, image_num):
self.vm = iotests.VM()
for i in range(0, image_num):
filename = '%s%d' % (test_img_base_name, i)
img = os.path.join(iotests.test_dir, filename)
device = '%s%d' % (test_drv_base_name, i)
qemu_img('create', '-f', iotests.imgfmt, img, str(self.image_len))
self.vm.add_drive(img)
self.expect.append({'image': img, 'device': device,
'snapshots': [],
'snapshots_name_counter': 0})
self.vm.launch()
def tearDown(self):
self.vm.shutdown()
for dev_expect in self.expect:
os.remove(dev_expect['image'])
def createSnapshotInTransaction(self, snapshot_num, abort = False):
actions = []
for dev_expect in self.expect:
num = dev_expect['snapshots_name_counter']
for j in range(0, snapshot_num):
name = '%s_sn%d' % (dev_expect['device'], num)
num = num + 1
if abort == False:
dev_expect['snapshots'].append({'name': name})
dev_expect['snapshots_name_counter'] = num
actions.append({
'type': 'blockdev-snapshot-internal-sync',
'data': { 'device': dev_expect['device'],
'name': name },
})
if abort == True:
actions.append({
'type': 'abort',
'data': {},
})
result = self.vm.qmp('transaction', actions = actions)
if abort == True:
self.assert_qmp(result, 'error/class', 'GenericError')
else:
self.assert_qmp(result, 'return', {})
def verifySnapshotInfo(self):
result = self.vm.qmp('query-block')
# Verify each expected result
for dev_expect in self.expect:
# 1. Find the returned image value and snapshot info
image_result = None
for device in result['return']:
if device['device'] == dev_expect['device']:
image_result = device['inserted']['image']
break
self.assertTrue(image_result != None)
# Do not consider zero snapshot case now
sn_list_result = image_result['snapshots']
sn_list_expect = dev_expect['snapshots']
# 2. Verify it with expect
self.assertTrue(len(sn_list_result) == len(sn_list_expect))
for sn_expect in sn_list_expect:
sn_result = None
for sn in sn_list_result:
if sn_expect['name'] == sn['name']:
sn_result = sn
break
self.assertTrue(sn_result != None)
# Fill in the detail info
sn_expect.update(sn_result)
def deleteSnapshot(self, device, id = None, name = None):
sn_list_expect = None
sn_expect = None
self.assertTrue(id != None or name != None)
# Fill in the detail info include ID
self.verifySnapshotInfo()
#find the expected snapshot list
for dev_expect in self.expect:
if dev_expect['device'] == device:
sn_list_expect = dev_expect['snapshots']
break
self.assertTrue(sn_list_expect != None)
if id != None and name != None:
for sn in sn_list_expect:
if sn['id'] == id and sn['name'] == name:
sn_expect = sn
result = \
self.vm.qmp('blockdev-snapshot-delete-internal-sync',
device = device,
id = id,
name = name)
break
elif id != None:
for sn in sn_list_expect:
if sn['id'] == id:
sn_expect = sn
result = \
self.vm.qmp('blockdev-snapshot-delete-internal-sync',
device = device,
id = id)
break
else:
for sn in sn_list_expect:
if sn['name'] == name:
sn_expect = sn
result = \
self.vm.qmp('blockdev-snapshot-delete-internal-sync',
device = device,
name = name)
break
self.assertTrue(sn_expect != None)
self.assert_qmp(result, 'return', sn_expect)
sn_list_expect.remove(sn_expect)
class TestSingleTransaction(ImageSnapshotTestCase):
def setUp(self):
self._setUp('test_a.img', 1)
def test_create(self):
self.createSnapshotInTransaction(1)
self.verifySnapshotInfo()
def test_error_name_empty(self):
actions = [{'type': 'blockdev-snapshot-internal-sync',
'data': { 'device': self.expect[0]['device'],
'name': '' },
}]
result = self.vm.qmp('transaction', actions = actions)
self.assert_qmp(result, 'error/class', 'GenericError')
def test_error_device(self):
actions = [{'type': 'blockdev-snapshot-internal-sync',
'data': { 'device': 'drive_error',
'name': 'a' },
}]
result = self.vm.qmp('transaction', actions = actions)
self.assert_qmp(result, 'error/class', 'DeviceNotFound')
def test_error_exist(self):
self.createSnapshotInTransaction(1)
self.verifySnapshotInfo()
actions = [{'type': 'blockdev-snapshot-internal-sync',
'data': { 'device': self.expect[0]['device'],
'name': self.expect[0]['snapshots'][0] },
}]
result = self.vm.qmp('transaction', actions = actions)
self.assert_qmp(result, 'error/class', 'GenericError')
class TestMultipleTransaction(ImageSnapshotTestCase):
def setUp(self):
self._setUp('test_b.img', 2)
def test_create(self):
self.createSnapshotInTransaction(3)
self.verifySnapshotInfo()
def test_abort(self):
self.createSnapshotInTransaction(2)
self.verifySnapshotInfo()
self.createSnapshotInTransaction(3, abort = True)
self.verifySnapshotInfo()
class TestSnapshotDelete(ImageSnapshotTestCase):
def setUp(self):
self._setUp('test_c.img', 1)
def test_delete_with_id(self):
self.createSnapshotInTransaction(2)
self.verifySnapshotInfo()
self.deleteSnapshot(self.expect[0]['device'],
id = self.expect[0]['snapshots'][0]['id'])
self.verifySnapshotInfo()
def test_delete_with_name(self):
self.createSnapshotInTransaction(3)
self.verifySnapshotInfo()
self.deleteSnapshot(self.expect[0]['device'],
name = self.expect[0]['snapshots'][1]['name'])
self.verifySnapshotInfo()
def test_delete_with_id_and_name(self):
self.createSnapshotInTransaction(4)
self.verifySnapshotInfo()
self.deleteSnapshot(self.expect[0]['device'],
id = self.expect[0]['snapshots'][2]['id'],
name = self.expect[0]['snapshots'][2]['name'])
self.verifySnapshotInfo()
def test_error_device(self):
result = self.vm.qmp('blockdev-snapshot-delete-internal-sync',
device = 'drive_error',
id = '0')
self.assert_qmp(result, 'error/class', 'DeviceNotFound')
def test_error_no_id_and_name(self):
result = self.vm.qmp('blockdev-snapshot-delete-internal-sync',
device = self.expect[0]['device'])
self.assert_qmp(result, 'error/class', 'GenericError')
def test_error_snapshot_not_exist(self):
self.createSnapshotInTransaction(2)
self.verifySnapshotInfo()
result = self.vm.qmp('blockdev-snapshot-delete-internal-sync',
device = self.expect[0]['device'],
id = self.expect[0]['snapshots'][0]['id'],
name = self.expect[0]['snapshots'][1]['name'])
self.assert_qmp(result, 'error/class', 'GenericError')
if __name__ == '__main__':
iotests.main(supported_fmts=['qcow2'])

View file

@ -0,0 +1,5 @@
............
----------------------------------------------------------------------
Ran 12 tests
OK

View file

@ -71,7 +71,7 @@ $QEMU_IO -c "write -P 0x2a 0 512" "$TEST_IMG" | _filter_qemu_io
./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features ./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
# Try to open the image R/W (which should fail) # Try to open the image R/W (which should fail)
$QEMU_IO -c "read 0 512" "$TEST_IMG" 2>&1 | _filter_qemu_io | sed -e "s/can't open device .*$/can't open device/" $QEMU_IO -c "read 0 512" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir | _filter_imgfmt
# Try to open it RO (which should succeed) # Try to open it RO (which should succeed)
$QEMU_IO -c "read 0 512" -r "$TEST_IMG" | _filter_qemu_io $QEMU_IO -c "read 0 512" -r "$TEST_IMG" | _filter_qemu_io

View file

@ -11,8 +11,7 @@ incompatible_features 0x0
qcow2: Preventing invalid write on metadata (overlaps with active L1 table); image marked as corrupt. qcow2: Preventing invalid write on metadata (overlaps with active L1 table); image marked as corrupt.
write failed: Input/output error write failed: Input/output error
incompatible_features 0x2 incompatible_features 0x2
qcow2: Image is corrupt; cannot be opened read/write. qemu-io: can't open device TEST_DIR/t.IMGFMT: IMGFMT: Image is corrupt; cannot be opened read/write
qemu-io: can't open device
no file open, try 'help open' no file open, try 'help open'
read 512/512 bytes at offset 0 read 512/512 bytes at offset 0
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)

206
tests/qemu-iotests/061 Executable file
View file

@ -0,0 +1,206 @@
#!/bin/bash
#
# Test case for image option amendment in qcow2.
#
# Copyright (C) 2013 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 generic
_supported_os Linux
echo
echo "=== Testing version downgrade with zero expansion ==="
echo
IMGOPTS="compat=1.1,lazy_refcounts=on" _make_test_img 64M
$QEMU_IO -c "write -z 0 128k" "$TEST_IMG" | _filter_qemu_io
./qcow2.py "$TEST_IMG" dump-header
$QEMU_IMG amend -o "compat=0.10" "$TEST_IMG"
./qcow2.py "$TEST_IMG" dump-header
$QEMU_IO -c "read -P 0 0 128k" "$TEST_IMG" | _filter_qemu_io
_check_test_img
echo
echo "=== Testing dirty version downgrade ==="
echo
IMGOPTS="compat=1.1,lazy_refcounts=on" _make_test_img 64M
$QEMU_IO -c "write -P 0x2a 0 128k" -c flush -c abort "$TEST_IMG" | _filter_qemu_io
./qcow2.py "$TEST_IMG" dump-header
$QEMU_IMG amend -o "compat=0.10" "$TEST_IMG"
./qcow2.py "$TEST_IMG" dump-header
$QEMU_IO -c "read -P 0x2a 0 128k" "$TEST_IMG" | _filter_qemu_io
_check_test_img
echo
echo "=== Testing version downgrade with unknown compat/autoclear flags ==="
echo
IMGOPTS="compat=1.1" _make_test_img 64M
./qcow2.py "$TEST_IMG" set-feature-bit compatible 42
./qcow2.py "$TEST_IMG" set-feature-bit autoclear 42
./qcow2.py "$TEST_IMG" dump-header
$QEMU_IMG amend -o "compat=0.10" "$TEST_IMG"
./qcow2.py "$TEST_IMG" dump-header
_check_test_img
echo
echo "=== Testing version upgrade and resize ==="
echo
IMGOPTS="compat=0.10" _make_test_img 64M
$QEMU_IO -c "write -P 0x2a 42M 64k" "$TEST_IMG" | _filter_qemu_io
./qcow2.py "$TEST_IMG" dump-header
$QEMU_IMG amend -o "compat=1.1,lazy_refcounts=on,size=128M" "$TEST_IMG"
./qcow2.py "$TEST_IMG" dump-header
$QEMU_IO -c "read -P 0x2a 42M 64k" "$TEST_IMG" | _filter_qemu_io
_check_test_img
echo
echo "=== Testing dirty lazy_refcounts=off ==="
echo
IMGOPTS="compat=1.1,lazy_refcounts=on" _make_test_img 64M
$QEMU_IO -c "write -P 0x2a 0 128k" -c flush -c abort "$TEST_IMG" | _filter_qemu_io
./qcow2.py "$TEST_IMG" dump-header
$QEMU_IMG amend -o "lazy_refcounts=off" "$TEST_IMG"
./qcow2.py "$TEST_IMG" dump-header
$QEMU_IO -c "read -P 0x2a 0 128k" "$TEST_IMG" | _filter_qemu_io
_check_test_img
echo
echo "=== Testing backing file ==="
echo
IMGOPTS="compat=1.1" _make_test_img 64M
IMGOPTS="compat=1.1" TEST_IMG="$TEST_IMG.base" _make_test_img 64M
$QEMU_IO -c "write -P 0x2a 0 128k" "$TEST_IMG.base" | _filter_qemu_io
$QEMU_IO -c "read -P 0 0 128k" "$TEST_IMG" | _filter_qemu_io
$QEMU_IMG amend -o "backing_file=$TEST_IMG.base,backing_fmt=qcow2" "$TEST_IMG"
$QEMU_IO -c "read -P 0x2a 0 128k" "$TEST_IMG" | _filter_qemu_io
_check_test_img
echo
echo "=== Testing invalid configurations ==="
echo
IMGOPTS="compat=0.10" _make_test_img 64M
$QEMU_IMG amend -o "lazy_refcounts=on" "$TEST_IMG"
$QEMU_IMG amend -o "compat=1.1" "$TEST_IMG" # actually valid
$QEMU_IMG amend -o "compat=0.10,lazy_refcounts=on" "$TEST_IMG"
$QEMU_IMG amend -o "compat=0.42" "$TEST_IMG"
$QEMU_IMG amend -o "foo=bar" "$TEST_IMG"
$QEMU_IMG amend -o "cluster_size=1k" "$TEST_IMG"
$QEMU_IMG amend -o "encryption=on" "$TEST_IMG"
$QEMU_IMG amend -o "preallocation=on" "$TEST_IMG"
echo
echo "=== Testing correct handling of unset value ==="
echo
IMGOPTS="compat=1.1,cluster_size=1k" _make_test_img 64M
echo "Should work:"
$QEMU_IMG amend -o "lazy_refcounts=on" "$TEST_IMG"
echo "Should not work:" # Just to know which of these tests actually fails
$QEMU_IMG amend -o "cluster_size=64k" "$TEST_IMG"
echo
echo "=== Testing zero expansion on inactive clusters ==="
echo
IMGOPTS="compat=1.1" _make_test_img 64M
$QEMU_IO -c "write -z 0 128k" "$TEST_IMG" | _filter_qemu_io
$QEMU_IMG snapshot -c foo "$TEST_IMG"
$QEMU_IO -c "write -P 0x2a 0 128k" "$TEST_IMG" | _filter_qemu_io
$QEMU_IMG amend -o "compat=0.10" "$TEST_IMG"
_check_test_img
$QEMU_IO -c "read -P 0x2a 0 128k" "$TEST_IMG" | _filter_qemu_io
$QEMU_IMG snapshot -a foo "$TEST_IMG"
_check_test_img
$QEMU_IO -c "read -P 0 0 128k" "$TEST_IMG" | _filter_qemu_io
echo
echo "=== Testing zero expansion on shared L2 table ==="
echo
IMGOPTS="compat=1.1" _make_test_img 64M
$QEMU_IO -c "write -z 0 128k" "$TEST_IMG" | _filter_qemu_io
$QEMU_IMG snapshot -c foo "$TEST_IMG"
$QEMU_IMG amend -o "compat=0.10" "$TEST_IMG"
_check_test_img
$QEMU_IO -c "read -P 0 0 128k" "$TEST_IMG" | _filter_qemu_io
$QEMU_IMG snapshot -a foo "$TEST_IMG"
_check_test_img
$QEMU_IO -c "read -P 0 0 128k" "$TEST_IMG" | _filter_qemu_io
echo
echo "=== Testing zero expansion on backed image ==="
echo
IMGOPTS="compat=1.1" TEST_IMG="$TEST_IMG.base" _make_test_img 64M
$QEMU_IO -c "write -P 0x2a 0 128k" "$TEST_IMG.base" | _filter_qemu_io
IMGOPTS="compat=1.1,backing_file=$TEST_IMG.base" _make_test_img 64M
$QEMU_IO -c "read -P 0x2a 0 128k" -c "write -z 0 64k" "$TEST_IMG" | _filter_qemu_io
$QEMU_IMG amend -o "compat=0.10" "$TEST_IMG"
_check_test_img
$QEMU_IO -c "read -P 0 0 64k" -c "read -P 0x2a 64k 64k" "$TEST_IMG" | _filter_qemu_io
echo
echo "=== Testing zero expansion on backed inactive clusters ==="
echo
IMGOPTS="compat=1.1" TEST_IMG="$TEST_IMG.base" _make_test_img 64M
$QEMU_IO -c "write -P 0x2a 0 128k" "$TEST_IMG.base" | _filter_qemu_io
IMGOPTS="compat=1.1,backing_file=$TEST_IMG.base" _make_test_img 64M
$QEMU_IO -c "write -z 0 64k" "$TEST_IMG" | _filter_qemu_io
$QEMU_IMG snapshot -c foo "$TEST_IMG"
$QEMU_IO -c "write -P 0x42 0 128k" "$TEST_IMG" | _filter_qemu_io
$QEMU_IMG amend -o "compat=0.10" "$TEST_IMG"
_check_test_img
$QEMU_IO -c "read -P 0x42 0 128k" "$TEST_IMG" | _filter_qemu_io
$QEMU_IMG snapshot -a foo "$TEST_IMG"
_check_test_img
$QEMU_IO -c "read -P 0 0 64k" -c "read -P 0x2a 64k 64k" "$TEST_IMG" | _filter_qemu_io
echo
echo "=== Testing zero expansion on backed image with shared L2 table ==="
echo
IMGOPTS="compat=1.1" TEST_IMG="$TEST_IMG.base" _make_test_img 64M
$QEMU_IO -c "write -P 0x2a 0 128k" "$TEST_IMG.base" | _filter_qemu_io
IMGOPTS="compat=1.1,backing_file=$TEST_IMG.base" _make_test_img 64M
$QEMU_IO -c "write -z 0 128k" "$TEST_IMG" | _filter_qemu_io
$QEMU_IMG snapshot -c foo "$TEST_IMG"
$QEMU_IMG amend -o "compat=0.10" "$TEST_IMG"
_check_test_img
$QEMU_IO -c "read -P 0 0 128k" "$TEST_IMG" | _filter_qemu_io
$QEMU_IMG snapshot -a foo "$TEST_IMG"
_check_test_img
$QEMU_IO -c "read -P 0 0 128k" "$TEST_IMG" | _filter_qemu_io
# success, all done
echo "*** done"
rm -f $seq.full
status=0

376
tests/qemu-iotests/061.out Normal file
View file

@ -0,0 +1,376 @@
QA output created by 061
=== Testing version downgrade with zero expansion ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
wrote 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
magic 0x514649fb
version 3
backing_file_offset 0x0
backing_file_size 0x0
cluster_bits 16
size 67108864
crypt_method 0
l1_size 1
l1_table_offset 0x30000
refcount_table_offset 0x10000
refcount_table_clusters 1
nb_snapshots 0
snapshot_offset 0x0
incompatible_features 0x0
compatible_features 0x1
autoclear_features 0x0
refcount_order 4
header_length 104
magic 0x514649fb
version 2
backing_file_offset 0x0
backing_file_size 0x0
cluster_bits 16
size 67108864
crypt_method 0
l1_size 1
l1_table_offset 0x30000
refcount_table_offset 0x10000
refcount_table_clusters 1
nb_snapshots 0
snapshot_offset 0x0
incompatible_features 0x0
compatible_features 0x0
autoclear_features 0x0
refcount_order 4
header_length 72
Header extension:
magic 0x6803f857
length 144
data <binary>
read 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
No errors were found on the image.
=== Testing dirty version downgrade ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
wrote 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
magic 0x514649fb
version 3
backing_file_offset 0x0
backing_file_size 0x0
cluster_bits 16
size 67108864
crypt_method 0
l1_size 1
l1_table_offset 0x30000
refcount_table_offset 0x10000
refcount_table_clusters 1
nb_snapshots 0
snapshot_offset 0x0
incompatible_features 0x1
compatible_features 0x1
autoclear_features 0x0
refcount_order 4
header_length 104
Repairing cluster 5 refcount=0 reference=1
Repairing cluster 6 refcount=0 reference=1
magic 0x514649fb
version 2
backing_file_offset 0x0
backing_file_size 0x0
cluster_bits 16
size 67108864
crypt_method 0
l1_size 1
l1_table_offset 0x30000
refcount_table_offset 0x10000
refcount_table_clusters 1
nb_snapshots 0
snapshot_offset 0x0
incompatible_features 0x0
compatible_features 0x0
autoclear_features 0x0
refcount_order 4
header_length 72
Header extension:
magic 0x6803f857
length 144
data <binary>
read 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
No errors were found on the image.
=== Testing version downgrade with unknown compat/autoclear flags ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
magic 0x514649fb
version 3
backing_file_offset 0x0
backing_file_size 0x0
cluster_bits 16
size 67108864
crypt_method 0
l1_size 1
l1_table_offset 0x30000
refcount_table_offset 0x10000
refcount_table_clusters 1
nb_snapshots 0
snapshot_offset 0x0
incompatible_features 0x0
compatible_features 0x40000000000
autoclear_features 0x40000000000
refcount_order 4
header_length 104
magic 0x514649fb
version 2
backing_file_offset 0x0
backing_file_size 0x0
cluster_bits 16
size 67108864
crypt_method 0
l1_size 1
l1_table_offset 0x30000
refcount_table_offset 0x10000
refcount_table_clusters 1
nb_snapshots 0
snapshot_offset 0x0
incompatible_features 0x0
compatible_features 0x0
autoclear_features 0x0
refcount_order 4
header_length 72
Header extension:
magic 0x6803f857
length 144
data <binary>
No errors were found on the image.
=== Testing version upgrade and resize ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
wrote 65536/65536 bytes at offset 44040192
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
magic 0x514649fb
version 2
backing_file_offset 0x0
backing_file_size 0x0
cluster_bits 16
size 67108864
crypt_method 0
l1_size 1
l1_table_offset 0x30000
refcount_table_offset 0x10000
refcount_table_clusters 1
nb_snapshots 0
snapshot_offset 0x0
incompatible_features 0x0
compatible_features 0x0
autoclear_features 0x0
refcount_order 4
header_length 72
magic 0x514649fb
version 3
backing_file_offset 0x0
backing_file_size 0x0
cluster_bits 16
size 134217728
crypt_method 0
l1_size 1
l1_table_offset 0x30000
refcount_table_offset 0x10000
refcount_table_clusters 1
nb_snapshots 0
snapshot_offset 0x0
incompatible_features 0x0
compatible_features 0x1
autoclear_features 0x0
refcount_order 4
header_length 104
Header extension:
magic 0x6803f857
length 144
data <binary>
read 65536/65536 bytes at offset 44040192
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
No errors were found on the image.
=== Testing dirty lazy_refcounts=off ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
wrote 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
magic 0x514649fb
version 3
backing_file_offset 0x0
backing_file_size 0x0
cluster_bits 16
size 67108864
crypt_method 0
l1_size 1
l1_table_offset 0x30000
refcount_table_offset 0x10000
refcount_table_clusters 1
nb_snapshots 0
snapshot_offset 0x0
incompatible_features 0x1
compatible_features 0x1
autoclear_features 0x0
refcount_order 4
header_length 104
Repairing cluster 5 refcount=0 reference=1
Repairing cluster 6 refcount=0 reference=1
magic 0x514649fb
version 3
backing_file_offset 0x0
backing_file_size 0x0
cluster_bits 16
size 67108864
crypt_method 0
l1_size 1
l1_table_offset 0x30000
refcount_table_offset 0x10000
refcount_table_clusters 1
nb_snapshots 0
snapshot_offset 0x0
incompatible_features 0x0
compatible_features 0x0
autoclear_features 0x0
refcount_order 4
header_length 104
Header extension:
magic 0x6803f857
length 144
data <binary>
read 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
No errors were found on the image.
=== Testing backing file ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
wrote 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
No errors were found on the image.
=== Testing invalid configurations ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
Lazy refcounts only supported with compatibility level 1.1 and above (use compat=1.1 or greater)
qemu-img: Error while amending options: Invalid argument
Lazy refcounts only supported with compatibility level 1.1 and above (use compat=1.1 or greater)
qemu-img: Error while amending options: Invalid argument
Unknown compatibility level 0.42.
qemu-img: Error while amending options: Invalid argument
Unknown option 'foo'
qemu-img: Invalid options for file format 'qcow2'
Changing the cluster size is not supported.
qemu-img: Error while amending options: Operation not supported
Changing the encryption flag is not supported.
qemu-img: Error while amending options: Operation not supported
Cannot change preallocation mode.
qemu-img: Error while amending options: Operation not supported
=== Testing correct handling of unset value ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
Should work:
Should not work:
Changing the cluster size is not supported.
qemu-img: Error while amending options: Operation not supported
=== Testing zero expansion on inactive clusters ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
wrote 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
No errors were found on the image.
read 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
No errors were found on the image.
read 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
=== Testing zero expansion on shared L2 table ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
wrote 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
No errors were found on the image.
read 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
No errors were found on the image.
read 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
=== Testing zero expansion on backed image ===
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
wrote 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base'
read 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset 0
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
No errors were found on the image.
read 65536/65536 bytes at offset 0
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 65536/65536 bytes at offset 65536
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
=== Testing zero expansion on backed inactive clusters ===
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
wrote 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base'
wrote 65536/65536 bytes at offset 0
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
No errors were found on the image.
read 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
No errors were found on the image.
read 65536/65536 bytes at offset 0
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 65536/65536 bytes at offset 65536
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
=== Testing zero expansion on backed image with shared L2 table ===
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
wrote 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file='TEST_DIR/t.IMGFMT.base'
wrote 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
No errors were found on the image.
read 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
No errors were found on the image.
read 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
*** done

View file

@ -164,6 +164,7 @@ QEMU_IO -- $QEMU_IO
IMGFMT -- $FULL_IMGFMT_DETAILS IMGFMT -- $FULL_IMGFMT_DETAILS
IMGPROTO -- $FULL_IMGPROTO_DETAILS IMGPROTO -- $FULL_IMGPROTO_DETAILS
PLATFORM -- $FULL_HOST_DETAILS PLATFORM -- $FULL_HOST_DETAILS
SOCKET_SCM_HELPER -- $SOCKET_SCM_HELPER
EOF EOF
#MKFS_OPTIONS -- $FULL_MKFS_OPTIONS #MKFS_OPTIONS -- $FULL_MKFS_OPTIONS

View file

@ -123,7 +123,7 @@ _make_test_img()
fi fi
# XXX(hch): have global image options? # XXX(hch): have global image options?
$QEMU_IMG create -f $IMGFMT $extra_img_options $img_name $image_size | \ $QEMU_IMG create -f $IMGFMT $extra_img_options $img_name $image_size 2>&1 | \
sed -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \ sed -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \
-e "s#$TEST_DIR#TEST_DIR#g" \ -e "s#$TEST_DIR#TEST_DIR#g" \
-e "s#$IMGFMT#IMGFMT#g" \ -e "s#$IMGFMT#IMGFMT#g" \

View file

@ -63,7 +63,9 @@
054 rw auto 054 rw auto
055 rw auto 055 rw auto
056 rw auto backing 056 rw auto backing
057 rw auto
059 rw auto 059 rw auto
060 rw auto 060 rw auto
061 rw auto
062 rw auto 062 rw auto
063 rw auto 063 rw auto

View file

@ -38,6 +38,8 @@ imgfmt = os.environ.get('IMGFMT', 'raw')
imgproto = os.environ.get('IMGPROTO', 'file') imgproto = os.environ.get('IMGPROTO', 'file')
test_dir = os.environ.get('TEST_DIR', '/var/tmp') test_dir = os.environ.get('TEST_DIR', '/var/tmp')
socket_scm_helper = os.environ.get('SOCKET_SCM_HELPER', 'socket_scm_helper')
def qemu_img(*args): def qemu_img(*args):
'''Run qemu-img and return the exit code''' '''Run qemu-img and return the exit code'''
devnull = open('/dev/null', 'r+') devnull = open('/dev/null', 'r+')
@ -80,6 +82,12 @@ class VM(object):
'-display', 'none', '-vga', 'none'] '-display', 'none', '-vga', 'none']
self._num_drives = 0 self._num_drives = 0
# This can be used to add an unused monitor instance.
def add_monitor_telnet(self, ip, port):
args = 'tcp:%s:%d,server,nowait,telnet' % (ip, port)
self._args.append('-monitor')
self._args.append(args)
def add_drive(self, path, opts=''): def add_drive(self, path, opts=''):
'''Add a virtio-blk drive to the VM''' '''Add a virtio-blk drive to the VM'''
options = ['if=virtio', options = ['if=virtio',
@ -112,6 +120,21 @@ class VM(object):
self._args.append(','.join(options)) self._args.append(','.join(options))
return self return self
def send_fd_scm(self, fd_file_path):
# In iotest.py, the qmp should always use unix socket.
assert self._qmp.is_scm_available()
bin = socket_scm_helper
if os.path.exists(bin) == False:
print "Scm help program does not present, path '%s'." % bin
return -1
fd_param = ["%s" % bin,
"%d" % self._qmp.get_sock_fd(),
"%s" % fd_file_path]
devnull = open('/dev/null', 'rb')
p = subprocess.Popen(fd_param, stdin=devnull, stdout=sys.stdout,
stderr=sys.stderr)
return p.wait()
def launch(self): def launch(self):
'''Launch the VM and establish a QMP connection''' '''Launch the VM and establish a QMP connection'''
devnull = open('/dev/null', 'rb') devnull = open('/dev/null', 'rb')

View file

@ -0,0 +1,135 @@
/*
* SCM_RIGHTS with unix socket help program for test
*
* Copyright IBM, Inc. 2013
*
* Authors:
* Wenchao Xia <xiawenc@linux.vnet.ibm.com>
*
* This work is licensed under the terms of the GNU LGPL, version 2 or later.
* See the COPYING.LIB file in the top-level directory.
*/
#include <stdio.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
/* #define SOCKET_SCM_DEBUG */
/*
* @fd and @fd_to_send will not be checked for validation in this function,
* a blank will be sent as iov data to notify qemu.
*/
static int send_fd(int fd, int fd_to_send)
{
struct msghdr msg;
struct iovec iov[1];
int ret;
char control[CMSG_SPACE(sizeof(int))];
struct cmsghdr *cmsg;
memset(&msg, 0, sizeof(msg));
memset(control, 0, sizeof(control));
/* Send a blank to notify qemu */
iov[0].iov_base = (void *)" ";
iov[0].iov_len = 1;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_control = control;
msg.msg_controllen = sizeof(control);
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
do {
ret = sendmsg(fd, &msg, 0);
} while (ret < 0 && errno == EINTR);
if (ret < 0) {
fprintf(stderr, "Failed to send msg, reason: %s\n", strerror(errno));
}
return ret;
}
/* Convert string to fd number. */
static int get_fd_num(const char *fd_str)
{
int sock;
char *err;
errno = 0;
sock = strtol(fd_str, &err, 10);
if (errno) {
fprintf(stderr, "Failed in strtol for socket fd, reason: %s\n",
strerror(errno));
return -1;
}
if (!*fd_str || *err || sock < 0) {
fprintf(stderr, "bad numerical value for socket fd '%s'\n", fd_str);
return -1;
}
return sock;
}
/*
* To make things simple, the caller needs to specify:
* 1. socket fd.
* 2. path of the file to be sent.
*/
int main(int argc, char **argv, char **envp)
{
int sock, fd, ret;
#ifdef SOCKET_SCM_DEBUG
int i;
for (i = 0; i < argc; i++) {
fprintf(stderr, "Parameter %d: %s\n", i, argv[i]);
}
#endif
if (argc != 3) {
fprintf(stderr,
"Usage: %s < socket-fd > < file-path >\n",
argv[0]);
return EXIT_FAILURE;
}
sock = get_fd_num(argv[1]);
if (sock < 0) {
return EXIT_FAILURE;
}
/* Now only open a file in readonly mode for test purpose. If more precise
control is needed, use python script in file operation, which is
supposed to fork and exec this program. */
fd = open(argv[2], O_RDONLY);
if (fd < 0) {
fprintf(stderr, "Failed to open file '%s'\n", argv[2]);
return EXIT_FAILURE;
}
ret = send_fd(sock, fd);
if (ret < 0) {
close(fd);
return EXIT_FAILURE;
}
close(fd);
return EXIT_SUCCESS;
}